Subagent

子代理给每个子任务一份干净的消息历史。

subagent 工具 = 新循环 + 新消息数组。

70 行1 个工具

问题

主 Agent 处理一个大型重构,上下文里已经堆了 50 轮对话。现在它想"先让另一个实例独立调研一下这个 API 的用法"。

如果把调研过程塞进主对话:20 轮调研消息会污染主上下文,且主 Agent 的 todo、工具、系统提示词全部对调研可见——子任务不需要也不应该看到父任务的完整历史。每个子任务需要一份干净的上下文:自己的消息数组、自己的系统提示词、自己的工具集。

解决方案

一个 subagent 工具:传入子任务的 prompt,harness 启动一个新的 agent 循环(s01 的 agentLoop 本身就是可复用的!),配一份空消息历史,跑完后只把最终结果返回给父循环。

define("subagent", {
  type: "object",
  properties: { prompt: { type: "string" }, system: { type: "string" } },
  required: ["prompt"],
}, async ({ prompt, system }) => {
  // 子代理 = 同一个循环 + 全新的 messages 数组
  const result = await agentLoop(prompt, { system, tools: CHILD_TOOLS, maxSteps: 20 });
  return `[subagent 结果] ${result}`; // 只回传最终输出
});

工作原理

第 1 步:让 agentLoop 接受配置,成为可复用的引擎(不再是单一入口)。

export async function agentLoop(query, opts = {}) {
  const messages = [{ role: "user", content: query }];
  const system = opts.system ?? DEFAULT_SYSTEM;
  const tools = opts.tools ?? DEFAULT_TOOLS;
  let steps = 0;
  while (true) {
    if (++steps > (opts.maxSteps ?? 50)) return "(达到最大步数)";
    // ... 与 s01 相同的请求/执行逻辑,用 system/tools 变量
  }
}

第 2 步:子代理的工具集通常更小——调研子代理只需要 read_file + web_search,不该有 bash工具隔离是上下文隔离的一部分

const CHILD_TOOLS = [read_file_def, web_search_def]; // 不含 bash

第 3 步:父循环等待子代理完成。此时父循环是阻塞的——s13 会引入后台子代理,让父循环继续推理。

子代理的价值公式:隔离的上下文 + 受限的工具 + 简洁的接口(prompt 进、文本出)。父代理不必知道子代理内部发生了什么,只需消费它的结论。

试一下

运行 s06,试试:

  • "调研这个目录的测试结构,然后告诉我应该在哪里加新测试"(子代理只读文件)
  • "同时调研 A 和 B 两个问题"(观察父循环串行等待两个子代理)

观察重点:子代理结果进入父上下文后,父代理能否直接使用?子代理内部出错(比如找不到文件)时,返回给父代理的是什么——错误文本还是异常?

以下内容基于 packages/subagent 的核查。DSH 的子代理是一个完整的能力族

角色
subagent 提供者注册、委托、延续的契约(ctx.subagents
tool-subagent 模型可见的委托工具
tool-subagent-control 子代理消息与列表控制工具
tool-subagent-report 子代理 → 父代理的报告通道
提供者们 subagent-spawn-in-process(全新进程内子代)、subagent-fork-in-process(继承父历史)、subagent-acp(ACP 进程外)、subagent-codex / subagent-claude-code / subagent-dsh-sdk(真实外部 Agent)

s17 会专门讲"一个接口、多个提供者"。这里先记住三个与最小实现的差异:

  1. 延续(continuable):DSH 的子代理可以后台运行并延续对话——父代理用 send_message 给已完成的子代理追加任务,子代理的会话保留。你的实现只能"跑完即弃"。
  2. 结果契约:委托返回结构化的运行结果(id、状态、输出),报告走专用通道,而不是把子代理内部的消息流灌回父上下文。
  3. 提供者可互换:同一个 subagent 工具,后端可以是进程内、ACP、Codex、Claude Code——模型侧契约不变。

一句话:你的"新循环 + 新消息数组"就是 DSH 子代理的最小核。生产版本把"跑完即弃"升级为"可延续、可控制、多后端"。