71. 子代理隔离系统 (Sub-Agent Isolation)
v5.0.1.7+ — SubAgentContext 上下文隔离与协作管理
概述
子代理隔离系统为多代理协作场景提供了上下文级别的隔离。通过 SubAgentContext 隔离原语,每个子代理拥有独立的消息历史、上下文引擎和工具白名单,仅将摘要结果返回给父代理,避免中间推理步骤和工具输出污染父代理上下文。
设计动机
问题分析
- 共享可变 messages 数组:agentLoop 将所有工具调用和结果追加到同一
messages[] - 全局上下文注入:CLIContextEngineering 的 BM25/记忆/偏好注入不区分任务范围
- 单例内存泄漏:hierarchical-memory 的
_working/_shortTerm是模块级全局 Map - Cowork 交叉污染:Debate moderator 接收所有 reviewer 的完整输出
- Skill 无隔离:skill handler 在主代理同一上下文运行
设计目标
- 子代理拥有独立消息历史,不与父代理共享
- 子代理仅返回摘要结果(三级策略)
- 记忆系统按命名空间隔离
- 上下文注入按角色/任务范围过滤
- 工具使用按角色白名单限制
- 向后完全兼容,不使用隔离功能时行为不变
架构设计
模块依赖
agent-core.js
├── sub-agent-context.js (隔离运行)
│ ├── cli-context-engineering.js (scope参数)
│ └── hierarchical-memory.js (namespace参数)
├── sub-agent-registry.js (生命周期追踪)
└── agent-coordinator.js (任务分解+执行)SubAgentContext 类设计
+-----------------------------------+
| SubAgentContext |
+-----------------------------------+
| + id: string |
| + parentId: string|null |
| + role: string |
| + task: string |
| + messages: Message[] | ← 隔离
| + contextEngine: CLIContextEng | ← 独立实例
| + allowedTools: string[]|null |
| + maxIterations: number |
| + status: "active"|"completed"|"failed" |
| + result: Result|null |
+-----------------------------------+
| + create(options): SubAgentContext |
| + run(prompt, opts): Promise<Result> |
| + summarize(content): string |
| + forceComplete(reason): void |
| + toJSON(): object |
| - _getFilteredTools(): Tool[] |
+-----------------------------------+命名空间化记忆
之前 (全局共享):
_working: Map<id, entry>
_shortTerm: Map<id, entry>
之后 (命名空间隔离):
_workingNS: Map<namespace, Map<id, entry>>
_shortTermNS: Map<namespace, Map<id, entry>>
向后兼容代理:
_working.size → _workingNS.get("global").size
_working.get(id) → _workingNS.get("global").get(id)
_working._nsMap → 直接访问命名空间 Map三级摘要策略
┌─ ≤500 chars ──→ 直接返回
│
输入内容 ──→ 长度检查 ┤
│
└─ >500 chars ──→ 检查结构化标题
│
├─ 有 ## Summary/Result → 提取 section (≤1000)
│
└─ 无 → 截断前500字符 + "[truncated, N chars]"角色工具白名单 (ROLE_TOOL_WHITELIST)
code-review: [read_file, search_files, list_dir] # 只读
code-generation: [read_file, write_file, edit_file, run_shell, search_files, list_dir]
data-analysis: [read_file, search_files, list_dir, run_code, run_shell]
document: [read_file, write_file, search_files, list_dir] # 无执行
testing: [read_file, write_file, edit_file, run_shell, search_files, list_dir, run_code]
general: null (所有工具)生命周期管理
SubAgentRegistry
+----------------------------------+
| SubAgentRegistry (Singleton) |
+----------------------------------+
| - _active: Map<id, SubAgentCtx> |
| - _completed: RingBuffer(100) |
| - _totalTokens: number |
| - _completedCount: number |
+----------------------------------+
| + getInstance(): SubAgentRegistry |
| + register(subCtx): void |
| + complete(id, result): void |
| + forceCompleteAll(sessionId) |
| + cleanup(maxAgeMs): void |
| + getActive(): object[] |
| + getHistory(): object[] |
| + getStats(): Stats |
+----------------------------------+集成点
| 集成模块 | 行为 |
|---|---|
| ws-session-manager | closeSession() 时 forceCompleteAll(sessionId) |
| agent-repl | /sub-agents 命令展示活跃/历史/统计 |
| agent-coordinator | executeDecomposedTask() 每个子任务一个 SubAgentContext |
| debate-review-cli | reviewer 输出截断到 300 字符传给 moderator |
数据流
spawn_sub_agent 执行流程
1. LLM 返回 tool_call: spawn_sub_agent({ role, task, context })
2. executeTool → _executeSpawnSubAgent()
3. ├── SubAgentContext.create({ role, task, inheritedContext })
4. ├── SubAgentRegistry.register(subCtx)
5. ├── subCtx.run(task, llmOptions)
6. │ ├── 独立 agentLoop(subCtx.messages, ...)
7. │ ├── 工具白名单过滤
8. │ └── 返回 { summary, artifacts, tokenCount, toolsUsed }
9. ├── SubAgentRegistry.complete(id, result)
10. └── 返回 summary 给父 messages (不含子代理中间步骤)文件清单
新增文件
| 文件 | 行数 | 说明 |
|---|---|---|
packages/cli/src/lib/sub-agent-context.js | ~290 | 子代理上下文核心 |
packages/cli/src/lib/sub-agent-registry.js | ~190 | 生命周期注册表 |
desktop-app-vue/src/main/ai-engine/agents/sub-agent-context.js | ~200 | Desktop CJS 等价实现 |
修改文件
| 文件 | 修改说明 |
|---|---|
packages/cli/src/lib/agent-core.js | 添加 spawn_sub_agent 工具定义 + executeTool case |
packages/cli/src/lib/cli-context-engineering.js | 添加 scope 参数支持 |
packages/cli/src/lib/hierarchical-memory.js | 命名空间化 + 向后兼容代理 |
packages/cli/src/lib/agent-coordinator.js | 添加 ROLE_TOOL_WHITELIST + executeDecomposedTask |
packages/cli/src/lib/cowork/debate-review-cli.js | reviewer 输出截断 |
packages/cli/src/lib/ws-session-manager.js | 会话清理集成 |
packages/cli/src/repl/agent-repl.js | /sub-agents 命令 |
测试策略
单元测试
| 测试文件 | 测试数 | 覆盖范围 |
|---|---|---|
| sub-agent-context.test.js | 38 | 创建、隔离、白名单、摘要、forceComplete、token预算 |
| sub-agent-registry.test.js | 12 | 单例、注册/完成、环形缓冲、清理 |
| scoped-context-engineering.test.js | 8 | scope 构造、命名空间传递、阈值过滤 |
| namespaced-memory.test.js | 9 | 命名空间存取、隔离、统计 |
集成测试
| 测试文件 | 测试数 | 覆盖范围 |
|---|---|---|
| sub-agent-isolation.test.js | 33 | spawn_sub_agent 工具、注册表生命周期、命名空间隔离、自动凝缩、token预算 |
E2E 测试
| 测试文件 | 测试数 | 覆盖范围 |
|---|---|---|
| sub-agent-isolation.test.js | 8 | 模块导入、工具定义、API 可用性 |
v5.0.1.8 增强功能
1. 系统提示词子代理引导
getBaseSystemPrompt() 现在包含 "Sub-Agent Isolation" 章节,指导 LLM 何时使用 spawn_sub_agent 工具:
- 代码审查与代码生成分离
- 大文件摘要后再纳入回复
- 独立的安全/性能分析
- 提醒不要对简单任务创建子代理
2. 自动上下文凝缩 (Auto-Condensation)
当 spawn_sub_agent 未提供 context 参数时,自动从父代理最近 3 条 assistant 消息中提取前 200 字符作为继承上下文。显式提供 context 时优先使用显式值。
3. Token 预算执行
SubAgentContext.run() 现在跟踪 token 消耗(基于字符长度估算,~4 字符/token),并在达到 tokenBudget 时自动 forceComplete("token-budget-exceeded")。适用于 CLI 和 Desktop 版本。
4. 并行子代理执行
executeDecomposedTask() 从串行改为批次并行执行,支持 maxConcurrency 选项(默认 3)。独立子任务在同一批次内并行运行。
5. Desktop 集成完善
agent-pool.js:acquireIsolatedAgent()支持inheritedContext和tokenBudget参数autonomous-agent-runner.js:delegateToSubAgent()新增子代理追踪(goal.subAgents 数组),支持tokenBudget参数
向后兼容性
agentLoop签名不变,不使用spawn_sub_agent时行为完全不变_working/_shortTerm通过代理对象保持原有 Map APIstoreMemory/recallMemory不传 namespace 时默认使用 "global"CLIContextEngineering不传 scope 时行为不变executeDecomposedTask()在单个子任务时行为与串行一致executeTool()context 参数新增可选parentMessages字段,不传时无影响- AGENT_TOOLS 数量从 9 增加到 10(添加 spawn_sub_agent)
