Ch33: Dify 源码走读——Workflow 与 RAG 引擎
Part 9: 四平台源码走读(Phase E) 前置:Ch16 Dify 精读、Ch32 大师通关 后续:Ch34 RAGFlow 源码
结论先行:Dify 的 RAG 实现在 api/core/rag/,业务编排落在 api/core/workflow/。大师读 Dify 应优先追踪 Workflow DAG 如何调用 KnowledgeRetrievalNode,而不是只读 Chat 封装。Dify 的护城河是「可视化编排 + Model Runtime」,RAG 是标准 BM25+向量双路——与 WeKnora 三路+RRF 互补,常见组合是 Dify 编排 + WeKnora 外部检索 HTTP 节点。
一、日常类比
Dify 像 宜家厨房样板间:预制柜体(节点)、说明书(Workflow DSL)、统一接口(Model Runtime)让你快速搭出「能用的」RAG 应用。WeKnora 像 专业后厨:刀工(解析)、三路备料(BM25/向量/图谱)、摆盘(Rerank)更细。大师要会选:展示 Demo 用宜家,竞赛改检索深度进后厨。
二、Commit / 版本 Pin 说明
| 项 | 建议 |
|---|---|
| 仓库 | langgenius/dify |
| Docker | langgenius/dify-api:0.15.x 或 clone 日 2026-06 对照 git log -1 --format=%ci |
| 原因 | api/core/workflow/nodes/ 与 core/rag/retrieval/ 在 0.14→0.15 间有目录微调 |
git clone https://github.com/langgenius/dify.git
cd dify && git checkout $(git tag -l '0.15.*' | tail -1) # 无 tag 则用 main + 记录 commit
cd docker && docker compose up -d
# 浏览器打开 :80,创建 Dataset + 最小 Workflow
走读前在笔记写:pin = tag/commit + 日期,PR 或实验结论必须可复现。
三、目录地图(RAG + Workflow)
| 路径 | 职责 |
|---|---|
api/controllers/console/ |
Console API 路由(Dataset、App、Workflow) |
api/services/dataset_service.py |
知识库 CRUD、文档索引任务派发 |
api/core/rag/retrieval/ |
VectorRetriever / KeywordRetriever / HybridRetriever |
api/core/rag/splitter/ |
Fixed / Recursive / Auto 分块 |
api/core/rag/extractor/ |
PDF / Word / Markdown 抽取 |
api/core/workflow/graph/graph.py |
DAG 拓扑与执行调度 |
api/core/workflow/nodes/knowledge_retrieval/ |
工作流内知识检索节点 |
api/core/model_runtime/ |
100+ Provider 统一 invoke() |
api/tasks/ |
Celery 异步摄入、Embedding |
分工记忆:services/ = 业务编排与权限;core/rag/ = 算法与管线;core/workflow/ = 节点运行时。
四、一条具体调用链(Workflow 带知识库)
以 Console 触发「知识检索 → LLM」Workflow 为例(文件路径以 0.15.x 为锚):
POST /console/api/.../workflows/run
→ api/controllers/console/app/workflow.py # 接 HTTP
→ api/services/workflow_service.py # 加载 DSL、鉴权
→ api/core/workflow/graph/graph.py # GraphEngine.run()
→ api/core/workflow/nodes/knowledge_retrieval/node.py
# 读 query 变量 → 调 RetrievalService
→ api/core/rag/retrieval/retrieval_service.py
→ api/core/rag/retrieval/hybrid.py # HybridRetriever.retrieve()
# KeywordRetriever + VectorRetriever → 权重融合
→ api/core/rag/datasource/vdb/* # Qdrant / Weaviate 等
→ (下一节点) api/core/workflow/nodes/llm/node.py
→ api/core/model_runtime/model_providers/*/llm.py # 生成答案
Chat 应用走 api/services/app_generate_service.py,最终仍复用 core/rag/retrieval/,但 变量分支与 if_else 只在 Workflow 路径完整暴露——故大师优先读 Workflow。
五、grep 起手式
cd dify/api
rg -n "class HybridRetriever" core/rag/retrieval/
rg -n "KnowledgeRetrieval" core/workflow/nodes/
rg -n "class GraphEngine" core/workflow/graph/
rg -l "RetrievalService" core/rag/ services/
rg "bm25_weight|vector_weight" core/rag/retrieval/ -n
对比 WeKnora 同题:Ch22 请求生命周期 — Handler→Service→HybridSearch;Dify 多一层 GraphEngine 节点调度。
六、与 WeKnora / Lab 对照
| Dify 能力 | WeKnora 可借鉴 | Lab 验证 |
|---|---|---|
| 可视化 DAG + if_else 低分分支 | 文档化推荐工作流,不必先做 UI | Ch37 C 组 |
| HTTP 节点调外部检索 | 官方 API 作 Dify 后端 | weknora-arch.md |
案例:case-1774 ↔ Dify controllers→services 分层。
七、动手练习(45–60 分钟)
| 时段 | 任务 |
|---|---|
| 0–15 min | Docker 起服务,UI 搭「知识检索 → LLM」Workflow |
| 15–30 min | DevTools 抓 run 请求,rg 定位 knowledge_retrieval/node.py |
| 30–45 min | 读 hybrid.py,记录 BM25/向量默认权重与是否支持 Rerank |
| 45–60 min | 上传 lab/corpus/weknora-arch.md,问 A1「错误码 E1042」类 query,写 5 行:Dify 混合检索 vs WeKnora RRF 差异 |
章末自检
- 说出
api/core/rag/与api/services/的分工边界 - 白板画出从
GraphEngine.run到HybridRetriever.retrieve的 6 步 - 找到
knowledge_retrieval/节点目录并说明其输入/输出变量名 - 解释为何大师读 Workflow 优先于只读 Chat 封装