Haven的小空间

双模型协作工作流:用 Claude + DeepSeek 提升开发效率

3 min read

在用 AI 辅助开发时,有个常见问题:随着对话越来越长,模型会开始丢失早期的上下文,反复犯同样的错误,或者做着做着把方向带偏了。另一个问题是角色混乱——你希望它做架构决策,它却去改代码细节;你希望它执行,它却反复问你方向。

这套工作流的思路是:把两个模型的角色分开,用三个文件管理协作状态,让每个模型只做自己擅长的事。

两个角色

全局者(Claude):负责方向、决策、审查。不读大量文件,不做迭代修改,只在关键节点介入——项目启动时定架构,工作者遇到分歧时给决策,阶段完成后做 review。

工作者(DeepSeek):负责执行、迭代、细节实现。读文件、查资料、写代码、改 bug,是实际干活的角色。完成一个阶段后把结果写入 context.md,然后等待确认或继续。

两个角色跑在不同的终端 session 里,用 cc 启动全局者,cc-ds 启动工作者。

三个文件

WORKFLOW.md

定义游戏规则:两个角色各自的职责、触发切换的条件、context.md 的写入格式。每次 session 启动时两个模型都会先读这个文件,确保行为一致。

CLAUDE.md

项目级别的启动指令。告诉模型每次 session 开始时必须做什么:

  1. WORKFLOW.md
  2. context.md(先看顶部的当前状态,再看最新历史)
  3. 复述确认:「我是[角色],当前任务是:[摘要]」,然后开始工作

这三步保证任何一方开启新 session 时都能快速恢复上下文,不需要用户重新交代背景。

context.md

这是整个工作流的核心。结构分两部分:

顶部的 ## 当前状态:全局者每次写入决策时同步覆盖这里,放最新的任务清单。工作者启动时优先读这里,快速定位当前要做什么。

下方的历史记录:仅追加,永不修改。每次角色写入都留一条带时间戳的记录,形成完整的决策和执行流水账。

如何使用

初始化项目:

cd /workspace/projects/<项目>
cc-init   # 复制三个模板文件到项目目录

日常使用:

cc      # 启动全局者(Claude)
cc-ds   # 启动工作者(DeepSeek)

启动时选择角色(1=全局者 / 2=工作者),模型会自动读取 WORKFLOW.md 和 context.md 恢复状态。

切换角色:

工作者完成一个阶段或遇到需要决策的问题,写入 context.md 后说「已写入 context.md,请切换到全局者」。用户去全局者终端输入:

「工作者修改了内容,查看 context 文件」

全局者读取后给出决策,写入 context.md,再说「决策已写入,可切换回工作者继续」。用户去工作者终端输入:

「全局者修改了内容,查看 context 文件」

这两句话是切换信号,模型收到后会主动去读 context.md,不需要用户再重新解释背景。

后来发现这两句话其实可以做成 slash 命令,少打几个字。在 .claude/commands/ 下放两个文件,内联读取指令:

  • 全局者终端输入 /as-overseer ≡ 「工作者修改了内容,查看 context 文件」(意:我现在接手全局者角色)
  • 工作者终端输入 /as-worker ≡ 「全局者修改了内容,查看 context 文件」(意:我现在接手工作者角色)

之所以叫 as-X 不叫 from-X,两个理由:一是语义上”我现在是什么角色”比”来自谁”更直觉;二是键盘上 a/s 相邻,起手比 f-r 跨距小,输入更顺手。语义完全一致,只是更便利的入口。手打话语仍然有效。

触发切换的条件

工作者遇到以下情况时必须停下,切换到全局者:

  1. 出现架构方向分歧,有多个方案无法判断选哪个
  2. 遇到安全、性能相关的设计决策
  3. 完成一个完整模块,需要确认方向再继续
  4. 连续两次尝试仍无法解决某个问题

条件 4 很重要——与其让工作者反复试错消耗 token,不如切到全局者换个思路。

context.md 的写入格式

全局者写入(同时覆盖当前状态 + 追加历史):

## [YYYY-MM-DD HH:MM] 全局者
**背景:** 为什么做这件事
**决策:** 具体决策内容
**任务清单:**(推荐每项附「完成标准」;任务名已清晰描述何为完成时可省略)
- [ ] 任务 1(完成标准:客观可检验条件)
- [ ] 任务 2

工作者写入:

## [YYYY-MM-DD HH:MM] 工作者
**完成情况:** 阶段结果摘要
**任务进度:**
- [x] 任务 1
- [ ] 任务 2(遗留:原因)
**关键决策点:**(每项必须显式回答,无则写「无」)
- 架构/接口变动:无 / 列出改动
- 安全相关:是否涉及 认证/密钥/用户输入/SQL/文件路径/外部请求
- 偏离原计划:无 / 列出与任务清单不符之处
- 未解决的疑虑:继续推进但没把握的点

工作者触发切换时写入:

## [YYYY-MM-DD HH:MM] 工作者 → 全局者
**问题:** 遇到了什么
**已尝试:** 做了什么、为什么不行
**需要决策:** 全局者需要回答什么
**附带变动:** 本次顺带做的接口/计划偏离,无则「无」

格式统一之后,任何一方开启新 session 都能通过历史记录还原完整的决策链,不会出现「不知道上次为什么这么做」的情况。

一个补丁:审查其实没在发生

这套工作流跑了一段时间后,发现一个藏得很深的问题:全局者名义上要”阶段性审查工作者输出”,但实际上它根本没在审查。

问题出在写入格式。工作者完成后只写”完成情况”和”任务进度”——全是结果(做完了什么),没有决策(怎么做的、动了什么)。全局者拿着一份结果摘要,无从判断方向有没有偏、有没有踩安全坑,审查只能停在”看起来做完了”的表面。

修法不是让全局者去读全部代码(那违背它”不读大量文件”的原则),而是让工作者在交付时主动暴露关键决策。在工作者写入格式里加了四项强制清单:

**关键决策点:**(每项必须显式回答,无则写「无」)
- 架构/接口变动:...
- 安全相关:是否涉及 认证/密钥/用户输入/SQL/文件路径/外部请求
- 偏离原计划:...
- 未解决的疑虑:继续推进但没把握的点

设计上有意把事实上报风险判断分开:工作者只需如实回答”改没改接口""碰没碰这些安全敏感面”——这是事实题,它即使不懂风险也答得出;风险判断交给全局者。其中”未解决的疑虑”是单条价值最高的一项,专门抓那种工作者没卡住、所以从不触发切换、但其实心里没底的盲区——这种情况以前完全漏在网外。

配套给全局者加了一条硬规则:只要安全项非空,就必须 invoke critic 子代理(放在 .claude/agents/critic.md,用 Haiku 跑)去读那些文件并返回结构化审查报告,不能只看摘要。这把”必须读代码”操作化了——全局者自己仍然不读大量文件,由 critic 代读、出报告,全局者凭报告判断。这是给安全相关改动留的逃生口:默认走轻量自报,一旦触到安全面就回退到真审查。

这个补丁不会自动生效

得诚实说一点:整套机制押在工作者自报诚实上,而工作者是 DeepSeek。模型有个倾向——为尽快收尾,把四项反射性全填「无」,尤其”未解决的疑虑”(承认不确定恰恰是模型最不愿做的)。一旦它在安全项该填却填了「无」,前面那个”安全项非空→必读代码”的逃生口就被空填绕过,整套机制空转。

所以对策不在再改格式,而在全局者的行为:与某个 DeepSeek 工作者协作的早期,即使它安全项写「无」,也要拿实际 diff 抽查一两次,先摸清这个工作者到底是诚实自报还是反射填「无」,据此校准对清单的信任度,而不是默认全信。机制设计能做的到此为止,剩下的得靠实跑校准。

实际效果

这套工作流目前用在了博客搭建和其他几个项目上。最直观的改变是:工作者(DeepSeek)专注执行,上下文几乎不会被无关的架构讨论污染;全局者(Claude)只在真正需要判断的节点介入,不会陷入细节。

两个终端同时开着,切换一句话,协作比单模型顺畅不少。

后记:写完才发现的 prior art

写完这套工作流、把它推到一个公开仓库之后,才想起一件本该最早做的事——去 GitHub 搜搜有没有类似的东西。结果有,而且不止一个。

这个模式在 LLM agent 圈本来就不新鲜:aider--architect 模式一行命令就能让强模型规划、弱模型编辑;AutoGenCrewAIMetaGPTLangGraph 这些多 agent 框架,规划者 / 执行者本来就是它们的标配分工。

直接撞型的小项目也有,而且都比我这个早:

诚实说,我这套不算新东西,只是众多小变体里的一个——人介入切换、零额外依赖、Markdown 作总线的特定组合。真正还能算我自己想出来的,大概只有「关键决策点 + 自报诚实校准」那一段——这段是这次复盘里冒出来的,没在那几个对应仓库的文档里见到。

教训也写在这:写文章和建仓库前应该先去 GitHub 搜一遍 prior art,这是 OSS 礼仪,也避免自己陶醉于”独创”。这次顺序反了,补一节诚实交代。

完整的对比列表放在公开仓库的 README 里:Haven16262/dual-model-workflow#related-work


工作流文件模板和项目配置见项目页面