子代理系统:Claude Code和Codex变成打工仔
摘要: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.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主任务拿到一个"写工具函数库"的需求后,大致会这么编排:
这里最核心的机制是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的到底是什么版本的代码。
坏处也很明显:主任务成了瓶颈,所有数据都要过它一遍。但在当前这种任务量级下,这个瓶颈基本可以忽略。等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自己不是编码能力最强的那个,但它站在上面,负责把最合适的活分给最合适的Agent。Claude Code擅长终端工作流,那就让它写测试;Codex的沙箱隔离强,那就让它写核心代码和做安全审查。每个角色都在自己的舒适区里干活,整体效率反而最高。
从"工作台"到"调度层",这意味着什么
回头看Harness从rc.7到rc.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/
葡萄城是专业的软件开发技术和低代码平台提供商,聚焦软件开发技术,以“赋能开发者”为使命,致力于通过表格控件、低代码和BI等各类软件开发工具和服务,一站式满足开发者需求,帮助企业提升开发效率并创新开发模式。
更多推荐
所有评论(0)