RAG 系统最终检索的不是用户上传的原始文件,而是经过解析、清洗、分块和索引后得到的 Chunk。任何一个环节出错,都会让后续模型建立在错误知识上。
Dify 的 RAG Pipeline 把文档入库过程从固定代码变成可编排工作流。它不仅让用户调整处理步骤,也要求系统解决异步任务、索引状态、配置发布和数据一致性问题。
本文只关注 Dify 的工程实现思路,分块、Embedding 和 Rerank 的基础原理可参考本站其他专题文章。
一、为什么入库需要 Pipeline
1.1 固定流程的局限
最简单的知识库流程是:
1 | 上传文件 → 提取文本 → 固定长度分块 → Embedding → 写入向量库 |
真实数据源却差异很大:
- PDF 需要版面解析和 OCR;
- Markdown 需要保留标题层级;
- 表格不能按普通段落切分;
- 网页需要去除导航、广告和脚注;
- 在线文档需要 OAuth 和增量同步;
- 不同业务还要脱敏、分类或提取元数据。
如果所有逻辑都塞进一个 index_document(),新增规则会不断增加条件分支,也很难观察究竟在哪一步失败。
1.2 Pipeline 的核心价值
Pipeline 将每一步变成有输入、输出和状态的节点:
flowchart LR
Source["数据源"] --> Extract["解析 / OCR"]
Extract --> Clean["清洗与标准化"]
Clean --> Split["结构化分块"]
Split --> Metadata["元数据增强"]
Metadata --> Embed["Embedding"]
Embed --> Index["写入索引"]
这样可以:
- 为不同数据源复用或替换节点;
- 单独预览某一步输出;
- 记录阶段状态和耗时;
- 失败后从明确阶段重试;
- 将处理配置作为 DSL 导入导出;
- 发布新版本时保留旧配置。
二、Dify RAG Pipeline 的组成
2.1 Pipeline、Workflow 与 Dataset
三个概念分别承担不同职责:
| 对象 | 职责 |
|---|---|
| Pipeline | 知识处理应用的身份和发布入口 |
| Workflow | 节点图、变量和具体处理配置 |
| Dataset | 文档、Chunk、检索配置和索引绑定 |
Pipeline 负责“怎么处理”,Dataset 负责“处理后的知识存在哪里并如何检索”。二者关联,但不应合成同一个对象。
2.2 草稿与发布版本
用户在界面编辑的是草稿工作流。发布时生成不可变版本,并把知识索引节点中的关键配置同步到 Dataset:
- 索引方式;
- Embedding Provider 和模型;
- Chunk 结构;
- 检索方式;
- Rerank 与分数阈值;
- 摘要索引配置。
运行任务应绑定一个明确版本,不能在执行中读取不断变化的草稿。否则同一批文档可能被不同配置处理。
2.3 数据源插件
Datasource 插件是 Pipeline 的起点,可表示:
- 本地上传文件;
- 在线网盘;
- 企业文档系统;
- 网站抓取;
- 其他外部内容源。
数据源节点不应该直接生成向量,它只负责可靠地产生统一文档对象和来源元数据。认证、分页、增量游标和删除同步属于数据源职责。
三、从文档到索引
3.1 Extract、Transform、Load
Dify 的 BaseIndexProcessor 抽象了几个关键阶段:
1 | class BaseIndexProcessor: |
可以把它理解为面向 RAG 的 ETL:
extract:从文件、网页或存储中得到标准文档;transform:清洗、切分并生成 Chunk;load/index:计算关键词或向量并写入检索存储;clean:文档更新或删除时移除旧索引。
3.2 标准文档模型
解析器输出应包含:
1 | 正文 + 来源 + 页码/标题层级 + 文件信息 + 自定义元数据 |
后续节点只依赖统一模型,而不关心原始文件是 PDF 还是在线文档。保留来源定位非常重要,否则检索结果无法引用原文。
3.3 分块结构
Dify 支持普通段落和父子分块等结构:
| 结构 | 索引与召回方式 | 适用场景 |
|---|---|---|
| 普通段落 | 每个 Chunk 独立索引与返回 | 短文、FAQ、结构简单内容 |
| 父子分块 | 子块用于精确召回,父块提供完整上下文 | 长文、章节和复杂说明书 |
分块结构一旦投入使用,不应随意原地修改。因为数据库中的 Segment、向量库记录和引用关系都依赖该结构。重大变化通常需要重建索引。
3.4 High Quality 与 Economy
可以简化理解为:
- High Quality 使用 Embedding,支持语义、全文、混合检索和 Rerank;
- Economy 主要使用关键词索引,成本低但语义能力有限。
选择索引方式会影响存储结构和后续检索能力,不能只作为界面显示选项。
四、异步索引与状态机
4.1 为什么必须异步
解析大文件、调用 Embedding 和批量写向量库可能持续数分钟。如果在上传请求中同步完成:
- HTTP 请求容易超时;
- 一个文件会长期占用 Web Worker;
- 无法显示细粒度进度;
- 批量上传难以限流;
- 失败重试会重复整个请求。
因此 API 只创建 Document 和任务,Celery Worker 在后台执行索引。
4.2 文档状态
Dify 会记录多个阶段时间:
1 | WAITING |
还需要 PAUSED、ERROR、STOPPED 等分支,以及:
processing_started_at;parsing_completed_at;cleaning_completed_at;splitting_completed_at;completed_at;error;- 已完成与总 Segment 数。
stateDiagram-v2
[*] --> Waiting
Waiting --> Parsing
Parsing --> Cleaning
Cleaning --> Splitting
Splitting --> Indexing
Indexing --> Completed
Parsing --> Error
Cleaning --> Error
Splitting --> Error
Indexing --> Error
Error --> Parsing: Retry
Waiting --> Stopped: Cancel
Parsing --> Paused: Pause
Paused --> Parsing: Resume
状态不仅用于界面进度,也是重试和清理旧数据的依据。
4.3 数据库事务边界
长时间调用解析器、模型和向量库时,不应一直占用数据库事务:
- 短事务读取任务与配置;
- 写入当前阶段并提交;
- 释放数据库连接;
- 执行外部耗时操作;
- 新事务保存结果和下一状态。
否则 Worker 并发增加后,连接池会先于 CPU 和模型配额耗尽。
4.4 幂等与重试
任务重试不能简单地再次插入所有 Chunk。推荐做法:
- 为一次索引生成批次 ID;
- Segment 使用稳定业务标识;
- 写索引使用 upsert;
- 重建前明确清理旧向量节点;
- 状态转换使用条件更新,防止两个 Worker 同时处理;
- 失败只重试可恢复阶段;
- 完成后再切换可见版本。
五、配置变更与索引一致性
5.1 哪些变更需要重建
| 配置变更 | 是否通常需要重建 |
|---|---|
| 文档显示名称 | 否 |
| 检索 Top K、分数阈值 | 否 |
| Rerank 模型 | 通常否 |
| Embedding 模型 | 是 |
| 分块大小、重叠和分隔符 | 是 |
| 普通分块切换父子分块 | 是 |
| 清洗规则 | 是 |
| 摘要索引策略 | 取决于是否影响已有摘要向量 |
Embedding 模型变化后,新旧向量不能直接混用。即使维度相同,向量空间也不一定兼容。
5.2 安全发布
生产级重建可以采用双版本:
1 | ACTIVE 索引继续服务 |
这比边删除边重建更可靠,避免长时间检索不到知识。
5.3 删除与更新
删除文档需要同时处理:
- Document 记录;
- Segment 与子 Chunk;
- 向量索引节点;
- 关键词倒排数据;
- 关联文件和附件;
- 缓存与检索引用。
应先标记不可检索,再异步清理物理数据。这样即使向量库暂时故障,也不会继续返回已删除内容。
六、从索引到检索闭环
入库配置最终要服务检索:
1 | Query → 路由知识库 → 召回 → 元数据过滤 → Rerank → 阈值过滤 → 上下文组装 |
Dify 根据 Dataset 的索引方式选择关键词、语义、全文或混合检索,再应用 Top K、Score Threshold 和 Rerank。
检索质量问题不能只在查询侧修补:
- 召回不到,可能是解析或分块丢失内容;
- 召回太散,可能是 Chunk 太小或缺少标题;
- 相似度异常,可能是查询和文档使用了不同 Embedding;
- 引用错误,可能是入库时没有保留来源定位。
因此需要把“入库版本—索引版本—检索日志—最终回答”串联起来,才能形成可评测闭环。
七、排障与工程清单
文档长期停留在 Queuing 时,按顺序检查:
- API 是否成功投递 Celery 任务;
- Worker 是否监听对应 Dataset/Pipeline 队列;
- Redis 是否存在积压;
- Document 状态和错误字段是否更新;
- 解析器、对象存储和远程文件是否可访问;
- Embedding 凭证、限流和超时是否正常;
- 向量库是否可写,集合维度是否匹配;
- 是否有重复任务锁或陈旧缓存;
- 重试前是否正确清理半成品索引。
生产环境还应监控:每阶段耗时、队列等待时间、Chunk 数、Embedding Token、失败率和索引写入延迟。
八、总结
Dify RAG Pipeline 的工程价值可以概括为:
- 用 Workflow 描述可变的知识处理步骤;
- 用 Dataset 承载文档、索引和检索配置;
- 数据源插件只负责可靠地产生统一文档;
- IndexProcessor 抽象 Extract、Transform、Load 和 Clean;
- Celery 将长时间索引移出请求线程;
- 细粒度状态支持进度、重试和排障;
- Embedding、分块等重大变更触发索引重建;
- 发布版本与索引版本关联,避免草稿污染生产;
- 入库指标和检索效果共同构成质量闭环。
RAG Pipeline 不是把几个处理函数画成流程图,而是让数据加工变得可版本化、可恢复、可观测和可评测。