下面按「从报错到跑通」的时间线,把这次 Unstructured 插件接入的配置过程梳理成一份可复用的排查流程,后来的同学可以照着一步步对。
一、问题起点:LLM 说「没收到文件」
- 场景:工作流里用户上传了扫描版 PDF + 问题,前面用了内置「文档提取器(Doc Extractor)」。
- 现象:LLM 在思考时提示「未收到任何上传的文档」。
- 根因:扫描 PDF 只有图片,没有文本层;内置 Doc Extractor 不做 OCR,输出
text为空;Prompt 引用的是空文本,LLM 当然说「没看到文件」。
结论:要解决扫描 PDF 场景,必须换成支持 OCR 的工具节点(如 Unstructured 插件、其他 OCR 插件),而不是只指望内置文档提取器。
二、第一阶段:部署 Unstructured 服务 & 插件接入
1. 本地启动 Unstructured 服务
- 用户在本地通过 Docker 起了一个
unstructured容器(端口 8000)。 - 这个服务和 Dify 其它容器在同一个 Docker 网络里。
这里的关键点:
后面在插件里配置的“服务地址”,要能在容器网络内部被访问,而不是只在宿主机访问。
2. 在 Dify 中安装 Unstructured 插件
- 在「插件 / Marketplace」里安装官方的 Unstructured 插件。
- 在插件的 API 配置弹窗中,需要填:
- 一个「Unstructured 服务 API URL」;
- 选择「服务类型」(本地部署);
- 如果服务需要额外认证,再填 Token(本例中用的是本地开放服务,可为空)。
三、第二阶段:URL / FILES_URL 配置相关的坑
1. API URL 拼错导致 404
一开始的配置类似:
Unstructured 服务 API URL: http://unstructured:8000/general/v0/general
问题在于:
插件内部会自己再拼接路径,比如再加上 /general/v0/general,于是最终请求 URL 变成了:
http://unstructured:8000/general/v0/general/general/v0/general
Unstructured 容器日志里能看到大量 404,说明路径被重复拼接。
修正方式:
只保留服务根地址:
http://unstructured:8000
后续 path 由插件自己处理。修正之后,404 就消失了,开始进入参数校验阶段。
提示点:以后如果看到类似
/xxx/xxx/xxx/xxx/xxx被重复拼接的路径、且返回 404,可以第一时间检查是不是在插件里把完整 path 写死了。
2. FILES_URL / 文件访问的思路
虽然这次主要问题集中在 API URL,但从你的反馈可以看出另一个常见坑:在 .env 里给 FILES_URL 写 localhost。
在多容器部署里:
localhost对每个容器来说都是“自己”,指的不是宿主机,也不是 Dify Web 容器;- 结果是:插件所在的容器去访问
http://localhost:xxx/...时,根本访问不到 Dify 暴露的文件下载地址。
比较稳妥的做法是:
- 在
.env里把 FILES_URL 设置为容器网络中可解析的服务名,例如(视你的 compose 而定):
FILES_URL=http://web:3000
# 或 http://nginx:80 等,看你实际暴露的是哪个服务
只要在 Unstructured 容器里能 curl 通这个地址并拿到上传文件,插件就能正常工作。
四、第三阶段:Unstructured 参数(chunking_strategy)错误
URL 修正后,下一个报错变成了参数校验:
An error occurred in ... Partition request failed.
msg:{
"detail":[
{
"type":"literal_error",
"loc":["body","chunking_strategy"],
"msg":"Input should be 'by_title'",
"input":"by_page",
"ctx":{"expected":"'by_title'"}
}
]
}
含义很直接:
- 你在插件节点里把 分块策略 填成了
by_page; - 当前使用的这个 Unstructured 接口只接受
'by_title'(或有限的一些值),于是返回 422 / 400。
修正方式:
- 把
chunking_strategy改成接口实际支持的值(例如by_title),或者干脆先留空使用默认值; - 先保证「不报错,能出结果」,再逐步调高级参数。
调试顺序建议:
- 先只填最少参数(OCR 策略、语言、文件输入),让节点能成功跑通;
- 再按官方文档一个个加上
chunking_strategy、chunk_size、overlap等; - 每加一项就 test run 一次,确保没有 4xx/5xx 错误。
五、第四阶段:输出结果 & 节点链路的最终形态
修正 URL + 参数后:
-
Unstructured partition 节点已经能成功解析扫描 PDF;
-
输出结构中,你看到的是一个 JSON 对象,大致包含:
text:一整段 / 多段拼接好的纯文本(OCR 后的内容);files:[](空列表);images: 可能为空或包含图片引用;elements:结构化元素列表;json:更原始的结构化结果。
1. 为什么 files 是空的?
- 这是为了兼容“压缩包、多文件、带附件文档”等更复杂场景的统一 schema 字段;
- 你现在传的是单个 PDF 且无嵌套附件,自然没有子文件可输出,所以是空列表;
- 跟 OCR 是否成功无关——真正有用的是
text/elements/json。
2. 是否还需要后面的「文档提取器」节点?
在你的这个场景下:
- Unstructured 已经完成了「文件 → OCR → 结构化元素 → 拼接文本」全过程;
- 再接一个内置「文档提取器」节点,只是对文本结果再做一次无意义处理,对扫描 PDF 没额外帮助。
推荐的最终工作流:
- 用户输入节点:上传扫描 PDF(
user_files)。 - Unstructured partition 节点:
- 输入文件:引用
{{ user_files[0].file }}(或你当前用的变量)。 - 配置 OCR + 分块策略(确保参数合法)。
- 输入文件:引用
- LLM 节点:
- 在系统 / 用户提示词里直接引用
{{ partition.text }}(用你节点的输出名)。
- 在系统 / 用户提示词里直接引用
- 若有进阶需求:
- 用
elements/json做更细粒度筛选(例如只取某几页、某些 element type)。
- 用
六、给后续同学的「快速自查 Checklist」
遇到类似「扫描 PDF + Unstructured 插件」问题时,可以按这个顺序排查:
-
服务连通性
- 本地 Unstructured 容器在 8000 端口正常运行。
- 在同一网络的其它容器中,
curl http://unstructured:8000能通。
-
插件 API URL
- 只填根地址:
http://unstructured:8000; - 不要手动拼
/general/v0/general这类 path,否则会被重复拼接导致 404。
- 只填根地址:
-
FILES_URL / 文件访问
-
.env里的 FILES_URL 不用localhost,而是像http://web:3000这样可以从别的容器访问到的服务名; - 在 Unstructured 容器里用该 URL 能下载到 Dify 上传的文件。
-
-
参数配置
- 初次测试时先不指定或少指定高级参数,确保接口不报 4xx;
- 给
chunking_strategy之类字段时,对照官方文档使用受支持的值(如by_title),遇到错误看返回的expected提示。
-
工作流使用方式
- 扫描 PDF 场景里,直接用 Unstructured 节点输出的
text到 LLM; - 内置文档提取器在这条链路中可以省略;
-
files为空不必纠结,重点看text是否有内容。
- 扫描 PDF 场景里,直接用 Unstructured 节点输出的
这样整体复盘下来,从「LLM 看不到文件」到「Unstructured OCR 跑通」的路径和中间坑点基本都覆盖到了,后面大家只要照着 URL→网络→参数→输出这几步顺序检查,基本都能比较快定位问题。