自己动手写Agent Harness【subagent】:子代理机制(subagent)

写在前面:系列是为了帮助大家更好的去理解Agent Harness基础设施,并不是想重复造轮子,真实开发建议选择一个成熟的SDK或Harness框架,才是最合适的选择~

1. 最小 harness 有了,缺的是「会派活」

前五篇我手写了一套最小 harness:能 loop、有工具、有审批、有会话记忆。它够干活,但只会一件事——自己干。你丢给它一个大任务,它一个人从头扛到尾,上下文越滚越乱,中途想换个思路只能推倒重来。缺的那格叫「会派活」。

单 agent 是执行者,子代理让 harness 会分工。子代理不是「更多 agent」,是两件事——把任务拆出去 + 把上下文隔离。 本文给一条从 spawn 到 fork 的极简实现路径。

2. 先看现成的:Claude Code 的 subagents 三件事

动手之前,先看 Claude Code 怎么做。官方文档「Create custom subagents」把子代理讲成三个设计点。

description 是「招工启事」。 子代理定义在 .claude/agents/ 下的 Markdown 文件里,frontmatter 必填两样:namedescriptiondescription 不写进子代理自己的上下文,它写给父 agent 看——父 agent 靠它决定「这个任务要不要派出去、派给谁」。官方文档原话:写详细的 description,因为 Claude 靠它决定何时派活。你在 description 里写「用于代码评审」「优先主动使用」,父 agent 就知道什么时候该找它。这跟我们《动手开发你的第一个 agent :给它装手和眼睛》那篇的工具 description 作用一模一样——决定「何时」被选。

隔离上下文,防污染。 子代理有自己的 context window、自己的 system prompt、自己的工具权限。父会话只收到子代理的最终结果,看不到它的中间思考、它翻过的每一页文件。官方文档说得直白:subagents 把重的文件读取和搜索挡在主会话外面,保护父上下文。想连文件改动都隔开,还有 isolation: "worktree"——给子代理一个临时 git worktree,它在里面改东西,主工作区不受影响。

prompt 须自包含。 子代理不继承父会话的历史(fork 是例外,后面讲)。它从零开始,只看到自己的 system prompt 和父 agent 写给它的那一份任务描述。所以任务描述必须自带全部背景——「读文件 package.json 并总结它的版本」这种话,子代理接得住;「把刚才那个文件读一下」这种话,子代理直接懵,因为它不知道「刚才」是谁。

这三件事里,后两件正好是这篇要实现的:隔离上下文、prompt 自包含。description 那件,我的实现里用 spawn_subagent 工具的 description 字段承载——它就是给主模型看的「招工启事」。

dsh 那边,我在《DeepSeek Harness 开源了,[一切皆插件],把他拆开看一看》拆过它的 SubagentProvider 接口(packages/subagent/subagent/src/types.ts:285),注册了五类实现:

  • spawn:新建子 agent,inheritsParentContext = false,孩子从零开始,看不到父对话;
  • fork:继承父的已完成轮次前缀(inheritsParentContext = true),从父的上下文续着干;
  • acp:进程外的 ACP 子 agent,无父上下文;
  • claude-code / codex:把一轮委派丢给 Claude Code 或 Codex 的进程当后端。

最后两个最开脑洞——主 harness 可以把脏活外包给别的 harness。这个我第五节对照时再提。先记着:五类实现里我最关心的是前两个,spawn 和 fork,它们是「子 agent 从哪来」这个问题的两个最基本答案。

3. Demo 先行:给 harness 加 spawn / fork

代码在 examples/first-agent/step5-subagent/index.js,零依赖,node step5-subagent/index.js 直接跑。先看两个系统提示词——它们把「上下文隔离」写进了提示词:

export const PARENT_SYSTEM =
  '你是一个主 agent,会派活:把完全独立的子任务交给 spawn 子代理(从零开始、无父会话记忆);' +
  '把需要在已有记忆基础上继续的子任务交给 fork 子代理(继承父会话前缀记忆)。' +
  '拿到子代理回传结果后,用一句话总结。'
export const SUBAGENT_SYSTEM =
  '你是一个子代理,只处理父 agent 交给你的这一个子任务。' +
  '你没有父会话的记忆,任务描述是自包含的——你看到的会话消息就是你的全部世界。' +
  '读到工具结果后,用一句话总结。'

SUBAGENT_SYSTEM 那句「你看到的会话消息就是你的全部世界」不是装饰——即便模型真有父会话的信息,提示词也明确要求它别依赖。这是把「隔离」从机制层面写进约束。

派活的两个原语,长在 SessionLog 上。一个是 spawn——从零新开子会话;一个是 fork——按 boundary 切子会话,继承前缀:

  // spawn:从零新开一个子会话。与父会话零共享——这就是「上下文隔离」的实现。
  // 子代理的会话是全新 SessionLog,父会话的事件、boundary 一概不继承。
  spawn() {
    return new SessionLog()
  }

  // fork:从指定 boundary(默认最后一次 turn/start)切子会话,继承前缀记忆。
  // 切法:复制 events[0..cut],cut 是 boundary 的 seq。boundary 之后的
  // 「父会话第二条」这类消息不继承——子代理只看到切点之前的记忆。
  fork(seq) {
    const cut = seq ?? this.boundaries.at(-1)
    if (cut === undefined) return this.spawn() // 还没有 boundary → 等价于 spawn
    const child = new SessionLog()
    child.events = this.events.slice(0, cut + 1).map((e) => Object.freeze({ ...e }))
    child.boundaries = this.boundaries.filter((b) => b <= cut)
    return child
  }

spawn 一行——new SessionLog(),空会话,父会话的事件一概不复制。fork 三行——从最后一次 boundary 处切开,复制 events[0..cut] 进子会话。两个原语加起来不到十行,语义正好相反:一个「什么都不给」,一个「给到切点为止」。

关键的一步是让主模型能调用这两个原语。做法和《动手开发你的第一个 agent :给它装手和眼睛》给 harness 加工具一模一样:把 spawn 和 fork 各自包装成一个工具,注册进主会话的 ToolRegistry。这是本文的核心主张——子代理不是一个新对象,它就是「工具 + 内部多一个 loop」

export function makeSubagentTools({ mainLog, childLLM }) {
  // 子代理的模型默认用基类 MockLLM(只干普通工具的活,不主动派活)。
  // 想让子代理也能递归派活?把子工具注册表也传给子会话即可——子代理即工具,可组合。
  const llm = childLLM ?? new MockLLM()

  const spawn_subagent = {
    name: 'spawn_subagent',
    description: '从零新开一个子会话(子代理没有任何父会话记忆),独立完成一个子任务并回传结果。适合完全独立、可拆出去的任务。',
    parameters: {
      type: 'object',
      properties: {
        prompt: { type: 'string', description: '给子代理的自包含任务描述。子代理没有父会话记忆,prompt 必须把任务背景说全。' },
      },
      required: ['prompt'],
    },
    async execute(args) {
      // spawn:全新 SessionLog。父会话此刻的所有事件一概不复制——
      // 这就是「上下文隔离」的实现:子代理从一张白纸开始。
      return await runSubagent({ llm, prompt: args.prompt, inherited: null, name: 'spawn_subagent' })
      // 真实工程:spawn/fork 都一样,这里一次只派一个子代理、同步 await 等它跑完,
      // 主 loop 完全串行。生产做法是任务队列/并发池:把一批子任务交给调度器,多个子代理
      // 并发扇出(对照 Claude Code 的 subagent 扇出),全部返回后统一收结果再汇总。
    },
  }

  const fork_subagent = {
    name: 'fork_subagent',
    description: '从父会话最后一次 turn/start boundary 切一个子会话,子代理继承到该 boundary 的前缀记忆,基于已有上下文继续完成子任务并回传结果。',
    parameters: {
      type: 'object',
      properties: {
        prompt: { type: 'string', description: '给子代理的子任务描述。子代理能看到继承来的前缀记忆。' },
      },
      required: ['prompt'],
    },
    async execute(args) {
      // fork:继承前缀记忆的子会话。此刻主会话日志里最后一个 boundary 就是当前
      // 这个「派活 turn」的起点——从那里切,子代理看不到派活本身的过程,
      // 只看到 boundary 之前的记忆(父会话更早的历史)。
      const inherited = mainLog.fork()
      return await runSubagent({ llm, prompt: args.prompt, inherited, name: 'fork_subagent' })
    },
  }

  return [spawn_subagent, fork_subagent]
}

两个工具长得几乎一样,唯一的分歧在 execute 里:spawn 传 inherited: null(从零开),fork 传 mainLog.fork()(继承前缀)。剩下都交给同一个 runSubagent——它就是「子代理即工具」的实现:

// runSubagent:真正跑子代理的公共逻辑。spawn 与 fork 都走这里,只差 inherited 是不是 null。
async function runSubagent({ llm, prompt, inherited, name }) {
  // 子会话:spawn 传 null → 全新;fork 传已继承前缀的日志。
  const child = inherited ?? new SessionLog()
  // 真实工程:子会话此刻只活在内存里——main() 退出即失,无法重放。
  // 生产做法是让子会话也走 step4 的 append-only 落盘(persist/resume):
  // 子会话事件独立写盘(如 dsh 里子 agent 的会话目录),主进程崩溃后按日志重放续跑。
  const childRegistry = new ToolRegistry()
  childRegistry.register(read_file) // 子代理手里有普通工具(读文件),没有派活工具
  const childPipeline = new ToolPipeline(childRegistry)
  const childLoop = new ReactLoop(llm, childRegistry, childPipeline, child, SUBAGENT_SYSTEM)
  // 真实工程:这里的子代理不是真隔离——childLoop 只是主进程内 new 的 ReactLoop 实例,
  // 与主 loop 共享进程堆,无独立 CPU/内存/权限边界,子代理能读到的东西和主 agent 一样多。
  // 生产做法是把子代理放进独立子进程(Node worker_threads / child_process.fork,自带资源与
  // 权限沙箱),或接外部 harness 当后端:dsh 的 SubagentProvider 就是 spawn/fork/acp/claude-code/
  // codex 五类「换子 Agent 从哪来」,能嵌套别的 agent,实现进程级隔离;子代理的会话文件
  // 独立落盘,实现跨会话持久隔离。

  const mode = inherited ? 'fork·继承父前缀记忆' : 'spawn·全新会话'
  console.log(`\n┌─ ${name} 开始(${mode}`)
  const finalText = await childLoop.turn(prompt) // 子 loop:一个完整 turn
  // 真实工程:这里对子代理没有 token/时长/步数上限,子 loop 跑多深全看它自己——
  // step1 的 maxTurns 停机思想只保护了单 loop 本身,没延伸到子代理。
  // 生产做法是给每个子代理设独立预算:maxSteps / token 预算 / 超时回收,
  // 超出即终止并标记结果状态,防止子代理失控拖垮整次任务。

  // 打印子会话派生消息,直观展示「spawn 只有自己的任务 / fork 还有继承来的记忆」
  const derived = child.deriveMessages()
  console.log(`  子会话派生消息(模型看到 ${derived.length} 条):`)
  for (const m of derived) {
    if (m.role === 'toolResult') console.log(`    [toolResult:${m.toolName}] ${String(m.content).slice(0, 40)}`)
    else if (m.toolCall) console.log(`    [assistant/toolCall] ${m.toolCall.name}`)
    else console.log(`    [${m.role}] ${String(m.content).slice(0, 40)}`)
  }
  console.log(`└─ ${name} 结束,子会话共 ${child.events.length} 条事件`)

  // 回传给主会话的 toolResult:只给结论。父会话不需要、也不该读到子会话的全部过程。
  return `${name}${finalText}(子会话 ${child.events.length} 条事件)`
  // 真实工程:这里回传的是整串文本,主会话只能拿到一段话,没有结构化字段,
  // 失败/超时也没有单独状态位(ToolPipeline 的 ok:false 只停在工具层,到不了这里)。
  // 生产做法是按 schema 回传结构化结果(如 { status: 'ok'|'error'|'timeout', summary, artifacts }),
  // 主 agent 按字段消费,对失败/超时走独立分支。
}

runSubagent 做的事,一句话:拿子会话建一个子 ReactLoop,跑一个完整 turn,把最终文本打包成字符串 return 出去。这个 return 值会走上一条既有的路——《动手开发你的第一个 agent 02》的 ToolPipeline 会把工具返回值转成 toolResult,写进主会话日志,主模型下一轮读得到。子代理机制没有造任何新管道。

画出来,主会话和子会话的关系是这样:

子会话 · 自己的 SessionLog + SUBAGENT_SYSTEM

主会话 · PARENT_SYSTEM

toolCall: spawn_subagent / fork_subagent

主 ReactLoop

主模型 complete

ToolPipeline

spawn/fork 工具的 execute

子 ReactLoop 跑一个完整 turn

子模型 → read_file 等普通工具

子 loop 跑完 → 最终文本

toolResult 回传主会话
只给结论 · 不给中间过程

虚线框是「上下文隔离」的边界:子会话有自己的 SessionLog、自己的系统提示词,父会话看不到框里面的东西,只收到从框里飘出来的一句结论。

跑一遍。下面输出逐字取自 step5-subagent/PRACTICE.md(本机 Windows 11 / Node v22.12.0 / mock 模型,无 API key):

$ node step5-subagent/index.js
== step5-subagent:子代理机制(spawn / fork)==
主题:让单 agent 会派活 —— 拆任务出去 + 把上下文隔离


[user] 把『读文件 package.json』这个子任务派给 spawn 子代理

┌─ spawn_subagent 开始(spawn·全新会话)

[user] 读文件 package.json
[assistant] 工具 read_file 返回了:{
  "name": "first-agent",
  "version": "0.1.0",
  "private"…
  子会话派生消息(模型看到 4 条):
    [user] 读文件 package.json
    [assistant/toolCall] read_file
    [toolResult:read_file] {
  "name": "first-agent",
  "version": 
    [assistant] 工具 read_file 返回了:{
  "name": "first-agen
└─ spawn_subagent 结束,子会话共 5 条事件
[assistant] 主 agent 总结:子代理 spawn_subagent 完成任务,回传 → 【spawn_subagent】工具 read_file 返回了:{
  "name": "first-agent",
…

[user] 我已经掌握 package.json 了,把『读文件 README.md』交给 fork 子代理(继承我的记忆)

┌─ fork_subagent 开始(fork·继承父前缀记忆)

[user] 读文件 README.md
[assistant] 工具 read_file 返回了:# first-agent —— 动手开发你的第一个 agent(系列配套工程)

系列文章《动手开发你的第一个 age…
  子会话派生消息(模型看到 8 条):
    [user] 把『读文件 package.json』这个子任务派给 spawn 子代理
    [assistant/toolCall] spawn_subagent
    [toolResult:spawn_subagent] 【spawn_subagent】工具 read_file 返回了:{
  "na
    [assistant] 主 agent 总结:子代理 spawn_subagent 完成任务,回传 → 
    [user] 读文件 README.md
    [assistant/toolCall] read_file
    [toolResult:read_file] # first-agent —— 动手开发你的第一个 agent(系列配套工程)
    [assistant] 工具 read_file 返回了:# first-agent —— 动手开发你的
└─ fork_subagent 结束,子会话共 11 条事件
[assistant] 主 agent 总结:子代理 fork_subagent 完成任务,回传 → 【fork_subagent】工具 read_file 返回了:# first-agent —— 动手开发你的第一个 a…

两段演示,一个看 spawn 一个看 fork。第一段:主 agent 把「读 package.json」派给 spawn 子代理,子会话派生消息 4 条——user(子任务)、toolCall(read_file)、toolResult、assistant(总结),全是本次子任务的内容,父会话的历史一条都没进来。

第二段是 fork:主 agent 说「我已经掌握 package.json 了」,把「读 README.md」交给 fork 子代理。子会话派生消息变成 8 条——前 4 条是父会话第一轮的记忆(派 spawn、子代理回传、主总结),后 4 条才是自己的子任务。fork 把切点之前的记忆带来了。

主会话日志什么样?这是最该看的一张——父会话是干净的:

—— 主会话日志(append-only,共 10 条事件)——
  #0 [boundary] turn/start
  #1 [user] 把『读文件 package.json』这个子任务派给 spawn 子代理
  #2 [assistant/toolCall] spawn_subagent({"prompt":"读文件 package.json"})
  #3 [toolResult:spawn_subagent] 【spawn_subagent】工具 read_file 返回了:{
  "name": "first-agent",
…
  #4 [assistant] 主 agent 总结:子代理 spawn_subagent 完成任务,回传 → 【spawn_subagent】工具 read_file 返回了:{
  "name": "first-agent",
…
  #5 [boundary] turn/start
  #6 [user] 我已经掌握 package.json 了,把『读文件 README.md』交给 fork 子代理(继承我的记忆)
  #7 [assistant/toolCall] fork_subagent({"prompt":"读文件 README.md"})
  #8 [toolResult:fork_subagent] 【fork_subagent】工具 read_file 返回了:# first-agent —— 动手开发你的第一个 a…
  #9 [assistant] 主 agent 总结:子代理 fork_subagent 完成任务,回传 → 【fork_subagent】工具 read_file 返回了:# first-agent —— 动手开发你的第一个 a…

10 条事件,两个 toolResult(#3、#8)是子代理的结论摘要,两个主模型总结(#4、#9)。read_file 的原始结果只活在子会话里,父会话一行都没有——这就是「父会话不被污染」在日志层面的直接证据。

配套的 node step5-subagent/test.js 用三个断言锁死这三个行为:spawn 空会话(父 2 条 / 子 0 条)、fork 继承前缀且不污染父会话、子代理产出作为 toolResult 回传主会话(并反向断言主会话里没有 read_file 的 toolResult、没有子会话内部的 user 消息)。本机三个断言全过:

$ node step5-subagent/test.js
== step5-subagent 测试 ==
  ✓ ① spawn 子会话不继承父会话任何消息(父 2 条 / 子 0 条)
  ✓ ② fork 子会话继承前缀记忆(子含父前 2 条),且后续不污染父会话

[user] 把『读文件 package.json』这个子任务派给 spawn 子代理
…
  ✓ ③ 子代理产出作为 toolResult 回传主会话,主会话日志记录了它(无 read_file、无子会话内部 user)

全部断言通过 ✔

最后补一句:演示能跑,靠的是一个「会派活」的 mock 模型。真实模型天生会决定调哪个工具;mock 没有这个能力,所以我在基类 MockLLM 上加了三条确定性规则——用户消息里用『』包住子任务、点名 spawn 或 fork,就输出对应的派活 toolCall;收到 toolResult 就总结。核心就两条规则,其余交给基类。这是 mock 的确定性替代,设计上保持「能回退」:命中不了派活规则就走基类逻辑。

上面那几行「真实工程:…」注释就是五个简化点,我逐个说清。隔离:demo 的 spawn/fork 只是主进程内 new 一个 loop,共享进程堆、无资源与权限边界;真实工程放独立子进程(worker_threads / child_process.fork),或把委派丢给外部 harness 当后端——dsh 的 SubagentProvider 五类实现(spawn/fork/acp/claude-code/codex)能嵌套别的 agent,子会话独立落盘做跨会话持久隔离。持久化:demo 子会话只在内存、主进程退出即失;真实工程走 append-only 落盘(persist/resume),崩溃重放续跑。并发:demo 一次派一个、同步等待;真实工程上任务队列/并发池,多子代理并发扇出(对照 Claude Code 的 subagent 扇出),统一收结果。预算:demo 子代理无上限;真实工程设 maxSteps / token 预算 / 超时回收,超出即终止标记。结果:demo 整串文本回传;真实工程按 schema 回传结构化结果(如 { status, summary, artifacts }),主 agent 按字段分支处理。

4. 两个核心设计:上下文隔离 + 结果回传

把上面的实现抽象出来,子代理机制就两件事:上下文隔离结果回传

4.1 上下文隔离:子代理自己的会话

第一件事,子代理必须有自己的会话。spawn 开一个全新 SessionLog,fork 复制前缀——两种切法,本质相同:子代理活在一条自己的事件流里,主会话活在另一条。

为什么必须隔离?回到《动手开发你的第一个 agent :让它有记忆》那个总论点——会话是数据库,模型每次请求都要能从历史重建。如果子代理和主会话共用一条历史,主会话就会被子代理的中间过程污染:它读文件读了一半、试了个思路又放弃,这些「草稿」全进主会话日志,上下文越滚越大,模型看到的东西越来越杂。

隔离之后,子会话的思考过程不进入父会话日志。上面那张主会话日志表就是证据:10 条事件,干净利落,子代理读文件读到什么,父会话完全不知道。

spawn · 零继承

fork · 按 boundary 切

父会话 SessionLog

完整事件流
boundary · surface · toolResult

子会话 A
全新空日志
派生 4 条 · 全是子任务

子会话 B
复制切点前缀
派生 8 条 · 含父前缀

隔离有一个直接后果,也是 Claude Code 官方反复强调的:子代理对主会话无记忆,prompt 必须自包含。 spawn 的子代理从一张白纸开始,它不知道你俩之前聊过什么。所以任务描述要自带背景——「把『读文件 package.json』这个子任务派给 spawn 子代理」这句话,落到子会话里变成一条干净的「读文件 package.json」。fork 子代理继承了前缀,知道「package.json 已经读过了」,但它对「切点之后」同样一无所知。

这个「无记忆」不是缺陷,是特性。它让子任务真正可独立验证:你给子代理的任务描述,就是它的全部输入;它回传的结论,就是它的全部输出。中间发生了什么,你可以选择不看。

4.2 结果回传:子代理即工具

第二件事,子代理的产出怎么回来。我的答案是:走工具结果的管道。

spawn_subagent 和 fork_subagent 就是两个普通工具,注册进主会话的 ToolRegistry,被《动手开发你的第一个 agent 02》的 ToolPipeline 接管。主模型输出一个 toolCall,流水线调 execute,execute 内部跑一个子 loop,子 loop 的最终文本被 execute return 出来,流水线把它转成 toolResult 写回主会话日志。主模型下一轮 step 读到这个 toolResult,总结一句。整条链路和「读文件」没有任何区别——除了 execute 内部多跑了一个 loop。

这就是核心主张的第二半:subagent 即工具。 子代理没有另起炉灶,它复用你早就建好的工具流水线。这带来两个直接好处。

  • 不需要新机制。 ToolPipeline、toolResult 回写、审批——子代理走的就是主工具的路。你给工具加的每一道闸,子代理天然继承。
  • 可组合。 既然子代理是工具,那子代理的工具也可以注册子代理——递归派活。我的实现里子代理手里只给了 read_file(没给派活工具),但 makeSubagentTools 的注释写明白了:把子工具注册表也传给子会话,它就能继续往下派。子代理即工具,所以它能套娃。

这个「子代理是工具」的判断,跟我《动手开发你的第一个 agent 02》里那句话是一个意思:工具是插件,不是特例。 子代理更是如此——它不是 harness 里的一种特殊角色,它就是一个 execute 内部嵌套了另一个 loop 的工具。

5. 对照 dsh:你的 spawn/fork 是 SubagentProvider 的最小投影

照本系列的惯例,写完对照一遍。你的 spawn/fork,是 dsh SubagentProvider 五类实现里前两个的最小投影。

你写的(step5)dsh 对应位置
SessionLog.spawn() 空会话spawn 实现,inheritsParentContext = falsesubagent-spawn-in-process/src/index.ts:41
SessionLog.fork(seq) 继承前缀fork 实现,inheritsParentContext = truesubagent-fork-in-process/src/index.ts:48
spawn_subagent / fork_subagent 工具「子 agent 从哪来」做成接缝SubagentProvider types.ts:285
runSubagent 内部再跑一个 ReactLoop子 agent 也是独立 loop(ReactLoopAgent)我的 dsh 拆解文详述
子代理产出当 toolResult 回传父会话只收最终答案我的 dsh 拆解文详述

dsh 比我多做了什么,三件事。

第一,接口化。 我的 spawn/fork 是 SessionLog 上的两个方法,dsh 把它们抽象成一个 SubagentProvider 接口(types.ts:285)——「什么是一次委派」被定义成接口,实现随便换。我的是方法,它的是接缝。

第二,二元选择显式化。 我在《gent Harness 架构到底需要些什么》那篇讲过,多 Agent 最关键的一个旋钮是「子 Agent 继承不继承父上下文」。dsh 用一个 inheritsParentContext 布尔值把它显式化——spawn 传 false,fork 传 true。我的实现里这个选择藏在 mainLog.fork()null 的区别里,语义一样,但没起名。起名有价值:它逼你把「从哪来」当成一个明确的维度去思考。

第三,后端可以是别的 harness。 五类实现里,acp 是进程外的 ACP 子 agent,claude-code / codex 更是把一轮委派丢给 Claude Code 或 Codex 的进程——父会话只收最终答案。这意味着 harness 之间不是互斥的,是可以互相嵌套的:你的主 harness 可以把脏活外包给别的 harness。我的最小实现只在一个进程里 spawn/fork,这是「换后端」接缝的最内层。真要接 Claude Code 当子代理,你的 spawn 原语外面包一层进程调用就行——接缝留好了,外面的事是协议的问题,不是架构的问题。

6. 结论:子代理 = 把任务拆出去 + 把上下文隔离

收束。这篇给你的不是一堆新对象,是两个原语和一个判断。

两个原语:spawn 从零开子会话,fork 继承前缀切子会话。加起来不到十行。

一个判断:子代理不是「更多 agent」,是「把任务拆出去 + 把上下文隔离」。拆出去,主会话不被大任务撑爆;隔离,子代理的草稿进不了主会话的真相。子代理即工具,结果回传走的是你《动手开发你的第一个 agent 02》就建好的工具流水线——没造任何新管道。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

luckystar513~

感谢你的认可!希望对您有用~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值