§19 避坑指南
这些坑不是我编的,来自 DeerFlow 自己的设计文档和代码改动记录。每一条都附正确姿势。
坑 1:同名 public/custom 技能互相串
现象:开 public 技能时,同名的 custom 技能也被开;配置键只用了 skill_name,两类技能分不清。
正确姿势:配置用组合键 {category}:{name},如 public:my-skill 和 custom: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_file 再 write_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 经验不照搬。