最近 coding agent 圈子里有个现象:工具越来越强,但各用各的反而更乱。我一直用一个叫 DeepSeek Harness 的开源编排层,它不是一个编程助手,而是把 Codex、Claude Code 这类 agent 统一接进来、按任务分派、再收拢结果的调度中枢。这两天它放出重磅更新:官方插件列表正式加入 Codex 和 Claude Code 子智能体适配器,新增 Git 工作树支持,还多了一个"思考翻译"模块。收到更新通知当天我就升级试了,跑了两天下来,结论是这版不是功能堆叠,它把多 agent 开发里最别扭的几个环节一步打通了。下面把配置过程、任务分派逻辑和踩过的坑完整写出来,给准备升级或还在观望的朋友做个参考。
1. 这版更新到底解决了什么问题
1.1 多 agent 协作的旧痛点
先回顾一下没有这套更新之前,我是怎么折腾的。最开始的方案很朴素:开两个终端,左边跑一个 coding agent,右边再跑另一个。任务稍微一多,问题就全冒出来了。第一是上下文搬运,这个 agent 的中间结论要手工复制给另一个,稍微漏一句,后面全歪;第二是工作区冲突,两个 agent 都盯着一份代码改,A 改完的文件被 B 顺手覆盖是常有的事,直到 git diff 炸出一堆不是你写的改动才发现;第三是没法判断 agent 当时到底怎么想的,它执行到一半停下来,或者给出一个匪夷所思的方案时,你只能对着终端日志猜,推理过程基本是个黑盒。
DeepSeek Harness 当初就是为了收拾这些乱局而生的:它把"用户意图、任务拆分、子任务下发、结果回收、上下文汇总"串成一条完整的执行链。你只需要在一个会话里描述清楚目标,Harness 负责决定派谁去执行、给多少上下文、放什么权限、怎么把结果合回来。这套思路本身不算新鲜,但之前几个关键环节一直是瘸腿的——插件生态不完整、并行执行没有隔离、推理过程不可读,导致它只能"管得住"却"不好用"。这次更新的三个大项,恰好就是把这三条瘸腿逐一补齐。
1.2 三条主线是一根链条上的三个环节
这次更新最值得注意的是"多了三个功能"这个事实本身,而是这三个功能互相咬合的关系。Codex 和 Claude Code 进官方插件列表,解决的是"能派给谁"的问题;Git 工作树支持,解决的是"派出去之后怎么不互相踩脚"的问题;思考翻译,解决的是"派出去的活干得怎么样、为什么干成那样"的问题。三者合起来,多 agent 协作才第一次有了完整闭环。
举个最直观的场景。以前我让一个 agent 写测试、让另一个 agent 同时做重构,只能先等第一个跑完再放第二个进场,因为两个进程在同一份工作目录里跑,结果几乎必然互相污染。现在 Harness 会把每个子任务放到独立的工作树里,各自有独立分支,跑完之后由 Harness 统一做归并校验。整个过程里每个 agent 的决策过程又能通过思考翻译变成结构化摘要,你不需要再盯着原始日志发呆找线索。这种改变不是单纯的"多了几个按钮",而是整个协作模式的底层逻辑变了。
1.3 这版适合谁
说句实在话,如果你只是偶尔用一两个 agent 处理零散小任务,这版更新的价值你感知不强,甚至会觉得配置成本大于收益。但如果你是下面这类情况,那这版就是实打实的效率提升:单次任务涉及多个模块、多个步骤,需要不同 agent 分工协作;你希望在一个会话里完成"拆解—执行—验证—汇总"的完整流程,而不是开一堆终端自己当传话筒;又或者你经常要对比不同 agent 的方案优劣,想在合并前就搞清楚每个方案背后的取舍。我属于第三种,所以这版更新对我几乎是刚需。
2. Codex 与 Claude Code 子智能体:接入方式和任务分派
2.1 先搞清楚"子智能体"在架构里的位置
很多人第一次看到"子智能体"会下意识以为是套了一层壳,其实理解起来很简单:Harness 是父层,负责理解目标、拆任务、做最终决策;子智能体是执行层,拿到一个明确的小任务,在自己的上下文里干活,把结果交回来。这里说的"插件",本质是适配器,负责把 Harness 的任务描述翻译成各个 agent 能理解的指令,再把 agent 的输出解析回 Harness 的统一格式。你可以把它想成 USB 接口和不同设备之间的关系——统一接口在中间,设备和系统各管各的。
之前社区里已经有人做了非官方的集成脚本,但版本一变就碎,接口稍微调整就完全跑不起来。这次 Codex 和 Claude Code 以适配器身份进官方插件列表,意味着 Harness 每个版本发布前都会和它们做兼容性验证,出问题有人跟进,行为变化也能在升级日志里提前看到。对使用者来说,省下来的不只是配置时间,更是长期维护成本。这个"官方"身份在开源工具里其实非常重要,尤其当你拿它做日常工作流时,稳定性比功能丰富度更值钱。
2.2 最小配置长什么样
以我现在的环境为例,升级后安装插件、注册 agent 的完整步骤大概是这样的。先更新 Harness 本体并同步插件注册表:
ds-harness update ds-harness plugin sync接着安装两个适配器插件:
ds-harness plugin install codex-subagent ds-harness plugin install claude-code-subagent然后在配置文件里声明 agent。不同版本的字段名可能略有差异,但核心结构是固定的,我这里给一个配置示意:
[[agents]] name = "codex-worker" adapter = "codex" model = "codex-mini" worktree = true thinking_translate = true permissions = ["workspace:read", "test:run"] [[agents]] name = "claude-worker" adapter = "claude-code" model = "claude-sonnet-4" worktree = true thinking_translate = true permissions = ["workspace:read", "workspace:write", "git:commit"]配置完先跑一次健康检查,确认两个适配器都能正常拉起子进程:
ds-harness agents probe --name codex-worker ds-harness agents probe --name claude-worker这里有个容易忽略的点:子智能体是以独立进程方式运行的,它不会自动继承 Harness 的环境变量。你之前在系统里配好的凭据,子进程里未必读得到。我建议在 agent 配置里做显式的环境变量映射,或者用 Harness 提供的密钥管理入口统一注入,千万别指望"我这个终端里有的,它也有"。这个坑我最早踩过一次,子任务反复报鉴权失败,排查了半天才发现是环境变量没传过去。
2.3 任务分派不是写死脚本,而是带条件的路由
配置好之后,更关键的问题是怎么派活。Harness 的分派逻辑支持按"任务类型 + 上下文 + 前置规则"做路由。简单说,你可以告诉它:涉及测试生成的任务优先丢给 codex-worker,涉及大规模重构的先给 claude-worker 试,两类任务边界模糊时再让父智能体自己判断。它不是每轮都机械地执行同一个流程,而是会根据当前任务描述动态决定执行者。
我实际用下来,比较顺手的做法是在任务描述里直接点名执行者。比如:
主会话用户输入:把 A 模块的测试补齐,同时给 B 模块做一次接口重构。 Harness 处理:拆成两个子任务。 A 模块测试 -> codex-worker(工作树独立分支,完成后跑测试) B 模块重构 -> claude-worker(工作树独立分支,完成后做静态检查)
如果某个子任务失败,Harness 会把失败信息和执行结果贴回主会话,你可以选择让原 agent 重试、换一个 agent 重试,或者自己接手。这种"失败后可切换执行者"的能力,比单个 agent 的自我重试要灵活得多,因为它不依赖某个 agent 是否擅长这块领域。同一份需求,换一个擅长对应领域的 agent 来干,成功率往往会高很多。
2.4 权限边界是最容易被忽略的配置项
最后提醒一句权限。子智能体默认只读工作区,跑测试、读文件没问题,想直接提交代码需要显式授权。这个设计一开始让我觉得麻烦,后来发现完全合理——并行场景下,如果每个子 agent 都有提交权限,归并时你根本分不清哪个提交是谁改的。我现在的做法是:子智能体只允许改工作树内文件、在本地提交到自己的分支,合并回主干必须由 Harness 完成,权限收敛在一个地方,出问题也好追踪。别觉得这是多此一举,多人多 agent 协作时,操作留痕是你排查问题的唯一线索。
3. Git 工作树:并行开发从"排队"变"同时进行"
3.1 工作树是什么,一句话讲清楚
Git 工作树不是什么新概念,它就是让同一个仓库可以同时存在多个工作目录,共享同一套 .git 记录,但每个目录可以 checkout 不同的分支。理解成一个仓库开了好几个"分身"就行。手动使用很简单:
git worktree add ../wt-fix-42 -b fix/issue-42 cd ../wt-fix-42 # 在这个目录里做修改、提交,主目录完全不受影响用完想清掉,先退出工作目录再执行git worktree remove ../wt-fix-42,或者用git worktree prune清理失效引用。原理不复杂,但在多 agent 协作里,它解决了一个特别要命的问题:之前两个 agent 在同一片文件系统里跑,互相覆盖几乎是必然。有了工作树,每个子 agent 都在自己的目录里干活,互不感知,最后由 Git 层面来做合并。冲突从"操作时冲突"推迟到了"合并时冲突",处理难度完全不是一个量级。
3.2 Harness 里的推荐用法:一任务一分支一工作树
Harness 接入工作树之后,默认行为是每个子任务创建一个独立工作树,命名规则大约是worktrees/task-<id>/,分支名跟随任务编号。你需要关心的主要是两个参数:起始分支(base branch)和依赖目录共享策略。
起始分支这块我吃过亏。早前版本如果不显式指定 base,子任务会从当前分支拉工作树,而当前分支可能已经被别的子任务动过了,结果就是两个任务带着对方的半成品一起开工,合并时一团乱麻。现在我在每个 agent 配置里都固定写base-branch = "main",保证每个子任务都从干净的主干出发。这一点强烈建议你配置时就写死,不要依赖默认值。
依赖目录共享则是针对 node_modules、vendor 这类体积大、又没必要每个工作树都复制一份的目录。做法是工作树创建后,把依赖目录通过符号链接指到主工作区的缓存目录,或者直接配置 Harness 的共享缓存路径。这个优化在多个子任务并行时非常明显,不做的代价是磁盘占用直接成倍上涨,小仓库无所谓,中等规模前端仓库能差出好几个 G。
3.3 合并策略:不要太快,也不要太慢
工作树并行干活迟早要归并,归并的时机我建议控制在"子任务完成 + 通过本分支验证"之后,而不是"子任务写完代码"就立刻合。多等一轮测试和静态检查,能挡掉大量低质量合并。合并前统一先 rebase 到最新主干,再走 fast-forward,这样历史是一条干净的直线,回滚也方便。如果你对 rebase 有顾虑,也可以选择 merge 保留分叉记录,但子任务一多历史会变得很难读。这个取舍没有绝对正确答案,根据自己的回滚习惯和团队约定选就好,但一定要保持一致,别今天 rebase 明天 merge。
3.4 三个实际会遇到的小坑
第一,IDE 和文件监听工具对工作树目录往往不够友好。如果你在主工作区开着某个监控构建的进程,新增的工作树文件可能不会被及时纳入索引,需要手动加路径白名单。第二,子任务结束后没有及时清理工作树,长期累积几十个目录后,git worktree list会看得人头疼。我建议在 Harness 配置里打开"任务结束自动移除工作树"的选项,省心很多。第三,Windows 环境下路径长度限制会让某些深目录创建失败,这个属于老问题,解决办法是把整个工作区的根路径放在浅目录里,越浅越稳。
4. 思考翻译:把推理过程变成可交接的上下文
4.1 先搞清楚它翻译的到底是什么
"思考翻译"这四个字容易让人误解,它跟机器翻译、日志翻译完全没有关系。现在带推理能力的模型在给出最终答案之前,会先产出一段私有的推理轨迹,也就是我们常说的 thinking 过程。这段内容有两个问题:一是又长又乱,充斥着自我怀疑、试错分支、中途修正,人眼很难追踪;二是不同 agent 的推理格式五花八门,A 模型的思考方式搬到 B 模型的上下文里完全不适用。
思考翻译模块做的就是这件事:把原始推理轨迹重新组织成结构化的摘要,提炼出目标、关键决策、已排除的路径、遗留风险四个维度。它不是简单截断前几句话,而是对整条推理路径做一次压缩和归因,让读者能在几秒内明白"这个 agent 为什么选了这条路"。这里我放一个示意对比,左边是原始推理片段,右边是翻译产物:
原始片段: "用户想要列表页筛选,但等等,筛选条件是复用现有组件还是新写?如果用现有组件,状态管理可能要动。嗯,先看下 api 有没有对应参数……好像有,那还是复用吧。" 翻译结果: 【目标】列表页筛选能力 【关键决策】复用现有筛选组件,需要扩展状态管理 【已排除】不新增组件;api 已有对应筛选参数 【遗留风险】筛选状态与分页参数联动需额外验证4.2 三个实际用法
第一个用法是调试。子智能体返回了错误结果时,与其盯着终端里的原始输出翻半天,不如直接看翻译后的思考摘要,很快就能定位它是在哪一步开始跑偏的。第二个用法是上下文交接。一个子任务完成后,把翻译摘要作为下一阶段任务的附加上下文传给另一个 agent,比传一坨原始日志省 token,而且更不容易误导对方。第三个用法是审计和复审。我经常需要在合并前问一句"你为什么要这么改",有了结构化的思考摘要,这个问题能直接从记录里找到答案,不需要再靠回忆复盘。
4.3 成本控制:不是所有任务都需要开启
思考翻译也有代价,主要是延迟和 token 消耗。开启后每个带推理的任务都要多一步"把推理轨迹翻译成摘要"的处理,简单任务可能因此多等几秒,累积起来体验差异很明显。我的建议是按任务粒度开关:复杂重构、多模块改动、跨语言迁移这类任务开,替换字符串、改个配置值这类明确的小任务关掉。在配置文件里对应 agent 上把thinking_translate置为 false 即可,不需要全局禁用。别图省事全局全开,也别图省钱全局全关,按任务价值来,性价比最高。
4.4 这个功能帮我少踩的一个大坑
以前我遇到最多的情况是:子 agent 给出一个看着合理的方案,合并后才发现它选了一条不可维护的实现路径。后来回看才明白,原因往往不是它能力不行,而是我在任务描述里给了它隐含的误导——它为了满足我字面上的要求,绕开了更优解。有了思考翻译之后,我能在合并前直接看到它的决策依据,及时发现问题出在任务描述本身,改一句话重新下发就行,不用等代码合并完再返工。这件事对我个人来说,价值比任何一项自动化功能都高。
5. 实测场景与完整避坑清单
5.1 一次真实的并行任务实录
为了验证这版更新,我在模拟项目 X 上跑了一次完整的并行任务:仓库有两个模块,一个模块测试覆盖长期不足,另一个模块有一处明显可以重构的重复逻辑。我在主会话里用一句话描述了需求,剩下交给 Harness 拆解。实测下来,codex-worker 被派去补齐第一个模块的测试,claude-worker 负责第二个模块的重构。两个子任务各自开了工作树,几乎同时开工,从下发到子任务完成大约四分钟,最后 Harness 分别跑完测试和静态检查,把两个分支依次合并回主干。
整个过程里,我只在两个节点介入了。一次是发现 claude-worker 的重构方案把某个公共函数挪了位置,我通过思考翻译的摘要看到了它的决策依据,确认是合理调整;另一次是 codex-worker 生成的测试里一个边界值写错了,我没有让它盲目重试,而是把错误信息和测试文件路径作为新上下文重新下发,第二次就对了。前后对比一下,这版更新把我的介入从"每个环节都要盯着"降到了"关键节点确认一下",这是质的变化。
5.2 踩坑清单
| 现象 | 根因 | 解决办法 |
|---|---|---|
| 子 agent 的提交出现在主干分支上 | 创建工作树时未指定起始分支 | agent 配置里显式设base-branch = "main" |
| 两个子 agent 反复合并冲突 | 任务切分按模块而非按文件归属 | 配置 ownership 规则,把文件路径固定映射到 agent |
| 思考翻译结果被截断 | 推理轨迹超过单次输出上限 | 提高上下文上限或开启分段翻译 |
| 多个工作树导致磁盘占用翻倍 | 依赖目录被各自复制 | 依赖目录用符号链接指向共享缓存 |
| 子 agent 拿不到凭据 | 子进程不继承父进程环境变量 | 在 agent 配置里显式映射环境变量 |
| 工作树清理不及时 | 任务结束未自动移除 | 打开"任务完成即移除工作树"选项 |
5.3 值得一试的进阶姿势
最后分享两个我准备长期采用的组合。第一个是"分场景选 agent":给 codex-worker 固定派测试生成,给 claude-worker 固定派跨文件重构,然后在主会话里对比两者的思考摘要,挑质量更好的方案合入。这等于把每个 agent 的长处当成了可路由资源来调度。第二个是"强制验收门":在 Harness 配置里打开合并前的自动 lint 和测试门禁,子 agent 的任何改动都必须先过验收,否则合并请求直接驳回。这两个组合配合思考翻译,基本把我平时最容易出问题的人为疏忽环节都兜住了。
这版更新我目前最满意的,是它没有把三个功能做成三个孤岛。Codex、Claude Code 给了更多执行选择,工作树给了并行安全,思考翻译给了决策可见性,合起来才算是把"多个 agent 一起干活"这件事从折腾变成了顺手。如果你也在处理类似的多 agent 协作场景,建议升级后先从一两个子任务切起,把权限边界和工作树清理规则先配好,再逐步放大任务规模,这样比一次性全量上马稳得多。