摘要:Harness把Claude Code和Codex变成子代理。搭建编码+审查+测试的多Agent流水线,拆解Profile Bundle、reportDelivery回传和非交互权限,附配置和踩坑。


上篇把Harness的"土法视觉"拆了个底朝天,最后留了个话头:rc.8干了一件更野的事——它把Claude Code和Codex这两个独立的编码Agent,直接收编成了Harness的子代理。不是"集成",不是"适配",是收编。Harness自己写代码未必最猛,但它现在能拆任务、派活、收结果、编排工作流,让Claude Code和Codex给它打工。

今天就来拆这个子代理系统。不是那种概念性的介绍,我会搭一个真实场景:一个需要"写代码→审查代码→写测试"的完整任务流,边做边看Harness怎么调度这三个角色。

先说清楚一件事:Harness到底在干嘛

动手之前,先对齐一个认知:Harness不是来替代谁的,它是来调度的。

最常见的疑问是:我直接装个Claude Code不好吗?为啥要在Harness里多包一层?先把三个东西的定位摆清楚:

  • Claude Code:钩子驱动的应用层治理,有自己的终端工作流,干单活很强,但它不调度其他Agent
  • Codex:内核级沙箱硬边界,安全隔离做得猛,但它也是单打独斗
  • Harness:自己未必是最强的编码Agent,但它是调度层——拆任务、分配给最合适的Agent、收结果、编排工作流

打个比方:Claude Code和Codex都是技术大牛,各自写代码都很猛。但如果你有一个项目需要先写代码、再做Code Review、然后补测试,你让一个大牛全干了,不如让Harness当项目经理,把活拆了分给不同的人干。

这就是Harness在rc.8做的事——从"独立Agent工作台"进化成了"Agent统一调度层"。

演进脉络:不是一步到位的

在正式搭场景之前,先花一分钟看看这个子代理系统是怎么一步步长出来的。

rc.8

做成即装即用的Profile Bundle

新增非交互权限模式

支持多个命名实例

reportDelivery机制成熟

rc.7 (8月17日)

Codex与Claude Code子代理任务接入Job Panel

统一管理,但还需要手动配置

rc.7的时候,Codex和Claude Code的任务已经能接入Job Panel统一看了,但配置起来还是有点糙。到了rc.8,直接做成了Profile Bundle——即装即用,装上就能调度。同时还加了两个关键能力:非交互权限模式(不用一次次手动确认)和多个命名实例(同一个任务里可以跑多个不同角色的Codex)。

搭一个真实的调度场景

直接上真实需求:写一个TypeScript的工具函数库,要求先写代码、再审查代码质量、最后补上单元测试。这是很标准的开发流程,平时要么自己全干,要么拉两个人配合。

用Harness的子代理系统,我这么编排:

第一步:安装Profile Bundle

先把Claude Code和Codex的子代理装上。rc.8之后这一步变得很简单:

# 安装Claude Code子代理Profile
npx dsh profile install @anthropic/claude-code

# 安装Codex子代理Profile(需要装两次,后面解释为什么)
npx dsh profile install @openai/codex --name coder
npx dsh profile install @openai/codex --name reviewer

注意第二条和第三条命令——我用了--name参数给Codex指定了不同的实例名。这就是rc.8新增的多个命名实例能力。同一个任务里,我可以跑两个不同职责的Codex:一个叫coder专门写代码,一个叫reviewer专门做审查。

这里不用两个不同的Profile,而是用命名实例,是因为Codex的沙箱隔离是它独有的优势,写代码和审查代码对沙箱策略的要求不一样——写代码要更宽松的文件访问权限,审查要严格只读。分开配置才互不干扰。

第二步:配置各角色的权限

安装完Profile之后,需要在任务配置里把各个子代理的职责和权限说清楚。这是package.json里的相关配置片段:

{
  "dsh": {
    "subagents": {
      "coder": {
        "profile": "@openai/codex",
        "permissions": {
          "filesystem": "write",
          "network": "restricted"
        },
        "interactive": false,
        "role": "负责根据需求编写TypeScript工具函数"
      },
      "reviewer": {
        "profile": "@openai/codex",
        "permissions": {
          "filesystem": "readonly",
          "network": "none"
        },
        "interactive": false,
        "role": "负责审查代码质量、安全性和最佳实践"
      },
      "tester": {
        "profile": "@anthropic/claude-code",
        "permissions": {
          "filesystem": "write",
          "network": "restricted"
        },
        "role": "负责根据代码和审查意见编写单元测试"
      }
    }
  }
}

这里面有几个关键点值得拎出来说:

interactive: false——这就是rc.8新增的非交互权限模式。没有这个的时候,Codex每次执行文件写入都需要你手动确认,在自动化工作流里根本跑不通。设成false之后,子代理可以在无人干预的情况下自动执行,整个流水线才能真正"流"起来。

permissions——不同角色的权限是不一样的。coder有写入权限但网络受限(防止它偷偷调外部API),reviewer是纯只读且完全断网(审查代码不需要改文件也不需要联网),tester有写入权限用来生成测试文件。

这种细粒度的权限控制,就是Codex的内核级沙箱带来的好处。Claude Code的权限管控更偏应用层,靠钩子(hook)来拦截,灵活但边界没那么硬。

第三步:编排任务流

配置写好了,接下来是调度逻辑。这是整个系统最有意思的部分。

Harness主任务拿到一个"写工具函数库"的需求后,大致会这么编排:

否,打回修改

Harness主任务
接收需求并拆解

调度coder子代理
编写TypeScript工具函数

reportDelivery
回传代码产出

调度reviewer子代理
审查代码质量

reportDelivery
回传审查意见

审查通过?

调度tester子代理
编写单元测试

reportDelivery
回传测试代码

Harness主任务
汇总所有产出,生成报告

这里最核心的机制是reportDelivery

reportDelivery:解决"父任务傻等"的老大难问题

做过分布式任务的人都知道,多Agent协作有个经典痛点:父任务把活派出去之后,怎么知道子任务完成了?轮询?回调?还是傻等?

轮询浪费资源,回调容易丢消息,傻等就更蠢了——万一子代理卡住了,父任务直接超时崩溃。

Harness用的reportDelivery机制,思路很直接:子代理完成任务后,主动把结果推送回来,同时唤醒等待中的父任务。不是父任务去问"你完了没",而是子代理干完了喊一声"我好了,结果在这"。

// 伪代码示意:主任务如何等待子代理结果
async function orchestrateTask(requirement: string) {
  // 派活给coder
  const codeResult = await dispatchSubAgent('coder', {
    task: `根据以下需求编写工具函数:${requirement}`,
    delivery: 'reportDelivery'  // 指定回传机制
  });

  // coder完成后reportDelivery自动唤醒这里,继续往下走
  const reviewResult = await dispatchSubAgent('reviewer', {
    task: `审查以下代码的质量和安全性:\n${codeResult.output}`,
    delivery: 'reportDelivery'
  });

  if (reviewResult.verdict === 'need_changes') {
    // 审查不通过,打回给coder重写
    return dispatchSubAgent('coder', {
      task: `根据审查意见修改代码:\n${reviewResult.comments}`,
      delivery: 'reportDelivery'
    });
  }

  // 审查通过,派活给tester写测试
  return dispatchSubAgent('tester', {
    task: `为以下代码编写单元测试:\n${codeResult.output}`,
    delivery: 'reportDelivery'
  });
}

单独看这段代码,好像就是个async/await。

区别在reportDelivery跑在Harness的调度框架里,超时重试、结果序列化、子代理崩溃恢复这些脏活它都替你处理了。自己写async/await,子代理进程挂了怎么办?网络抖动怎么办?结果太大传不回来怎么办?这些都得自己兜底。

跟直接用Claude Code/Codex比,到底差在哪

这些活我直接开三个终端窗口,分别跑Claude Code和Codex,不也一样吗?

能跑通。但体验完全两码事。

场景一:直接开三个终端手动协调

你打开终端A跑Claude Code写代码,写完之后手动把代码拷到终端B跑Codex做审查,审查完了再把代码和审查意见一起丢给终端C写测试。中间审查不通过?你还得手动把意见拷回去,让终端A改。

这个流程你能跑通,但你本质上就是那个"调度器"——你在做任务拆分、结果传递、流程控制。累不累?

场景二:在Harness里编排

你只需要在主任务里描述需求,Harness自动把活分给对应的子代理,reportDelivery自动传递结果,审查不通过自动打回重做。你从头到尾只需要看最终的汇总报告。

场景三:用Claude Code独立跑全流程

Claude Code确实有自己的终端工作流,也支持钩子做一些扩展。但它的设计哲学是"一个Agent干所有事"。让它又写代码又审查又写测试?不是不行,但它在审查自己写的代码时,天然就有"自己审自己"的问题。而且它不调度其他Agent,Codex的沙箱隔离能力它用不上。

场景四:用Codex独立跑全流程

Codex的安全隔离很猛,但它是为"单个任务的安全执行"设计的,不是为"多角色协作"设计的。你让它同时扮演coder和reviewer,它的沙箱策略就得妥协——写代码要写权限,审查要读权限,混在一起边界就模糊了。

所以Harness作为调度层的价值就在这:它不是来替代Claude Code或Codex的,它是来让它们各司其职的。 每个Agent做自己最擅长的事,Harness负责把这些碎片拼成完整的工作流。

社区插件dsh-agent-teams:多Agent协同的另一种玩法

除了内置的子代理系统,社区还有个插件叫dsh-agent-teams,提供更高层的多Agent协同能力。

但这里必须提一个社区踩过的坑:务必设置"监督者Agent"

两个编码Agent(比如一个Claude Code一个Codex)并行改同一个文件,很容易陷入一种诡异的死循环——A改了代码,B觉得不对又改回去,A看到B的修改又改回来……无限循环,Token烧得飞起,代码越改越乱。

社区的经验是,多Agent协同必须有一个"说了算"的监督者角色。在Harness的架构里,这个监督者天然就是主任务本身——它负责分配任务、裁决冲突、决定最终产出。这也是为什么Harness作为调度层而不是编码层很重要:它不"动手",所以不会有"自己改自己"的问题。

{
  "dsh": {
    "teams": {
      "supervisor": "main-task",
      "workers": ["coder", "reviewer", "tester"],
      "conflict_resolution": "supervisor_decides",
      "max_revision_cycles": 3
    }
  }
}

max_revision_cycles这个配置也值得注意——限制最多打回重做3次。防止审查Agent是个完美主义者,无限打回让编码Agent改到死。

一个容易忽略的细节:主任务的Prompt工程

前面都在讲子代理怎么配置、怎么调度,有个容易被忽略的点——主任务自己的Prompt也很关键

主任务是"项目经理",得把用户的需求拆成具体的子任务再分派下去。这个"拆分"的过程,全靠主任务的Prompt驱动。

如果你的主任务Prompt写得太模糊,比如就一句"帮我写个工具函数库",它可能拆出来的子任务也很模糊,导致coder不知道写什么、reviewer不知道审什么、tester不知道测什么。

我个人的经验是,主任务的Prompt里要显式定义每个子代理的输入输出格式。比如:

# 任务编排指令

当收到编码需求时,按以下流程执行:

1. 将需求拆解为具体的函数签名和接口定义,派发给coder
2. 将coder产出的代码连同原始需求一起派发给reviewer,
   审查维度:类型安全、边界处理、错误码规范
3. 如果reviewer返回need_changes,将审查意见派发给coder修改,
   最多修改2轮
4. 审查通过后,将最终代码派发给tester,
   要求:覆盖正常路径+异常路径,使用vitest框架
5. 汇总所有产出,输出:代码文件清单、审查报告摘要、测试覆盖率

这种写法看起来啰嗦,但实际跑起来效果比一句"帮我写代码"好太多。因为每个子代理收到的任务描述都很具体,不需要自己再去猜"项目经理到底想要什么"。

这也是Harness作为调度层的一个有趣特性:它的编码能力可能不是最强的,但它可以通过Prompt工程来弥补——好的调度指令能让60分的Agent干出85分的活

子代理之间的数据流向

再看一个点:子代理之间的数据怎么流转。

前面流程图里,所有数据都经过主任务中转——coder把代码交给主任务,主任务再转给reviewer。为什么不直接让coder把代码丢给reviewer?

这其实是Harness的一个设计选择:星型通信,不走网状通信

网状通信(不采用)

coder

reviewer

tester

星型通信(Harness采用)

coder

主任务

reviewer

tester

星型通信的好处是:主任务能看到所有数据流,方便做日志记录、审计追踪、冲突检测。如果子代理之间直接通信,主任务就失去了全局视角,出了问题也很难排查——你不知道coder传给reviewer的到底是什么版本的代码。

坏处也很明显:主任务成了瓶颈,所有数据都要过它一遍。但在当前这种任务量级下,这个瓶颈基本可以忽略。等Harness以后支持更大规模的多Agent并行调度时,这个问题可能需要重新考虑。

实际跑起来的几个注意点

最后说几个我自己跑这套流程踩过的坑:

1. 命名实例的配置不要搞混

两个Codex实例的--name一定要区分清楚,配错了就会出现"reviewer在写代码"或者"coder在做审查"的混乱局面。检查方法很简单:

npx dsh profile list

确认每个实例的name和role对应正确。

2. 非交互模式下要格外注意权限配置

interactive: false确实方便,但一旦开了就没有人工确认环节了。如果你的permissions配得太宽松,子代理可能会做一些你不想让它做的事。建议:

  • 审查类角色一律readonly + network: none
  • 编码类角色给write但限制network
  • 所有角色都不要给sudo权限

3. reportDelivery的结果大小有限制

如果子代理产出的代码量特别大(比如生成了几十个文件),reportDelivery可能会截断。这种情况下建议让子代理把产出写到约定的文件路径,主任务自己去读文件,而不是把所有内容都塞进delivery消息里。

4. 调试的时候先关掉非交互模式

第一次配置子代理的时候,建议先把interactive设成true,手动跑一遍流程确认没问题之后再关掉。不然出了问题你都不知道是哪一步卡住的。

这张全景图你得存一下

把所有东西合到一张图:

Harness 调度层 (rc.8)

子代理体系

派活

代码产出

唤醒

派活

审查意见

派活

测试代码

增强

主任务
需求拆解 + 流程编排

Claude Code 子代理
(Profile Bundle)
角色:tester
特点:钩子驱动治理

Codex 子代理 #1
(Profile Bundle, name:coder)
角色:coder
特点:沙箱写权限

Codex 子代理 #2
(Profile Bundle, name:reviewer)
角色:reviewer
特点:沙箱只读+断网

reportDelivery
结果回传 + 父任务唤醒

用户需求

汇总产出
生成报告

社区插件
dsh-agent-teams
提供监督者机制

Harness自己不是编码能力最强的那个,但它站在上面,负责把最合适的活分给最合适的Agent。Claude Code擅长终端工作流,那就让它写测试;Codex的沙箱隔离强,那就让它写核心代码和做安全审查。每个角色都在自己的舒适区里干活,整体效率反而最高。

从"工作台"到"调度层",这意味着什么

回头看Harness从rc.7rc.8的这一步,本质上是定位的转变。

之前的Harness更像是一个"Agent工作台"——你在上面配置模型、挂工具、写技能,但干活的还是Harness自己。现在它成了"Agent调度层"——它可以自己不动手,而是指挥Claude Code和Codex去干活。

这个转变的意义在于:以后如果有新的编码Agent出来(比如DeepSeek自己的编码Agent,或者别的什么),只要它能接入Harness的子代理协议,就能被调度进来。Harness的价值不在于"谁的代码写得最好",而在于"谁能把一群Agent组织得最好"。

这也解释了为什么Day02我们聊"一切皆插件"的时候我说Harness的设计哲学是"松耦合"——连子代理都是即插即用的Profile Bundle,换掉一个不影响其他的。


好了,子代理系统先拆到这。搞清楚了Harness怎么把Claude Code和Codex变成打工仔,怎么编排多角色协作,怎么避免Agent互相拆台。

但还有个问题悬着:今天用的子代理——Claude Code和Codex——都是国外的。国内模型呢?GLM、Qwen这些能不能接进来当子代理?

好消息是,Harness的Profile Bundle机制是通用的,理论上任何模型都能接入。但国内模型的配置确实有些独特的坑,比如API兼容层、上下文长度限制、工具调用的格式差异。这就是Day07的内容——接国内模型:GLM/Qwen配置全攻略。明天见。

本文由「梅雅达编程笔记」原创,首发CSDN。 DeepSeek Harness 从零到实战系列共10篇,点个关注不迷路✨
CSDN博客:https://blog.csdn.net/2601_96428997/

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐