§03 一次对话背后发生了什么
你点下发送,到答案回来,中间跨了四层服务、两套运行路径。先把拓扑画清楚。
服务拓扑
DeerFlow 2.0 的生产拓扑是这样的:
Nginx (2026)
├── /api/langgraph/* → Gateway 的 LangGraph 兼容运行时
└── /api/* → Gateway REST API(端口 8001)
Frontend (Next.js,端口 3000)
Provisioner (可选,端口 8002,负责按需拉起沙箱)
注意:Nginx 在前,Gateway 在后。所有 /api/* 都进 Gateway,只有 /api/langgraph/* 走 LangGraph 兼容运行时。
两套运行路径,一个 Agent 工厂
这是 DeerFlow 架构里最容易被误解的点。现在代码里有两条运行路径,但它们共用同一个 Agent 工厂:
| 路径 | 触发方 | 运行方式 | 持久化 |
|---|---|---|---|
Web/Gateway run_agent() |
浏览器/API | 异步 astream + StreamBridge 做 SSE |
写 thread_meta |
DeerFlowClient.stream() |
终端 TUI / 嵌入 | 同步、进程内 | 只写 checkpointer |
两条路都通过同一个 make_lead_agent / create_agent() 工厂构建 Agent。也就是说,Agent 行为一致,编排外壳不同。
一次对话的完整生命周期
你的消息
→ Gateway 接收,注入已认证用户上下文
→ run-launch helper 启动一个 run
→ lead agent(主代理)拿到 prompt
→ 命中 Skill?加载对应技能
→ 需要子任务?拆给子代理并行跑
→ 要执行代码?进沙箱(bash / 写文件)
→ 要调外部能力?走 MCP 或内置工具
→ 工具调用前过护栏(GuardrailMiddleware)
→ 中间状态写 checkpointer + event store
→ 流式结果经 StreamBridge 回前端
→ 会话索引写 thread_meta,前端侧边栏可见
关键存储有两个,别混:
- checkpointer:LangGraph 的状态快照,记录「当前消息列表」。
- event store(RunJournal):追加式事件流水,记录每一条 trace / message / lifecycle / middleware 事件。它不受上下文压缩影响——这点 §08、§16 会展开讲。
标叔的结论:理解 DeerFlow,先记住「Gateway 管调度和持久化,Agent 工厂管智能,LangGraph 管状态和流式」。三层职责分清,后面每章都不乱。
下一章,我们聊最容易让人兴奋也最容易踩坑的事:换模型。