1. 背景
上一份笔记解决的是"Dify 这个仓库整体由什么组成"。这一份笔记继续回答另一个更关键的问题:
一个请求从哪里发起,经过哪些层,最终落到哪些核心模块?
如果说宏观知识架构解决的是"地图问题",那么请求主链路解决的是"路径问题"。
阅读 Dify 时,真正高频需要掌握的,不是某个类的全部实现细节,而是以下主链路:
- 前端如何组装请求
- 请求如何分流到不同 API 前缀
- Flask 如何注册不同业务入口
- Controller 如何做鉴权、参数校验、上下文注入
- Service 如何承接业务
- 业务何时进入
workflow、model_runtime、rag - 何时落 DB、Redis、Celery、向量库
2. 请求主链路总览
先给出最重要的宏观结论:
2.1 Dify 后端不是单入口,而是多入口分流
后端通过蓝图把请求分成几类:
/console/api- 面向控制台
- 给已登录的工作台前端使用
- 典型场景:应用管理、模型配置、知识库管理、工作流编排、工作区设置
/api- 面向 Web App / 分享页 / 嵌入页
- 典型场景:聊天、会话列表、消息流式输出、站点访问
/v1- 面向服务 API / 外部程序集成
- 典型场景:开发者用 API Key 调用应用、数据集和工作流能力
/files- 文件相关能力
/mcp- MCP 相关能力
/triggers- Trigger / Webhook 能力
也就是说,Dify 的"请求主链路"第一步不是进某个 controller,而是先判断它属于哪条入口线。
2.2 前端同样是多前缀请求
web/config/index.ts 中定义了两个最关键的前端 API 前缀:
API_PREFIX- 默认指向
http://localhost:5001/console/api - 面向控制台请求
- 默认指向
PUBLIC_API_PREFIX- 默认指向
http://localhost:5001/api - 面向公开 Web App 请求
- 默认指向
因此,前端在发请求时,实际上已经在做第一次业务分流:
- 控制台请求走
console/api - Web App/分享页请求走
api
3. 前端请求是如何发起的
3.1 统一请求封装层
前端核心请求封装在:
web/service/fetch.tsweb/service/base.ts- 各领域 service 文件,如
web/service/apps.ts
其中 fetch.ts 负责的事情很关键:
- 选择请求基地址
- 注入 Cookie/CSRF
- 为公开 Web App 请求注入
X-App-Code - 为 Web App 注入 passport / access token
- 统一处理错误提示
- 统一处理 204、下载、JSON、流式响应等差异
这意味着:
页面组件通常不直接理解后端细节,而是通过 service 层进入统一请求总线。
3.2 控制台请求链路
以 web/service/apps.ts 为例:
fetchAppList()调get(url, { params })createApp()调post('apps', { body })fetchAppDetail()调get(\{url}/{id}`)`
这些调用默认不带 isPublicAPI,因此最终会走:
API_PREFIX- 也就是
/console/api
3.3 Web App 请求链路
fetch.ts 中如果 isPublicAPI = true,请求会走:
PUBLIC_API_PREFIX- 即
/api
同时还会做两件事:
- 注入
Authorization - 注入
X-App-Code与X-App-Passport
这说明公开 Web App 的访问不是普通匿名接口,而是带"应用身份 + 终端用户身份"的请求模型。
4. 后端应用初始化与总入口
4.1 Flask app 的创建
后端入口在:
api/app_factory.py
这里做了三件关键事情:
- 创建
DifyApp - 注册
before_request/after_request - 初始化一组 extensions
其中 before_request 会初始化日志上下文,after_request 会把 OpenTelemetry trace 信息写回响应头。
所以真正的主链路在后端不是"请求直接进 controller",而是:
WSGI/Flask -> app_factory -> before_request -> extensions -> blueprints -> controller
4.2 蓝图注册是后端分流核心
蓝图注册发生在:
api/extensions/ext_blueprints.py
这里把几条主入口注册进 Flask:
controllers.console->/console/apicontrollers.web->/apicontrollers.service_api->/v1controllers.filescontrollers.inner_apicontrollers.mcpcontrollers.trigger
同时,这里还顺手配置了不同入口的 CORS 策略。
所以,阅读 Dify 请求路径时,ext_blueprints.py 是后端的路由分发总图。
5. 三类主请求入口
5.1 Console API
定义位置:
api/controllers/console/__init__.py
关键特征:
- 蓝图前缀:
/console/api - 面向 Dify 控制台
- 聚合 app、datasets、workspace、auth、billing、explore 等大量子控制器
这一层的请求通常意味着:
- 需要控制台登录态
- 需要租户上下文
- 需要 workspace / account 权限校验
这是"管理面请求"的主入口。
5.2 Web API
定义位置:
api/controllers/web/__init__.py
关键特征:
- 蓝图前缀:
/api - 面向最终用户访问的 Web 应用
- 包含 conversation、message、workflow、login、passport、site 等能力
这一层的重点不是后台管理,而是:
- 终端用户和具体 App 的关系
- App Code / Passport 校验
- 会话、消息、站点访问
这是"运行面请求"的主入口。
5.3 Service API
定义位置:
api/controllers/service_api/__init__.py
关键特征:
- 蓝图前缀:
/v1 - 面向程序化调用
- 更接近"开放平台 API"
这一层更适合:
- 外部系统集成
- 使用 API Key 调用应用
- 将 Dify 当 BaaS 能力嵌入自有系统
6. Controller 层真正做什么
很多人读到 controller 时容易误判它只是"转发层",但在 Dify 里它承担了几项非常重要的责任。
6.1 参数校验
典型模式是:
- 用 Pydantic
BaseModel定义 Query/Payload - 在请求进入业务逻辑前做结构化校验
例如:
- 分页参数
- UUID 参数
- app mode 参数
- tracing 参数
这使得请求在非常靠前的位置就被约束住。
6.2 鉴权与上下文注入
例如:
api/controllers/console/wraps.pyapi/controllers/web/wraps.py
两类 wrap 的职责不同:
- Console wraps
- 校验 setup、登录态、租户、套餐、权限等
- Web wraps
- 解码 Web App passport
- 校验
X-App-Code - 定位
App、Site、EndUser - 校验企业版 webapp auth 访问规则
所以 Dify 的 controller 不是"薄 controller"到完全空壳的程度,它在入口层承担了很重的"请求合法化"职责。
6.3 调用 Service 层
当参数和上下文都就绪后,controller 才会进入 service。
这里形成 Dify 很典型的结构:
controller -> service -> core/domain -> repository/model/task
7. Service 层在主链路中的位置
api/services/ 是业务编排主区。它做的不是单纯 CRUD,而是把多个子系统拼起来。
例如一个 service 可能会同时协调:
- 数据库模型
- 模型运行时
- 工作流引擎
- RAG 检索
- Celery 异步任务
- 企业功能开关
- 插件或工具系统
这意味着:
- controller 负责"进门"
- service 负责"把事办成"
- core 负责"真正执行核心 AI 能力"
8. 核心能力层如何接入请求链路
8.1 工作流链路
当请求触发工作流应用、调试工作流、运行节点、查看执行状态时,通常会继续下钻到:
api/core/workflow/
这里的关键对象包括:
- Graph
- Node
- Edge
- Runtime State
- Event
- Layer
- Command Channel
真正复杂的 AI 编排逻辑,大多在这一层,而不在 controller 或 service 表层。
8.2 模型运行时链路
当请求涉及模型选择、模型调用、参数配置、Provider 凭证时,会继续进入:
api/core/model_runtime/
这一层负责:
- 统一不同模型供应商
- 统一不同模型能力类型
- 统一参数规则和鉴权方式
这也是为什么前端无需针对每个模型供应商改一套页面逻辑。
8.3 RAG 链路
当请求涉及数据集、文档导入、切片、召回、重排时,会进入:
api/core/rag/api/services/rag_pipeline/api/services/document_indexing_proxy/
此时请求主链路通常不再是一个同步数据库 CRUD,而会扩展为:
HTTP 请求 -> service -> 文档处理/索引任务 -> 向量化/入库 -> 检索配置生效
9. 异步任务与中间件如何介入主链路
很多 Dify 请求不是"一次 HTTP 完成所有工作",而是"HTTP 触发 + 异步处理继续执行"。
常见介入点包括:
- PostgreSQL
- 存主业务数据
- Redis
- 缓存、队列协同、状态协调
- Celery
- 文档处理、异步工作流、清理任务等
- Vector DB
- 知识库索引与检索
因此,Dify 的真实请求主链路往往是:
Browser -> Web -> API -> Service -> DB/Redis/Core -> Celery/VectorDB -> 回写状态 -> 前端轮询或流式消费
而不是普通 SaaS 那种:
Browser -> API -> DB -> Response
10. 样例 1:控制台"应用管理"请求链路
这里用"获取/创建应用"作为控制台主链路样例。
10.1 前端发起
文件:
web/service/apps.ts
典型调用:
fetchAppList()createApp()
它们最终通过统一 get/post 方法走到 API_PREFIX,也就是:
/console/api
10.2 后端路由入口
文件:
api/controllers/console/__init__.pyapi/controllers/console/app/app.py
console 蓝图统一挂在 /console/api 下,app.py 提供 app 相关资源接口。
10.3 控制器处理
在 api/controllers/console/app/app.py 中可以看到几类典型动作:
- 定义 Query/Payload 模型
- 校验 app mode、分页参数、tag ids 等
- 做登录/初始化/套餐/权限装饰器校验
- 调用
AppService
10.4 Service 承接业务
文件:
api/services/app_service.py
这里能看到 Dify 不是简单插一条 app 记录,而是会继续做:
- 根据 app mode 选择默认模板
- 通过
ModelManager解析默认模型 - 初始化
AppModelConfig - 写数据库
- 发出
app_was_created事件 - 根据 feature 开关更新 webapp access mode
- 清理 billing cache
这正是 Dify 请求主链路的典型风格:
一个看起来简单的接口,背后会联动多个平台子系统。
11. 样例 2:Web App"会话列表"请求链路
这里用公开 Web App 的会话列表作为第二个样例。
11.1 前端发起
公开 Web App 请求会设置:
isPublicAPI: true
这样统一请求层会走:
PUBLIC_API_PREFIX- 即
/api
同时自动加上:
AuthorizationX-App-CodeX-App-Passport
11.2 后端路由入口
文件:
api/controllers/web/__init__.pyapi/controllers/web/conversation.py
这里的资源路径是:
/api/conversations
11.3 Web wraps 注入上下文
文件:
api/controllers/web/wraps.py
WebApiResource 会通过装饰器自动执行:
- 解析请求头里的 app code
- 提取并校验 passport
- 找到
App - 找到
Site - 找到
EndUser - 校验企业版 Web App 权限模式
所以 controller 方法签名里可以直接拿到:
app_modelend_user
这是 Dify Web 侧请求设计里非常关键的一层抽象。
11.4 控制器处理
文件:
api/controllers/web/conversation.py
它会:
- 校验 app 是否是 chat / agent-chat / advanced-chat
- 校验分页参数
- 调用
WebConversationService.pagination_by_last_id(...) - 返回结构化分页结果
11.5 Service 与数据库
这一链路下,service 会结合:
app_modelend_userInvokeFrom.WEB_APP
去读取该终端用户在该 app 下的会话数据,再返回给前端。
这说明 Dify 的公开 Web 请求不是单纯"按用户 token 查数据",而是同时绑定:
- app 身份
- 站点身份
- 终端用户身份
- 访问来源
12. 从主链路理解 Dify 的设计特点
把上述链路压缩后,可以看到 Dify 有几个非常鲜明的设计特征。
12.1 入口分层清晰
- Console API 处理管理面
- Web API 处理运行面
- Service API 处理对外程序集成
这让不同访问者不会混进同一套路由语义里。
12.2 Controller 负责入口合法化
controller 不只是收参,它还做:
- 参数结构化
- 鉴权
- 上下文定位
- feature gating
- edition / billing / permission 校验
12.3 Service 负责跨子系统编排
service 往往要同时操作:
- 数据模型
- 配置模型
- 模型运行时
- 工作流
- RAG
- 插件
- 异步任务
12.4 Core 负责真正的 AI 执行能力
真正的护城河不在 HTTP 层,而在:
core/workflowcore/model_runtimecore/rag
13. 建议的源码阅读顺序
如果是为了"看懂 Dify 请求怎么走",建议按这个顺序读:
web/config/index.tsweb/service/fetch.tsweb/service/base.tsapi/app_factory.pyapi/extensions/ext_blueprints.pyapi/controllers/console/__init__.pyapi/controllers/web/__init__.pyapi/controllers/service_api/__init__.pyapi/controllers/console/wraps.pyapi/controllers/web/wraps.py- 任选一个垂直样例继续下钻:
api/controllers/console/app/app.py->api/services/app_service.pyapi/controllers/web/conversation.py->api/services/web_conversation_service.py
14. 总结
Dify 的请求主链路可以概括成一句话:
前端统一请求封装做第一次分流,Flask 蓝图做第二次分流,controller 完成入口合法化,service 编排业务,core 执行 AI 能力,最终由 DB/Redis/Celery/VectorDB 等基础设施共同支撑。
如果把这个链路真正看清,后面再读任何模块都会轻松很多,因为你会知道它处在整条链的哪个位置:
- 在入口层
- 在业务编排层
- 在核心 AI 执行层
- 在异步/存储支撑层