系列回顾:主循环 · 代码库工具 · REPL · 项目上下文 · Skills · 权限 + Write · MCP 概念 · MCP 实现 · Context Budget · Bash · compact 2.0 · autocompact · Hooks · Memory

主会话一路 Read / Grep / Bash,细节全堆进 messages[]——下一轮还要带着这些噪音继续聊。
人会说:「你去旁边查一下,回来只告诉我结论。」
这篇讲 v6-subagents:内置 Agent 工具,同步嵌套一次 query(),子任务用独立消息列表跑完,只把摘要塞回父会话的 tool_result——父循环骨架仍是原来的 ReAct。


为什么需要「旁边那间屋」?

主会话适合:定目标、做决策、改代码。
不适合:一次性扫半个仓库、跑一长串探索工具,却把每一步轨迹永久留在父历史里。

主会话自己干 Agent 子代理
消息历史 写进 QueryEngine.messages 独立数组,不写回父
回给主模型的 整段 tool 轨迹 末条 assistant 文本摘要
上下文污染
费用/延迟 少一次嵌套 多一次完整子循环

所以子代理不是「另一个产品」,而是:同一套 query(),换一间干净房间,干完只交报告。


工具长什么样?

Agent({
  description: 'Find auth entrypoints', // 必填:3–5 词短描述(TRACE / 状态)
  prompt: '在仓库里找出登录相关入口文件,只列路径与一句理由。', // 必填:子任务全文
  tool_names: ['Glob', 'Grep', 'Read'], // 可选:子工具白名单
})
字段 必填 作用
description 给人看的短标签;当子推理正文
prompt 子会话的第一条 user 消息
tool_names 白名单;缺省 = 父工具表去掉 Agent 后的全部

父侧 tool_result 成功时大致是:

[Agent: Find auth entrypoints]
src/auth/login.ts — 登录表单入口
…

中间子工具的 tool_use / tool_result 不会进入父 messages


一张图:嵌套怎么跑

父 query(depth=0)
  → 模型 tool_use: Agent
  → AgentTool.call
       → createSubagentContext(depth=1,新 AbortController 链到父)
       → toolsForSubagent(去掉 Agent,可选白名单)
       → 嵌套 query({ messages: [prompt], depth: 1, … })
       → 子自己 ReAct:callModel → 工具 → …
       → 取最后一条 assistant 的 text(≤32KB,超长留尾)
  → 父 tool_result = [Agent: description] + 摘要
  → 父继续下一轮

对应实现:src/tools/AgentTool.ts + src/utils/subagent.ts


防递归:两道闸

子代理若还能再调 Agent,就会无限套娃。mini 用双保险:

  1. 子工具池排除 AgenttoolsForSubagent
  2. depth 上限默认 1:父为 0 时可 spawn depth=1;已在 depth≥1 再调 → 直接错误 tool_result,不启嵌套
depth 0(主会话)──Agent──▶ depth 1(子)
                              └── 再 Agent?→ 拒绝 / 池子里根本没有

depth 与 Stop hooks 共用同一语义:只有 depth === 0 跑 Stop;子代理收尾跑 Stop,也不做单独的 SubagentStop


权限、中止、失败

主题 行为
权限 默认复用父 canUseTool;父 deny 对子生效;写操作仍可能弹 REPL y/N
Abort 子有独立 AbortController,但链到父:父中止 → 子中止
失败 异常 / aborted / 无文本 → 父侧 is_errortool_result(fail-soft,不炸主循环)
Compact 子用同一套 deps;子历史独立,不写回父 Engine

Agent 标成非只读、非并发安全:派生子任务有副作用与串行成本,不跟只读工具抢并发。


和 Skills / Memory / Hooks 怎么区分?

机制 一句话
Skills 往主会话注入说明书(仍在同一 messages)
Memory system 里跨会话偏好
Hooks 工具前后拦 / 记;Stop 只在顶层
Agent 另开一轮嵌套 query,只交摘要

Skills 是「给当前大脑多读一页纸」;Agent 是「派一个实习生去查,回来交纪要」。


30 秒感受一下

bun run dev
# 或 mock:需能走到真实工具时再试 Agent

自然语言示例:

用 Agent 工具:在仓库里找与 MCP 配置相关的文件,只返回路径列表。
description 用 "Locate MCP config"。

TRACE=1 时 stderr:

[trace] agent.start description=Locate MCP config depth=1
[trace] agent.end   description=Locate MCP config ok=true reason=completed

和主循环的关系

L1 主 query / QueryEngine  → 照旧;Agent 只是多一个 Tool
L2 AgentTool.call          → 嵌套 query(独立 messages)
L3 createSubagentContext   → depth+1、abort 链、工具池裁剪
L4 摘要回传                → 父 tool_result;子轨迹不进父历史

子代理复用 query(),不另写一套循环;隔离的是消息列表与深度,不是换引擎。


刻意没做什么?

没做 意味着什么
swarm / 多 worker / Coordinator 一次一个同步子任务
worktree / 后台 fork 子跑完才回父;无并行隔离目录
subagent_type 命名 agent 目录 不按角色配置多套 agent
SubagentStop 子收尾不跑 Stop;顶层 Stop 另算
完整 resume UI / 换模型字段 入参只有 description / prompt / tool_names

这一刀验证的是「隔离调研」的最小面:

Agent 工具 → 嵌套 query → depth≤1 + 排除 Agent
  → 权限派生 → 摘要回父 → TRACE 可观测

系列拼图

能力
主循环 单会话 ReAct
compact / Memory 主会话怎么瘦、怎么记住
Hooks 工具生命周期
本篇 旁路子任务,结论回传

Harness 再多一根柱子:循环、工具、会话、上下文、技能、权限、MCP、预算、hooks、memory、subagent。


你可以从这里带走什么?

  1. 子代理 = 嵌套 query() + 独立 messages——不是新框架。
  2. 父只要摘要——中间工具轨迹留在子会话里。
  3. 防递归靠池子 + depth——去掉 Agent,且默认 maxDepth=1。
  4. 权限与中止要继承——派生 canUseTool,abort 链到父。
  5. depth 是全局语义——顶层才 Stop;子不跑 Stop。
  6. 失败 fail-soft——错误进父 tool_result,主循环继续。

仓库与相关文档

欢迎 Star、Issue 和 PR。


本文基于 react-agent-mini 变更 v6-subagents(同步 Agent 工具 + depth/防递归 + 摘要回传)撰写。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐