
很多人是在真正跑了一次多代理编程之后才意识到 subagent 不是一个“多问模型几次”的问题而是一整层工程问题。最近看到有人在 Hacker News 上开源了一个面向 Codex 和 Claude 的 subagent runtime标题写得很收敛让 subagent 体验更好。但你真在仓库里拆过任务、让多个代理并行干活就会明白这件事背后藏着的复杂度远比“把 prompt 写得更清楚”大。subagent 直译过来是“子代理”。在一个较大的编码任务里主代理不会自己从头写到尾而是把调研、实现、补测试、写文档、代码审查这些环节拆出去交给一个或一组独立会话去完成。每个子代理可以有不同的目标、不同的工作目录、甚至不同的模型。等它们执行完后再把结果回收给主代理。这套思路在演示视频里很顺畅可一旦放到真实代码库上问题就全冒出来了谁负责隔离文件权限谁把子代理的执行过程转成日志两个子代理同时改同一个文件怎么办子代理失败后它写了一半的内容算不算数所以我想围绕这个方向展开聊一聊。真正决定 subagent 体验好坏的往往不是模型能力本身而是有没有一层足够可靠的 runtime开源 runtime 的价值也不是让模型更聪明而是把这些散落在脚本里的工程细节变成可复用、可观察、可控制的基础设施。1. subagent 的真正难点不是模型变强而是运行时变厚1.1 从“对话式编码”到“委派式编码”传统编程助手本质上是“对话式编码”你和模型坐在同一个工位前你们共享同一个上下文所有讨论都留在聊天记录里。遇到问题时你会顺手让模型读文件、跑测试、改代码每一步你都看得到。subagent 模式则不一样它更接近“委派式编码”。主代理接受到一个高复杂度任务后不再单线程地完成任务而是把任务切块分发给多个下级代理。这些子代理各自拥有独立上下文执行过程中会产生文件修改、工具调用、中间决策最后只把一个相对结构化的结果交回主代理手里。这个变化会立刻引出两个约束主代理的上下文窗口有限它不可能把每个子代理的思考过程都完整读一遍。多个子代理在同一个仓库里协作必须有明确的边界否则改着改着就会互相覆盖。于是任务的执行方式从一次长对话变成了一次短周期、多进程、可横向扩展的分布式作业。你需要有人去分配任务、记录状态、汇总产物、处理错误。这层东西就是 runtime。1.2 它和 Tool Call 的本质差异不少人会这样理解subagent 本质上就是一种特殊的工具调用主代理把任务包装成一个函数然后等待返回值。这个抽象并没有错但它掩盖了 subagent 和普通工具调用之间巨大的工程差异。普通 tool call 的生命周期很短通常一次调用只做一件事输入输出直接暴露在主上下文里。subagent 则不一样它内部可能要做多轮推理、多次读写文件、调用各种外部命令最后还不一定以一段文本结尾而是产出一个补丁、一份报告、一个测试结果目录。用下面这个表格看会更清晰对比项普通 Tool CallSubagent生命周期通常一次调用包含多轮思考和多次工具调用上下文关系输入输出都在主上下文里上下文隔离只回传结构化摘要典型返回值一段文本或一个 JSON文件、diff、报告、结构化结果过程可观察性主流程里能看到调用参数需要独立会话日志和事件流失败后果一次调用失败主流程兜底可能在仓库里留下半成品修改恢复复杂所以从产品设计角度你可以把 subagent 当作“另类的 tool”但到了实现层面它更像一个需要进程管理、状态存储、沙箱边界、结果回收的作业单元。正是因为这种复杂度runtime 才成为一个独立话题而不是被随便塞进某个 CLI 里就完事。2. 为什么官方 CLI 离“subagent 友好”还差一层2.1 Codex 和 Claude Code 的优势在人机交互Codex 和 Claude Code 这类终端工具解决的核心问题是一个人如何更高效地和模型协作。它们把模型能力放进了终端或 IDE让开发者可以用自然语言指挥模型读代码、写代码、跑命令。从“人”的角度看它们是很好用的交互层。但 subagent 场景有一个微妙变化真正调用 subagent 的往往不是“坐在终端前的人”而是另一个程序。这个程序可能是主代理也可能是一段持续集成脚本。程序不希望从终端拿一段人类可读的文字它更希望拿到结构化状态、可回放的操作日志、以及可验证的文件产物。这就使得官方 CLI 在很多编排场景里显得不够“服务化”。CLI 更多是在完成人与模型的会话而不是开放一个可组合、可注入、可拦截的运行单元。2.2 缺失 runtime 时你只能自己造轮子我见过不少团队尝试自己搭一个多代理协作流程。一开始大家觉得只要把 Codex 和 Claude 的 CLI 像 shell 命令一样循环调用就行。结果跑了几次后发现真正工作量的重心早就从“写 prompt”转移到了这些地方工作目录隔离主代理和子代理应该共享哪些目录子代理能不能越界读取生产配置环境变量与密钥管理不能把.env、~/.ssh、云厂商密钥直接暴露给一个行为不可完全预知的代理。二进制和运行库依赖Codex CLI、Claude Code 本身的安装依赖、系统级运行库、Node/Python 环境不同机器可能完全不同。日志与会话恢复子代理执行到一半退出已经做的步骤能不能保留下一条记录从哪里继续文件冲突处理两个子代理同时修改同一个文件时是覆盖、报错还是做合并这些问题哪一个单拎出来都不难但数量一多就会变成一大片无人维护的胶水代码。开源 runtime 想做的就是把这层胶水固化下来。2.3 很多安装报错其实都在提醒你 runtime 的重要性另一个容易被忽略的现象是在真实使用 Codex、Claude Code 这类工具时用户经常连第一步“把工具跑起来”都会卡住。热搜里最常见的几个问题例如 WebView2 runtime not found、unable to locate the codex cli binary、OCI runtime create failed、.NET runtime无效本质上都和“环境里缺少某个运行单元”有关。这类报错看起来像是工具坏了实际是本地环境缺少了某个系统级运行库、桌面组件运行时或者二进制路径没有被正确配置。真正要在生产环境维护一个 subagent runtime 时你必须把语言运行时、系统运行库、模型 CLI 二进制、权限目录都纳入管理否则换一台机器整套流程就会断掉。这正好说明了一件事当多代理编排成为主流程时运行环境不再是你“装一次就忘掉”的东西而是要像依赖锁文件一样被显式记录和校验。3. 一个 subagent runtime 真正要处理的设计问题既然要做一个开源 runtime它到底该关心什么因为没有实际使用过这个项目我这里只从通用工程视角拆几个绕不开的设计问题所有配置都只是示意结构不是某个项目的官方格式。3.1 任务描述怎么标准化如果一个 runtime 要向 Codex、Claude 或其他模型分发子任务首先得定义一套任务描述格式。任务描述不能只是几句自然语言它至少要包含角色、目标、约束、上下文入口、期望输出位置。这样 runtime 才能把任务变成可调度、可审计的单元。一个常见的任务描述可能是这样{ role: code_reviewer, goal: review the theme switch implementation in ./frontend, constraints: [ read-only; do not modify source files, ignore node_modules and dist, output a report.md, not just a chat summary ], context: { include: [./frontend/src/styles, ./frontend/src/hooks], exclude: [./frontend/.git] }, artifacts: { report: ./output/review/result.md } }任务描述标准化之后后面的事件日志、失败重试、并行调度才有稳定的对象。否则每个任务都长成“一段 prompt”你很难回答“这个任务到底让子代理访问了哪些文件、产出了什么”。3.2 上下文怎么隔离又怎么回收这是整个 runtime 里最容易踩坑的地方。子代理不应该自动继承主代理的全部上下文。它只需要看到和当前子任务相关的文件、约束和历史记录。否则主代理的上下文限制解决不了反而多出数倍 token 开销。设计上通常采用两段式处理输入侧runtime 按任务描述裁剪上下文把指定目录、相关文件、git diff 注入给子代理。输出侧runtime 不把子代理的完整思考过程拼回主代理而是要求它把“最终修改”写成补丁或文件把“决策摘要”写进报告再按结构化摘要回收。这种方式相当于让每个子代理在独立工作区里完成自己的活最后只交回一份“结案摘要”和若干产物文件。真正需要看细节时再去查阅对应目录里的日志和报告而不是让主代理硬吞几百 KB 对话记录。3.3 文件系统权限怎么收敛当多个代理在同一个仓库里协作权限控制就变成了安全问题。一个负责“读代码并输出分析”的子代理完全没有必要获得写入权限一个负责“实现新功能”的子代理也应该只能在自己的工作区或指定目录里写文件。一个示意性的权限配置可能长这样workspace: root: ./case-001 allow: - ${workspace}/src - ${workspace}/output read_only: - ${workspace}/config deny: - ${workspace}/.git - ${workspace}/.env这种做法不是限制模型能力而是在保护整个仓库。代理的行为本身有不确定性尤其是当它看到一条权限不足的命令时可能会有各种尝试绕过的倾向。runtime 需要从一开始就限制文件系统能力而不是等它真改了不该改的文件后再去恢复。3.4 事件和数据怎么流转subagent 运行过程充满异步调用创建任务、读取文件、执行命令、写入结果、失败重试。如果 runtime 只在最后返回一个结果那所有中间状态对用户和管理者都是黑盒。比较推荐的模式是让 runtime 把内部事件以结构化日志的方式暴露到一条统一事件流上。每条事件可能是这样{type: task.started, task_id: task_001, agent: claude, ts: 2025-01-01T00:00:00Z} {type: tool.called, task_id: task_001, tool: filesystem.write, path: ./output/report.md, ts: 2025-01-01T00:00:01Z} {type: task.completed, task_id: task_001, result: ./output/report.md, ts: 2025-01-01T00:02:00Z}这种设计让我想起一个类比runtime 像一根总线IDE、CLI、持续集成脚本都只是总线上的接收面板。真正的运行细节不散落在某一个终端里而是集中在可检索的日志流里。出现问题时你可以沿着事件流追出“子代理在什么时候、调用了什么工具、写了哪个文件”。3.5 要不要兼容 Codex 和 Claude 两层模型开源 runtime 选择同时支持 Codex 和 Claude大概率是因为实践中很少只有一个模型在干活。不同模型在某些环节各有优势有的擅长架构梳理有的擅长生成测试用例有的大段文档输出速度更快。如果 runtime 能把它们封装成统一的“子代理会话接口”那上层主代理就无所谓底层跑的是 Codex 还是 Claude。比较理想的状态是只暴露下面这种概念接口SubAgentSession.run(task: TaskSpec, event_handler) - RunResult至于底层是调 Codex 的 API还是启动 Claude Code 的进程都由适配器负责。这样换模型时上层任务编排不用改主要成本落在适配层。3.6 失败恢复和预算控制subagent 一旦并行跑起来成本和失败概率都会放大。一个子代理可能会在几十秒内连续调用很多次工具token 消耗肉眼可见地增加。因此 runtime 一定要给每个任务设置边界比如最大步数、最大 token、超时时间。对失败任务比较稳妥的做法是先把子代理已经产生的中间文件保留下来再决定是重试还是换一个提示词继续。读操作型任务往往可以直接重试但写操作型任务要格外小心。同一个补丁如果因为超时被重复应用会让仓库进入冲突状态。所以在设计时要尽量减少“子代理自己写文件”和“runtime 统一帮你打补丁”的混用。一旦确定用哪套机制就保持一致否则后续排查会非常困难。4. 落地边界哪些场景值得用哪些先别上4.1 适合与不适合的场景subagent runtime 听起来很有潜力但它不是银弹。在实际落地前最好先做一个判断典型场景是否值得上 subagent runtime单次问答、查资料、解释一段代码不需要直接用主代理聊就行小仓库的单功能重构可以先手动拆两到三个子任务多模块改造需要并行梳理、补测试、写文档值得重点观察文件冲突和上下文隔离每周都会重复的代码审查、测试生成很值得把流程沉淀成可复用任务只跑一次的一次性脚本不一定编排治理成本大于收益关键不是“能不能用”而是“用了之后维护成本高不高”。如果任务本身只出现一次花大量时间定义任务规范、配置权限、调试事件流反而得不偿失。4.2 runtime 不负责任务分解只负责把任务执行好一句话需要说透runtime 能保证任务被执行、被记录、被隔离但它不能保证任务拆得合理。如果主代理把任务拆成互相依赖的碎片子代理之间再等到做完才发现需求冲突runtime 也很难自动修复。因此真正的核心能力还是主代理对任务结构的理解以及人对整个流程的监督。runtime 更像是一条保险丝加一根总线它防止单个子代理越界也把整个过程暴露给你看但它不会替你判断“这个仓库到底应该怎么改”。4.3 先解决本地运行环境的稳定性在评估 subagent runtime 之前很多人连第一步都会被卡住。你可能已经在网上见过类似报错代码工具在启动时提示缺少某个 runtimeWindows 下会报 WebView2 runtime not foundLinux 容器环境里可能报 OCI runtime create failed还有各种“找不到 cli”的问题。它们表面上是不同工具的毛病本质却都是同一件事本地运行环境没有被打理干净。如果要用这类工具做正经的 subagent 流程最好先养成一套排查顺序先确认报错来自哪个二进制是模型 CLI还是 IDE 插件还是容器运行时。再检查它依赖的系统运行库、桌面组件是否安装版本是否匹配。然后检查 CLI 路径是否正确有没有安装多个版本导致调用到旧路径。接着检查当前工作目录的权限、目录可写性。最后才去怀疑模型接口或子代理逻辑本身。这几个步骤看起来基础但在日常使用中绝大多数“工具打不开”“启动后闪退”问题答案都出在这一层。4.4 成本、人工审批和可追溯性并行 subagent 会让执行速度变快也会让 token 消耗和工具调用次数同步上升。更麻烦的是某个子代理一旦跑偏它可能在一个错误的方向上重复调用工具很久直到预算耗尽。行之有效的方法有三个先只并行两个子代理观察整体 token 量和耗时再慢慢加并发。对高风险操作设置人工审批例如删除文件、覆盖线上配置、执行外部命令。每次任务结束后保留任务描述、事件日志、最终产物确保同一任务能被复盘和回放。这些措施不复杂却是 runtime 进入生产环境前必须补齐的工程能力。5. 给想试的人一条最小落地路径5.1 不要从复杂框架开始如果你想认真体验 subagent 协作我建议先不要急着搭一套完整 runtime。大多数团队现在最缺的其实是对“任务拆解和产物回收”的真实感受。可以按下面这个顺序来project/ runs/ 001_login-refactor/ tasks/ research/ input.md result.md implementation/ patch.diff review/ issues.md manifest.yaml第一步准备一个干净目录把任务拆成研究、实现、审查三个子任务。第二步手动把其中一个子任务交给 Codex 或 Claude 执行要求它把产出写到指定文件而不是只在聊天框里回复。第三步看执行结果是否完整它读了哪些文件产出了什么有没有把不必要的内容写进工作区第四步再加一个子任务让两个子代理并行执行观察是否会互相覆盖文件。第五步如果重复多次仍然稳定再考虑引入 runtime 或自定义 harness 去把它固化下来。这个过程的意义在于它会强迫你提前思考“任务边界”和“产物格式”。即使最终你没有用到任何开源 runtime这些经验也会让后续落地顺畅很多。5.2 判断该不该引入 runtime 的三个信号当下面几个问题的答案多数为“是”时就值得认真看一下 runtime 方向同一类任务是不是会周期性出现比如每个迭代都要生成测试、做代码审查、更新文档。是不是需要多个代理并行处理不同模块而不是一个代理单线程跑完整件事。是不是需要把执行过程变成团队可复用的配置而不是每次都在终端里临时口头交代。是不是需要结果可追溯、可回滚甚至连失败现场都能还原。如果一个都不满足直接用官方 CLI 可能更舒服如果满足了两个或以上那 runtime 带来的治理能力会逐渐超过它的配置成本。5.3 验收时不要只看“任务完成”最后提醒一个容易被忽略的点当子代理跑完你验收的绝对不只是“它说自己完成了”。你还要验证修改文件没有越过权限范围。产物文件真实存在且内容格式正确。执行过程在日志里可回放能回答“哪个代理、在什么时候、改了什么文件、为什么这么改”。失败之后能恢复而不是每次都要从头重跑。在 subagent 场景里输出不等于生成一段文本。输出是改到磁盘上的文件、写进日志里的决策、沉淀在报告里的结论。一个真正好用的 runtime本质上就是在帮你保证这些输出可以被确认、被追溯、被复用。所以回到最开始那个判断Codex 和 Claude 的模型能力已经足够让人看到 subagent 的潜力但潜力要变成生产力还需要一层可靠的运行时来承接。这个方向被开源出来值得关注的价值不在某个具体命令或界面而在于它提醒了我们复杂的多代理协作终究不是靠几段聪明 prompt 就能稳定运转的。真正让人放心的是那些把执行过程管住、把失败边界收住、把每一步都记录下来的基础设施。对想尝试的人来说下一步可以先别求多用最小流程跑两个子任务亲自感受一下断点会出现在哪里。