Subagent 生命周期
对应源码
| 主题 | 源码 |
|---|---|
| 状态、创建、取消和结果注入 | nanobot/agent/subagent.py::SubagentStatus、SubagentManager |
| Subagent hook | nanobot/agent/subagent.py::_SubagentHook |
| 系统提示和完成通知模板 | nanobot/templates/agent/subagent_system.md、subagent_announce.md |
| Subagent Tools 注册 | nanobot/agent/tools/loader.py、nanobot/agent/tools/spawn.py |
学习目标
读完后,你应当能够:
- 说明 Subagent 使用哪些共享组件、隔离哪些状态;
- 解释 Subagent 结果如何回到主 Session;
- 说明并发限制和取消;
- 避免把当前实现描述为通用多 Agent 编排平台。
前置知识
- 理解 AgentRunner;
- 理解 MessageBus 和 pending queue;
- 理解 Tool scope。
1. SubagentManager 的职责
位置:
nanobot/agent/subagent.py::SubagentManager
它管理:
- 后台 Task;
- task ID 和状态;
- 按 Session 关联的任务集合;
- Subagent ToolRegistry;
- 独立 AgentRunner 执行;
- 结果公告;
- Session 级取消。
2. 共享与隔离
| 项目 | 行为 |
|---|---|
| Provider | 默认共享主 Agent 当前 Provider/model |
| AgentRunner 实现 | 复用同一个 AgentRunner 类 |
| ToolRegistry | 为 Subagent 单独创建 |
| Tool scope | scope="subagent" |
| workspace | 默认共享,也可使用 workspace scope |
| Session history | 不直接加载主 Session history |
| system prompt | 使用 Subagent 专用模板和 Skill 摘要 |
| 结果 | 作为新 InboundMessage 回到主 Agent |
Subagent 输入主要是:
subagent system prompt
+ task description它不是复制主 Agent 的完整当前上下文。
3. 创建流程
spawn():
- 生成短 task ID;
- 创建
SubagentStatus; - 保存 origin channel/chat/session;
asyncio.create_task(_run_subagent(...));- 记录 running task;
- 记录 Session 到 task ID 的映射;
- 注册 done callback 清理内存状态;
- 立即向主 Agent 返回“已启动”文本。
flowchart TD MAIN["主 Agent 调用 spawn"] --> ID["创建 task_id/status"] ID --> TASK["后台 asyncio Task"] TASK --> TOOLS["独立 subagent ToolRegistry"] TOOLS --> RUN["AgentRunner.run"] RUN --> ANN["announce result"] ANN --> BUS["MessageBus.publish_inbound"] BUS --> MAIN2["主 Agent 同一 Session"]
4. Subagent ToolRegistry
_build_tools():
- 创建新的 Registry;
- 创建新的 ToolContext;
- 继承必要的 exec/web/file 配置;
- 应用 workspace restriction;
- 使用
ToolLoader.load(..., scope="subagent")。
不是所有主 Agent Tool 都会自动暴露给 Subagent。
这避免:
- MessageTool 等产品层能力被后台任务滥用;
- Subagent 自己继续无限 spawn;
- 不适合后台执行的 Tool 被误用。
具体 scope 以 ToolLoader 和每个 Tool 配置为准。
5. Runner 配置差异
Subagent AgentRunSpec 具有一些更严格选择:
fail_on_tool_error=Truefinalize_on_max_iterations=False- 自定义 max-iteration message;
error_message=None- checkpoint 只更新 SubagentStatus;
- 使用 origin Session 对应的 LLM timeout 策略;
- 使用独立 hooks 记录阶段和 iteration。
所以“使用同一个 AgentRunner”不等于行为完全相同。
6. 结果如何回主 Agent
_announce_result():
- 使用
subagent_announce.md渲染结果; - 创建
InboundMessage; channel="system";sender_id="subagent";- metadata 标记
injected_event=subagent_result; - 使用
session_key_override对齐主 Agent effective Session; - 发布到 MessageBus inbound。
如果主 Session 仍在运行:
- 结果进入该 Session 的 pending queue;
- 作为 mid-turn injection 被 Runner 接收。
如果主 Session 已空闲:
- 结果作为新的系统入站 Turn 处理。
7. 为什么不直接发 OutboundMessage
Subagent 结果先回主 Agent,而不是直接展示给用户,主 Agent因此可以:
- 读取结果;
- 与原任务合并;
- 继续调用工具;
- 解释部分失败;
- 决定最终用户回复。
直接发 outbound 会绕过主 Agent 的整合和 Session 语义。
8. 并发限制
配置:
agents.defaults.maxConcurrentSubagents- 当前默认值为 1。
达到限制时 spawn Tool 返回错误,由主 Agent决定等待或调整任务。
该限制保护:
- 本地模型 KV cache;
- Provider 并发额度;
- workspace 中并发副作用;
- 用户对后台任务数量的可控性。
9. 取消和状态
SubagentManager 维护:
_running_tasks_task_statuses_session_tasks
支持:
- 按 task ID 观察;
- 按 Session 取消所有 Subagent;
- done callback 清理状态。
这些状态主要在进程内,不是持久化任务队列。进程重启后不能假设后台 Subagent 自动恢复。
10. 当前能力边界
当前实现更准确的描述是:
主 Agent 可以启动有限数量的后台工具型 AgentRunner,并把结果重新注入原 Session。
它不是:
- 持久化多 Agent 工作流引擎;
- 带 DAG 的调度器;
- 多 Agent 共享黑板;
- 自动共识协议;
- 跨进程任务系统。
观察任务
- 启动一个只读 Subagent;
- 记录返回的 task ID;
- 在
_build_tools()查看工具集合; - 比较主 Agent 和 Subagent system prompt;
- 查看结果 InboundMessage;
- 观察主 Turn 活跃和空闲时的两种回传;
- 将并发限制保持为 1,先理解生命周期,不做压力测试。
源码与测试
nanobot/agent/subagent.pynanobot/templates/agent/subagent_system.mdnanobot/templates/agent/subagent_announce.mdnanobot/agent/tools/loader.pytests/agent/test_subagent.pytests/agent/test_subagent_lifecycle.pytests/agent/tools/test_subagent_tools.py
自检
- Subagent 为什么不直接读取主 Session history?
- 为什么要创建独立 ToolRegistry?
- 结果为什么作为 InboundMessage 而不是 OutboundMessage?
- 默认并发限制为什么只有 1?
- 哪些事实说明它不是持久化多 Agent 编排平台?
下一步
进入“运行表面”,学习 Channel、Gateway、API、SDK 和 WebUI 如何围绕同一个核心 runtime 工作。