第 14 章:部署、调试与扩展 —— 从“读懂”走向“改得动”
最后一章不重复安装说明,而是总结各运行模式的真实边界、排障顺序和安全扩展点。
默认服务拓扑
生产 Compose 包含 Nginx、Frontend、Gateway、Redis,以及按 sandbox 配置启用的 Provisioner。默认只发布:
127.0.0.1:${PORT:-2026} -> nginx:2026Gateway 8001、Frontend 3000、Provisioner 8002 都只在容器网络内。Gateway 在容器内监听 0.0.0.0 是正常的;真正的外部暴露由 Compose port binding 决定。
Redis 负责跨 worker 的 StreamBridge,不等于主数据库。生产若把 GATEWAY_WORKERS 调大,却仍使用 memory bridge,重连请求可能落到另一个 worker 而看不到原运行事件。
三种开发入口
| 入口 | 适用场景 | 主要命令 |
|---|---|---|
| 整栈本地 | 调前后端联动 | make dev |
| 后端模块 | Agent/Gateway 开发 | cd backend && make dev/test |
| 前端模块 | UI 与事件投影 | cd frontend && pnpm dev/check/test |
根 Makefile 负责 orchestration,模块 Makefile/package scripts 负责单层。先明确问题在哪一层,避免为了一个 reducer 单测启动完整 Docker。
配置加载与重载边界
主配置来自仓库根 config.yaml,MCP/skills 状态来自 extensions_config.json。AppConfig 用 Pydantic 校验并缓存,某些字段可 reload,另一些影响进程级结构:
- checkpoint channel mode 与 snapshot cadence;
- database/checkpointer engine;
- stream bridge 类型;
- 某些后台服务与连接池。
这类字段修改后需要重启。源码用 freeze/reload boundary 明确拒绝运行时漂移,不能仅因为 YAML 能保存就假设立即生效。
从症状反推层次
“模型没调用某工具”
按顺序查:工具是否加载 → group/agent allowlist → RBAC 组装过滤 → 是否被 deferred → 技能是否激活 → schema 是否被 middleware 隐藏。不要一上来改 prompt。
“文件写了但页面没有”
查工具结果与 workspace → 是否调用 present_file → ThreadState artifacts 是否更新 → values 是否发布 → Artifact provider 是否刷新。写文件与交付文件是两条链。
“刷新后少了步骤”
查 run event store → messages/page 的 seq → 是否被 hidden/control 过滤 → checkpoint 是否已经摘要 → 前端 gap 恢复是否触发。不要用当前 checkpoint messages 直接判断完整历史。
“Stop 后仍显示运行中”
查 cancel outcome、owner lease、finalizing、terminal event 与前端二次 refetch。取消是分布式状态变化,不是单纯 task.cancel()。
“只在多 worker 复现”
优先检查 memory store/bridge、共享数据库、worker lease、session affinity 和本地文件是否被错误当作共享存储。
推荐扩展点
新工具
实现 LangChain BaseTool,加入 config;声明稳定且唯一的 name,提供有界输出与 async 路径。涉及文件时只使用 sandbox 虚拟路径;涉及角色时接入 AuthorizationProvider/Guardrail,而不是在工具里读客户端 user_id。
新中间件
先写清它需要在哪个 hook 观察什么、是否写 ThreadState、与哪些中间件有顺序关系。SDK 工厂优先用 @Next/@Prev 锚点;产品 Lead Agent 可通过 configured extensions 注入。新增 state 字段时必须定义并发 reducer 和 delta-mode 兼容性。
新沙箱 Provider
实现 acquire/get/release 与 Sandbox 接口;验证每用户/线程隔离、只读映射、timeout、进程终止和 shutdown。最容易漏的是 reverse path mapping:命令输出中的真实路径也要翻回虚拟路径。
新 Memory Backend
继承 MemoryManager,明确是否支持 search、tool mode 是否仍需 passive writes,并实现 async 边界。测试冲突、损坏文件、并发 flush 与用户命名空间。
新 Channel
复用 ChannelManager 到统一 run 生命周期,不要另写 Agent loop。需要定义平台事件去重、thread 映射、sender 身份、附件同步、消息长度/Markdown 转换和重试语义。
测试策略
DeerFlow 的测试分层值得保留:
- reducer/解析器用纯单元测试;
- Gateway router 用依赖替身验证 HTTP 契约;
- blocking IO 测试用 Blockbuster 捕获 event loop 上的同步调用;
- 前端 unit tests 验证 stream folding;
- Playwright 用路由 mock 测真实页面交互;
- live 外部 API 测试显式 opt-in。
改运行时可靠性时至少验证:正常、取消、异常、断线重连、重复事件、乱序/晚到、跨进程恢复。只测 happy path 无法覆盖 Agent 产品最昂贵的故障。
最终心智模型
DeerFlow 的核心不是某一个环,而是每条箭头都有明确契约:身份从哪里来、状态怎么合并、事件如何恢复、能力在哪一层拒绝。读懂这些箭头,你就从“会用 Agent 框架”走到了“能设计 Agent 系统”。
源码锚点
至此主线结束。需要落到具体文件时,继续查看源码阅读地图。