DeerFlow 2.0 · 从入门到精通 目录

§19 避坑指南

这些坑不是我编的,来自 DeerFlow 自己的设计文档和代码改动记录。每一条都附正确姿势。

坑 1:同名 public/custom 技能互相串

现象:开 public 技能时,同名的 custom 技能也被开;配置键只用了 skill_name,两类技能分不清。

正确姿势:配置用组合键 {category}:{name},如 public:my-skillcustom:my-skill 分开。同名时 API 补 ?category=

{ "skills": { "public:my-skill": { "enabled": true }, "custom:my-skill": { "enabled": false } } }

坑 2:TUI 建的会话 Web 看不见

现象:终端跑的会话,开 Web 侧边栏不显示。

根因:早期 DeerFlowClient 只写 checkpointer,不写 thread_meta,而 Web 从 thread_meta 读列表。

正确姿势:用 2.0 的新版——TUI 复用同一 thread_meta 持久层,终端会话自动进 Web。老版本请升级,别自己补同步逻辑。

坑 3:改文件前没读,被读写门拦

现象:代理想改已存在文件,返回「请先读该文件」,写入失败。

根因ReadBeforeWriteMiddleware 默认开,要求改前 read-mark 的 hash 和当前文件一致。

正确姿势:让代理先 read_filewrite_file / str_replace。这是护栏不是 bug——它治的是「只追加不回读」导致的重复产物。别为了省事关掉它。

坑 4:护栏里客户端伪造身份

现象:有人试图在请求体里塞 user_id=admin 提权。

正确姿势:DeerFlow 注入只信服务端认证态 request.state.user,客户端 body 里的身份字段不覆盖。你写自己的 provider,也别读客户端传的 user 字段——读 runtime.context 里 Gateway 注入的那个。

坑 5:定时任务租约和重叠

现象:多实例部署时任务重复跑;或上一个 run 没结束又触发。

正确姿势:调度器靠 DB 租约原子认领,强制 enabled: true 才跑;重叠按 skip、错过按 run_once。部署多实例时,别自己加 leader 开关——DB 租约就是护栏。注意 min_interval_seconds 默认 60,短于它的用户任务会被拒。

坑 6:生成 provider 被自动切走

现象:半夜生成突然走了不同的供应商,产物风格变了。

根因_resolve_provider() 自动判断,两套密钥都在时按「现有 provider 优先」,但你若只配了覆盖变量又改了名,行为会偏。

正确姿势:想锁定,显式设 IMAGE_GENERATION_PROVIDER=gemini 这类环境变量,覆盖自动判断。别依赖「自动」做生产决策。

坑 7:历史被压缩后 feedback 错位

现象:给历史消息点赞,赞到了错误的 run。

根因/history(checkpoint)和 /messages(event store)两条数据链 AI 消息数不一致。

正确姿势:2.0 已让 /history 改读 event store,两边对齐,顺手修好。确保你用的是新版本;老版本别在压缩后的长对话里依赖点赞归属。

标叔的结论:这七个坑,本质都指向 DeerFlow 的设计哲学——默认安全、显式优先、持久层单一真相。你顺着这三条走,坑基本都绕得开。


写在最后

DeerFlow 2.0 不是一个「又一个小代理库」。它是字节把内部研究代理产品化后,开源出来的完整工作台:底层 LangGraph 编排,上层 Gateway/Frontend/TUI/Provisioner 服务栈,中间用 Skills 做扩展、用护栏做安全、用 event store 做不可篡改的历史。

你如果只想嵌个代理,它重;你想给团队一个「能搜、能写、能生成、能定时、能审计」的代理平台,它刚好。

书到这里。下一步建议:把 §02 跑起来,照 §07 写一个自己的 Skill,再用 §12 加一道你业务的护栏。动起手,比读十遍都管用。

本文所有事实均来自 DeerFlow 官方仓库(README、CLAUDE.md 摘要、规格与代码改动文档)。2.0 为重写版本,v1 经验不照搬。