Day 01 / 共 20 天 · 阶段1 全景与架构

项目全景与前后端分包:先拿到地图,再进城

面对 Dify 这种上万文件的大项目,最怕的不是看不懂某段代码,而是不知道自己在哪。今天不读一行复杂逻辑,只做一件事:把整个项目的骨架画清楚——后端 api/ 和前端 web/ 各管什么、api/ 内部怎么分层、业务大脑 api/core/ 里各个子目录分别负责什么。读完你会有一张"心里的地图",后面 19 天每读一个文件都知道它在地图哪个格子。

📍 你在 20 天里的位置(阶段1:全景与架构 · D01-04)
D01 项目全景 D02 启动 D03 应用类型 D04 请求旅程 S2 模型运行时 S3 工作流 S4 RAG S5 工具/Agent S6 收官
💡 先用两个类比兜住今天 类比一:读大项目就像到一座陌生城市。你不会一下街道全背下来,而是先看地图——"东边是老城/商业区、西边是住宅区、中间是政务中心"。今天 Dify 这座城的分区是:api/(后端政务中心,所有决策在这)、web/(前端商业街,用户逛的门面)、api/core/(政务中心里的"核心决策部门")。类比二:api/core/ 各子目录像一家公司的部门——model_manager 是"对外采购部(管模型供应商)"、workflow 是"流程编排部"、rag 是"资料检索部"、agent 是"能自己决策的项目经理"。今天就是拿到这张组织架构图。
L01

痛点:几万个文件,到底从哪读起

🤔 痛点你 clone 下 Dify,ls 一看:api/web/docker/sdks/packages/e2e/……几十个顶层目录,光 api/core/ 里就有 agent、app、workflow、rag、tools、plugin、mcp、memory、prompt 十几个子目录。直接一头扎进某个 .py 文件,读两屏就迷路了:这个类被谁调用?这个目录和那个目录什么关系?缺的不是耐心,是一张地图。
💡 本质:先分"层",再分"块"任何 Web 后端项目都能套一个通用心智:接口层(收请求)→ 服务层(编排业务)→ 领域核心(真正干活)→ 基础设施(DB/缓存/队列)。Dify 也不例外。今天我们就用这把尺子,把它的目录一格格对号入座。学会"给陌生项目分层"这个动作,比记住 Dify 具体目录名更值钱——它对任何项目都管用。

先看顶层长什么样(在项目根目录执行 ls,真实输出节选):

# /Users/bitmart/work/codes/github/AI_WORK/dify/ 顶层
api/          # ← 后端(Python/Flask),本教程 90% 在这
web/          # ← 前端(Next.js)
docker/       # ← 部署编排(docker-compose 等),Day02 讲
sdks/         # 各语言 SDK(Python/Node/PHP...)
packages/     # 前端可复用包
e2e/          # 端到端测试
README.md  Makefile  pnpm-workspace.yaml  ...
大白话顶层这一堆里,真正装"业务逻辑"的只有两个:api/(后端)和 web/(前端)。其余的(docker/sdks/e2e/packages)都是"周边配套"。所以第一刀就切干净了:先只看 api/ 和 web/
L02

顶层分工:api/(后端)与 web/(前端)

Dify 是典型的前后端分离架构。看两个目录各自的顶层结构就一目了然(真实 ls 输出节选):

# api/ —— 后端(Flask 应用)
app.py            # 进程入口(Day02 细读)
app_factory.py    # 应用工厂:造 Flask app + 装扩展
dify_app.py       # DifyApp 类(Flask 的子类)
controllers/      # 路由层:console / web / service_api / openapi
services/         # 服务层:业务编排
core/             # ★业务大脑:模型/工作流/rag/agent/tools/...
models/           # 数据库表模型(ORM)
tasks/            # Celery 异步任务
extensions/       # 各种扩展(db/redis/celery/login...)
migrations/       # 数据库迁移
configs/          # 配置

# web/ —— 前端(Next.js App Router)
app/              # 页面路由
service/          # 调后端 API 的封装
context/  hooks/  models/  i18n/  ...
api/controllers/HTTP 路由入口。里面按"谁来调"分成 console(后台管理界面)、web(发布出去的应用页)、service_api(开放 API)、openapi。同一个功能常有三套入口,这是 Dify 的特点。
api/services/服务层。controller 拿到参数后,把活交给 service 去"编排"。比如 app_generate_service.py 负责"看这是什么应用、该调哪个生成器"(Day04 主角)。
api/core/★真正的业务核心。模型调用、工作流引擎、RAG、Agent、工具全在这。本教程 D05 起长期驻扎这里。
api/tasks/Celery 异步任务。像"文档索引""发邮件""删应用"这种耗时活,不在请求里同步做,扔进队列后台跑(Day02、Day19 讲)。
web/service/前端调后端接口的封装。前端不直接写 fetch,而是走这里统一的封装,和 controllers/ 一一呼应。
💡 设计取舍:为什么 controllers 要分 console / web / service_api 三套? 同样是"发一条消息给应用",后台调试(console,需登录鉴权)、公开的应用页(web,用 end-user 身份)、开发者用 API Key 调(service_api)三种场景的鉴权方式、限流策略、参数来源都不同。Dify 的选择是:把入口拆开、把核心业务收拢到 core。代价是同一功能有三个 controller 文件(看起来重复),好处是每个入口能独立控制权限与限流,核心逻辑却只写一份。这就是"入口多样、内核唯一"的分层智慧。
L03

api/ 内部:一次请求从上到下穿过哪几层

api/ 摆成"从外到内"的分层,一次请求的穿透路径就清楚了:

api/ 的分层(外层收请求,内层干活) controllers/ · 路由层:认证 + 取参数(console / web / service_api) services/ · 服务层:编排(看类型、选生成器、限流) core/ · 领域核心:模型 / 工作流 / rag / agent / tools models/ + extensions/ · 基础设施:DB / Redis / Celery 越往里越"重":controller 尽量薄,core 最厚(真正的复杂度都在这)
图注:这就是"洋葱式分层"。外层只做门卫的活(验票、登记),内层才是工厂车间。
💡 记住一句口诀"controller 薄、service 中、core 厚"。看到一个文件在 controllers/,就预期它没什么复杂逻辑(几十行,取参数转发);看到在 core/,就预期它是硬骨头。这个预期能帮你分配注意力——不在门卫室浪费时间。
还有几个"横向"目录不在主链路上但很常见:models/(数据库表定义,比如 AppConversationMessage)、extensions/(把 db/redis/celery 等挂到 app 上,Day02 讲)、configs/(读 .env 的配置)、factories/(构造复杂对象,如文件对象)。
L04

core/ 地图:业务大脑的各个"部门"

这是本教程最该记住的一张图。api/core/ 的真实子目录(ls api/core/ 输出节选):

# api/core/ —— 业务大脑
model_manager.py       # 模型实例:怎么拿到一个能调的模型(D05)
provider_manager.py    # 供应商:管几十家 LLM 厂商的配置/凭据(D05)
app/                   # 应用运行时:五种应用类型 + 生成器 + task_pipeline(D03/D04)
workflow/              # ★工作流引擎:节点/图/变量池(D08-12,Dify 最核心)
rag/                   # 检索增强:切片/索引/检索(D13-15)
indexing_runner.py     # RAG 索引流程的总调度
tools/                 # 工具系统(D16)
agent/                 # Agent:cot / function-calling 两种(D17)
plugin/  mcp/          # 插件与 MCP 协议扩展(D18)
memory/                # 对话记忆
prompt/                # 提示词构造(D07)
llm_generator/         # 基于 LLM 的小工具(起标题等,D07)
ops/                   # 可观测/链路追踪(D19)
callback_handler/  moderation/  file/  entities/  ...
app/"应用"这层是入口枢纽:一次对话请求进 core 后,先落在 app/apps/ 里对应类型的生成器。D03 拆它的类型、D04 走它的完整流程。
model_manager.py / provider_manager.py模型运行时的两根支柱。前者产出"一个能 invoke_llm() 的模型实例",后者管"这个租户配了哪些供应商、凭据是什么"。D05 精读。
workflow/★Dify 的招牌能力。拖拽式工作流最终都变成这里的一张"图",由引擎逐节点执行。整个阶段3(D08-12)专攻它。
rag/ + indexing_runner.py知识库背后的引擎:文档进来先被 indexing_runner 切片、向量化入库,检索时再多路召回。阶段4(D13-15)。
agent/能"自己决定下一步"的智能体,两种范式:cot_agent_runner.py(思维链)和 fc_agent_runner.py(函数调用)。D17。
大白话core/ 想成一栋写字楼,每个子目录是一个部门。你现在不用懂每个部门具体怎么运作,只要记住"哪个牌子挂哪个门"。后面每天,我们就推开其中一扇门进去看。

地图不是空话——给三个"真实锚点",让你现在就能翻到源码里对上号:

锚点①:后端 app 就是一个 Flask 子类(api/dify_app.py:11)——证明 api/ 是 Flask 应用。

# api/dify_app.py:11
class DifyApp(Flask):
    """Flask application type with Dify-specific extension attributes."""

    login_manager: DifyLoginManager

锚点②:五种应用类型是一个枚举(api/models/model.py:364)——这就是 D03 要展开的 app/apps/ 各个子目录对应的类型。

# api/models/model.py:364
class AppMode(StrEnum):
    COMPLETION = "completion"
    WORKFLOW = "workflow"
    CHAT = "chat"
    ADVANCED_CHAT = "advanced-chat"
    AGENT_CHAT = "agent-chat"

锚点③:模型运行时的"入口对象"叫 ModelInstanceapi/core/model_manager.py:35)——D05 会拿它 invoke_llm()。现在只需知道"调模型从这个类开始"。

# api/core/model_manager.py:35
class ModelInstance:
    """
    Model instance class.
    """

    def __init__(self, provider_model_bundle: ProviderModelBundle, model: str, credentials: dict | None = None) -> None:
        ...
这三段你现在不用读懂,只需验证"地图上的格子在源码里真实存在"。这是读大项目的好习惯:每认一个目录,随手翻一个真实文件确认它是干这个的,地图才不会变成空中楼阁。
core 子目录负责什么本教程哪天精读
app/应用类型 + 请求生成 + 流式输出D03 / D04
model_manager.py / provider_manager.py模型运行时D05 / D06
prompt/ / llm_generator/提示词与生成D07
workflow/工作流引擎(核心)D08-12
rag/ / indexing_runner.pyRAG 知识库D13-15
tools/ / agent/ / plugin/ / mcp/工具·Agent·插件D16-18
tasks/(在 api/根) / ops/异步任务与可观测D19
L05

前端 web/ 一瞥:知道它在哪、怎么和后端对话

本教程重点是后端,但你得知道前端长什么样、怎么和后端接上。web/ 是一个 Next.js(App Router)项目(web/app 是页面、web/service 是接口封装):

# web/ 关键目录
app/        # 页面(Next.js App Router):应用列表、编辑器、对话界面
service/    # 调后端的封装:每个函数对应一个后端接口
context/    # 全局状态(当前用户、当前 workspace、模型供应商...)
hooks/  models/  i18n/  themes/  ...
💡 前后端怎么"对上"前端 web/service/ 里的每个请求,最终打到后端 api/controllers/ 里的某个路由。比如"发一条聊天消息":前端调 service → 打到后端 controllers/.../completion.pyChatApi.post(Day04 你会看到这个真实入口)。所以调试一个功能时,你可以从前端 service 找到接口路径,再去 controllers 里搜同名路由,两头就接上了。
大白话前端是"遥控器",后端是"机顶盒"。遥控器按钮(web/service)发出信号,机顶盒(api/controllers)接收并执行。你想看某个按钮到底干了啥,就顺着信号从遥控器追到机顶盒。
L06

建立地图 + 今日小结

👶 小白:我一定要按这个目录顺序全读完吗?感觉好多。

👨‍🏫 老师:不用。这张地图的价值是"随用随查"——你不用背下来,只要以后看到一个类,能快速判断"它大概在哪个层、哪个部门"就够了。真正要精读的是主链路:controllers → services → core/app → core/model_manager → core/workflow。旁支(moderation、file、callback)用到再看。先掌握主干,枝叶自然生长。

⚠️ 新手常见误区看到 core/ 里几十个子目录就想"每个都点开看看",结果一天过去只记住一堆目录名、没建立任何联系。正确姿势:盯住"一次请求怎么走"这条线(Day04 会完整跑一遍),沿线经过的目录才深入,没经过的先跳过。地图是用来导航的,不是用来背诵的。

🧠 今天你应该能回答

  • Dify 顶层真正装业务的是哪两个目录?(api/ 后端、web/ 前端)
  • api/ 的分层口诀是什么?(controller 薄、service 中、core 厚)
  • controllers/ 为什么分 console / web / service_api?(鉴权、限流、来源不同,核心逻辑收拢到 core)
  • api/core/ 里工作流引擎在哪个目录?(workflow/,Dify 最核心)
  • 模型运行时的两根支柱是哪两个文件?(model_manager.py / provider_manager.py
  • 前端功能怎么和后端接口对上?(web/service 的请求 → api/controllers 的路由)

✋ 10 分钟动手

# 1. 亲眼看顶层与 api 分层
cd /Users/bitmart/work/codes/github/AI_WORK/dify
ls                    # 看顶层:api / web / docker / sdks ...
ls api                # 看后端分层:controllers services core models tasks ...

# 2. 把 core 地图打印出来贴墙上
ls api/core           # agent app workflow rag tools plugin mcp memory prompt ...

# 3. 找一条前后端对应关系(体会"遥控器→机顶盒")
grep -rn "class ChatApi" api/controllers   # 后端聊天入口在哪个文件
ls web/service                              # 前端接口封装
明日预告 · Day 02:地图有了,明天让它"跑起来"。我们读 api/app.pyapp_factory.pydify_app.pydocker/——看 Dify 是怎么用"应用工厂"造出 Flask app、按顺序装上 20 多个扩展,以及同一份代码怎么分身成 api / worker / beat 三种进程角色。
← 返回总目录 Day 02 · 环境搭建与启动 →