Skip to main content
BCC
s29

Subagent 进阶

Agent 智能

工具限制与深度控制

~300 行代码11 个工具filterToolsForAgent + 递归深度 + AsyncLocalStorage + 生命周期清理
子 Agent 的能力必须小于父 Agent——权力越大,风险越大

A subagent should do less, not more

A subagent should do less, not more.

[ Phase 7: Agent 智能 ] · 工具数: 11 · 代码量: ~350 行


前置知识

  • 需要完成: s28 [Subagent 基础]

你将学到

  • 工具过滤策略:DISALLOWED_TOOLS 和 readOnly 只读模式
  • 递归深度控制:MAX_DEPTH 与 canCreateSubagent 检查
  • 生命周期清理:finally 块中的 todo/shell/MCP 资源回收
  • 最小权限原则在 Agent 系统中的体现

问题场景

s28 的子 Agent 有完整的工具集——包括 agent 工具本身。这意味着:

无限递归风险:

  主 Agent → agent("做任务 A")
    └── 子 Agent A → agent("做子任务 B")
        └── 子 Agent B → agent("做子任务 C")
            └── 子 Agent C → agent("做子任务 D")
                └── ... 无限创建下去
                    → 资源耗尽 + API 成本失控

A subagent should do less, not more. 子 Agent 的能力必须小于父 Agent。

设计决策

工具过滤策略

工具过滤层级:

  父 Agent 工具集:
  ┌──────────────────────────────────────┐
  │ glob  grep  ls  file_read  bash     │
  │ file_write  file_edit  todo_write   │
  │ help  agent                          │
  └──────────────┬───────────────────────┘
                 │ filterToolsForAgent()
                 ↓
  子 Agent 工具集 (标准):
  ┌──────────────────────────────────────┐
  │ glob  grep  ls  file_read  bash     │
  │ file_write  file_edit  todo_write   │
  │ ✗ help  ✗ agent ← 已移除            │
  └──────────────────────────────────────┘

  子 Agent 工具集 (只读):
  ┌──────────────────────────────────────┐
  │ glob  grep  ls  file_read           │
  │ todo_write                           │
  │ ✗ 所有写操作工具 ← 已移除            │
  └──────────────────────────────────────┘

递归深度控制

深度限制(MAX_DEPTH = 3):

  depth=0: 主 Agent        ← 全部工具
  depth=1: 子 Agent A      ← 过滤后工具(无 agent)
  depth=2: 子 Agent B      ← 理论上可以(如果 A 有 agent)
  depth=3: ✗ 拒绝创建      ← canCreateSubagent() 返回 false

  教学版简化: 子 Agent 没有 agent 工具,
  所以实际上 depth 最多到 1。
  生产版通过 queryTracking.depth 追踪。

生命周期清理

子 Agent 结束时的清理工作(finally 块):

  try {
    // 子 Agent 运行 Agent Loop
  } finally {
    cleanupSubagent(agentId):
      ├── clearTodos(agentId)     ← 清理 todo 列表
      ├── killShellTasks(...)     ← 杀死遗留 shell 进程
      ├── killMcpTasks(...)       ← 关闭 MCP 连接
      └── readFileState.clear()   ← 清理文件状态
  }

实现

工具过滤

const DISALLOWED_TOOLS = new Set([
  "agent",  // 防止递归
  "help",   // 子 Agent 不需要
]);

export function filterToolsForAgent(
  tools: Tool[],
  options: { readOnly?: boolean } = {},
): Tool[] {
  return tools.filter(tool => {
    if (DISALLOWED_TOOLS.has(tool.name)) return false;
    if (options.readOnly) return READONLY_TOOLS.has(tool.name);
    return true;
  });
}

深度检查

const MAX_DEPTH = 3;

export function canCreateSubagent(currentDepth: number) {
  if (currentDepth >= MAX_DEPTH) {
    return { allowed: false, reason: "已达最大递归深度" };
  }
  return { allowed: true };
}

AgentTool 集成

async call(input, context) {
  // 1. 深度检查
  const depthCheck = canCreateSubagent(currentDepth);
  if (!depthCheck.allowed) return { output: depthCheck.reason, isError: true };

  // 2. 工具过滤
  const filteredTools = filterToolsForAgent(allTools, { readOnly });

  // 3. 运行子 Agent
  try {
    while (turns < MAX_TURNS) { ... }
  } finally {
    // 4. 生命周期清理(即使出错也执行)
    cleanupSubagent(subContext.agentId);
  }
}

运行验证

cd agents/s29-subagent-advanced
npm run dev

> 用只读子 Agent 分析 src/ 目录结构
# 子 Agent 只有读操作工具,不能修改文件

对照 Claude Code

维度教学版 (s29)Claude Code
禁止工具agent + helpALL_AGENT_DISALLOWED_TOOLS:TaskOutput、plan 工具、AskUserQuestion 等
异步白名单ASYNC_AGENT_ALLOWED_TOOLS:读/搜索/编辑/shell/TodoWrite 等
深度追踪简单计数queryTracking.depth(AsyncLocalStorage 跨调用链传递)
递归防护canCreateSubagent() 检查深度fork 路径:检查 querySource + isInForkChild()
生命周期clearTodos()MCP 清理 + shell kill + todo 清理 + readFileState
工具解析简单过滤resolveAgentTools() — 支持通配符、per-agent disallowedTools、allowedAgentTypes

深入思考

  1. 最小权限原则:子 Agent 应该只拥有完成任务所需的最少工具。readOnly 模式让分析类子任务更安全。
  2. 递归是危险的:没有深度限制的 Agent 递归可能导致 API 成本指数增长。每层子 Agent 都有自己的对话历史和 API 调用。
  3. finally 的重要性:子 Agent 可能因为 abort、错误或超时而提前退出。finally 确保资源总是被清理,防止内存泄漏和进程残留。

练习

  1. DISALLOWED_TOOLS 中添加 bash,创建一个"安全沙箱"模式的子 Agent
  2. 实现 AsyncLocalStorage 版的深度追踪,让深度信息自动在调用链中传递
  3. 添加子 Agent 的执行时间限制:超过 30 秒自动中止

下一课预告

子 Agent 的安全和控制已经到位,但 Agent 的知识还在 system prompt 里硬编码。下一课 s30 Skill 系统 将实现按需加载知识——SKILL.md 文件、延迟工具(deferred tools)和 tool_search,让 Agent 在需要时才加载专业知识。