跳转至

Deep Dive | tools/AgentTool/ 6072 行多 Agent 入口拆解

重要性:⭐⭐⭐⭐⭐(多 Agent 协作的入口 —— Claude Code "能开子 agent" 的关键) 真实位置src/tools/AgentTool/6072 行 / 15 文件) 角色:让 LLM 能启动子 agent(explore / plan / 自定义)跑独立任务 关联phase-05-tools.md § 5.9phase-06-agent-loop.md § 6.7topics/deep-dive-query-engine.md


1. 15 个文件全景

src/tools/AgentTool/ (6072 行)
├── AgentTool.tsx           1397 行  ⭐ 主实现(Tool + call())
├── UI.tsx                  871 行  ⭐ 渲染组件
├── runAgent.ts             973 行  ⭐ 实际跑子 agent 的逻辑
├── loadAgentsDir.ts        755 行  ⭐ 加载所有可用 agents(内置 + 用户)
├── prompt.ts               287 行  ⭐ 喂给 LLM 的工具描述
├── resumeAgent.ts          265 行  ⭐ 恢复已停止的 agent
├── forkSubagent.ts         210 行  ⭐ fork 子 agent
├── agentToolUtils.ts       686 行  工具函数
├── agentMemorySnapshot.ts  197 行  内存快照
├── agentMemory.ts          177 行  agent 记忆
├── agentDisplay.ts         104 行  显示
├── builtInAgents.ts         72 行  内置 agent 列表
├── agentColorManager.ts      66 行  颜色
├── constants.ts             12 行  常量
└── built-in/             (推测)内置 agent 的具体实现

结构与 FileEditTool 类似(7 文件结构),但因为 agent 比 edit 复杂得多,总行数是 6 倍


2. AgentTool.tsx 1397 行主实现

2.1 推测的导出

// AgentTool.tsx 推测导出
export const AgentTool: ToolDef<typeof inputSchema>

// 推测:
export type AgentInput = {
  prompt: string
  subagent_type: 'explore' | 'plan' | 'general-purpose' | string
  // ... 20+ 字段
}

export type AgentResult = {
  content: string
  agentId: string
  is_error: boolean
}

2.2 推测的 7 字段结构

export const AgentTool = buildTool({
  name: 'Agent',
  description: 'Launch a new agent to handle complex tasks...',
  inputSchema: z.object({
    prompt: z.string(),
    subagent_type: z.string(),
    // ... 推测 20+ 字段
  }),
  isReadOnly: false,
  isConcurrencySafe: false,

  renderToolUseMessage: AgentUI,
  validateInput: validateAgentInput,

  // 推测:用 async *call 模式
  async *call(input, context) {
    yield* runAgent(input, context)
  },
})

2.3 关键设计

Agent 是 7 文件工具的"集大成者": - 实现 + UI + prompt + 校验 + 工具函数 + 状态 + 记忆 - 比 FileEditTool 复杂,因为agent 自己有完整生命周期


3. runAgent.ts 973 行子 agent 执行

3.1 函数签名(推测)

export async function* runAgent(
  input: AgentInput,
  context: ToolUseContext,
): AsyncGenerator<AgentEvent, AgentResult, unknown>

异步生成器 —— 持续 yield 子 agent 进度,return 最终结果。

3.2 推测的内部流程

async function* runAgent(input, context) {
  // 1. 验证输入
  const validation = validateAgentInput(input)
  if (!validation.ok) {
    return { content: validation.error, is_error: true }
  }

  // 2. 加载 agent 定义
  const agentDef = await loadAgentDefinition(input.subagent_type, context)
  if (!agentDef) {
    return { content: `Unknown agent type: ${input.subagent_type}`, is_error: true }
  }

  // 3. 创建子任务
  const task = createAgentTask({
    type: 'local_agent',  // 或 'in_process_teammate'
    parentSessionId: getSessionId(),
    prompt: input.prompt,
    agentDef,
  })

  // 4. yield 启动
  yield { type: 'agent_started', taskId: task.id, agentType: input.subagent_type }

  // 5. 启动子 agent
  yield* task.run()

  // 6. 等待完成
  for await (const event of task.events()) {
    yield* convertAgentEventToAgentEvent(event)
  }

  // 7. 返回结果
  const finalState = await task.waitForCompletion()
  return {
    content: finalState.result,
    agentId: task.id,
    is_error: finalState.status === 'failed',
  }
}

7 步执行 —— 标准 agent 生命周期。

3.3 关键设计

  1. 子 agent 自己的 QueryEngine —— 子 agent 独立于父 session
  2. task 系统集成 —— 通过 tasks/LocalAgentTask/InProcessTeammateTask/
  3. 异步生成器 —— 父 agent 能看到子 agent 的实时进度

4. loadAgentsDir.ts 755 行 agent 加载

4.1 加载哪些 agent

// loadAgentsDir.ts 推测
const AGENT_DIRS = [
  '~/.claude/agents/',          // 用户级
  '.claude/agents/',            // 项目级
  'src/tools/AgentTool/built-in/'  // 内置
]

export async function loadAgentsDir(cwd: string): Promise<AgentDefinition[]> {
  const agents: AgentDefinition[] = []

  // 1. 加载内置
  agents.push(...builtInAgents)

  // 2. 加载用户级
  for (const file of await glob('~/.claude/agents/*.md')) {
    agents.push(parseAgentFile(file))
  }

  // 3. 加载项目级
  for (const file of await glob(`${cwd}/.claude/agents/*.md')) {
    agents.push(parseAgentFile(file))
  }

  return agents
}

3 个来源 —— 内置 + 用户 + 项目。

4.2 agent 文件格式(推测)

<!-- ~/.claude/agents/code-reviewer.md -->
---
name: code-reviewer
description: Reviews code for bugs and style issues
model: sonnet
tools: [Read, Grep, Glob]
---

You are an expert code reviewer. When given a file path:
1. Read the file
2. Look for bugs
3. Check style
4. Report findings

frontmatter + 正文 —— 类似 skill 文件格式。

4.3 755 行的实现

推测 755 行包含: - YAML frontmatter 解析 - 文件 glob 扫描 - 路径解析(用户 / 项目 / 内置) - agent 合并(同名按优先级) - 校验 - 缓存


5. UI.tsx 871 行渲染组件

5.1 推测的结构

function AgentUI({ input, result, onApprove, onDeny }) {
  return (
    <Box flexDirection="column">
      {/* 头部:工具名 + agent 类型 */}
      <Box>
        <Text color="cyan">🤖 Agent: {input.subagent_type}</Text>
      </Box>

      {/* Prompt 突出显示 */}
      <Box borderStyle="round" borderColor="green" paddingX={1}>
        <Text>{input.prompt}</Text>
      </Box>

      {/* 推测的 agent 元信息 */}
      <Box>
        <Text dimColor>Model: {agentDef.model}</Text>
        <Text dimColor>Tools: {agentDef.tools.join(', ')}</Text>
      </Box>

      {/* 子 agent 进度(如果运行中) */}
      {runningProgress && (
        <Box>
          <Spinner />
          <Text>{runningProgress.status}</Text>
        </Box>
      )}

      {/* 键盘提示 */}
      <KeyboardShortcutHint keys="Enter" label="Approve" />
      <KeyboardShortcutHint keys="Esc" label="Deny" />
    </Box>
  )
}

类似 FileEditTool UI,但多"agent 类型"、"进度"。

5.2 871 行的实现

推测 871 行包含: - 多种 agent 类型的不同 UI 变体 - 进度显示(动画) - 错误状态显示 - 嵌套子 agent 折叠展开


6. prompt.ts 287 行喂给 LLM 的描述

export const AGENT_TOOL_PROMPT = `
# Agent

Launch a new agent to handle complex, multi-step tasks.

## When to use
- Multi-step research tasks
- Codebase exploration
- Parallel investigation
- Tasks requiring different tools/perspective

## When NOT to use
- Simple single-tool calls
- Trivial operations
- Tasks that fit in one prompt

## Available agent types

${builtInAgents.map(a => `- ${a.name}: ${a.description}`).join('\n')}

## Best practices

1. **Use specific agent types** for specialized tasks
2. **Provide clear context** in the prompt
3. **Don't nest agents too deeply** (max 3 levels)
4. **Wait for results** before deciding next step
...
`

~287 行的精心设计 —— 包括 何时用 / 不用 / agent 类型 / 最佳实践。


7. resumeAgent.ts 265 行恢复 agent

7.1 推测的流程

export async function* resumeAgent(
  agentId: AgentId,
  input: ResumeInput,
  context: ToolUseContext,
): AsyncGenerator<AgentEvent, AgentResult, unknown> {
  // 1. 加载 agent 状态
  const state = await readAgentMetadata(agentId)
  if (!state) {
    return { content: 'Agent not found', is_error: true }
  }

  // 2. 检查状态
  if (isTerminalTaskStatus(state.status)) {
    return { content: `Agent already ${state.status}`, is_error: true }
  }

  // 3. 恢复 task
  const task = await resumeTask(agentId)

  // 4. 继续监听
  yield* task.events()

  return { content: task.result, is_error: task.status === 'failed' }
}

265 行的"agent 恢复" —— 推测涉及: - agent 状态加载 - 任务状态检查 - event 重新订阅 - 状态同步


8. forkSubagent.ts 210 行 fork 子 agent

// 推测
export async function* forkSubagent(
  parentAgentId: AgentId,
  input: ForkInput,
  context: ToolUseContext,
): AsyncGenerator<AgentEvent, AgentResult, unknown> {
  // 1. 复制父 agent 状态
  const parentState = await readAgentMetadata(parentAgentId)

  // 2. 创建新 agent
  const newAgent = await createAgentTask({
    type: 'local_agent',
    parentSessionId: getSessionId(),
    parentAgentId,
    initialState: parentState,  // 继承
    prompt: input.prompt,
  })

  // 3. 启动
  yield* newAgent.run()
}

210 行的 fork —— 推测: - 复制父 agent 状态 - 标记为"forked from" - 独立运行 - 共享部分资源


9. agentToolUtils.ts 686 行工具

推测包含: - agent name 解析 - 工具子集过滤 - prompt 模板 - 错误消息构造 - 状态转换 - ... 30+ 工具函数


10. agentMemory.ts 177 行 + agentMemorySnapshot.ts 197 行

10.1 agentMemory.ts

// 推测
export function getAgentMemory(agentId: AgentId): AgentMemory
export function updateAgentMemory(agentId: AgentId, update: Partial<AgentMemory>): void
export function clearAgentMemory(agentId: AgentId): void

agent 专属记忆 —— 不共享主 session 的 memory。

10.2 agentMemorySnapshot.ts 197 行

// 推测
export function createAgentMemorySnapshot(agentId: AgentId): MemorySnapshot
export function restoreAgentMemorySnapshot(snapshot: MemorySnapshot): void

memory 快照 —— fork / resume 时用。


11. builtInAgents.ts 72 行内置 agent 列表

// builtInAgents.ts 推测
export const builtInAgents: AgentDefinition[] = [
  {
    name: 'general-purpose',
    description: 'General-purpose agent for complex multi-step tasks',
    model: 'sonnet',
    tools: [...allTools],
  },
  {
    name: 'explore',
    description: 'Read-only agent for codebase exploration',
    model: 'haiku',
    tools: [Read, Grep, Glob],  // 限制只读
  },
  {
    name: 'plan',
    description: 'Plan-mode agent for creating structured plans',
    model: 'sonnet',
    tools: [Read, Grep, Glob],  // 限制只读
  },
]

3 个内置 agent: - general-purpose —— 通用(默认) - explore —— 只读探索 - plan —— Plan Mode

意义: - 用户0 配置就能用 - 子 agent 有"内建的角色"(不是裸的 LLM 调用)


12. agentColorManager.ts 66 行 + agentDisplay.ts 104 行

12.1 颜色管理

// agentColorManager.ts 推测
const AGENT_COLORS = [
  'red', 'green', 'blue', 'yellow', 'magenta', 'cyan'
]

export function getAgentColor(agentId: AgentId): string {
  // 根据 agentId 哈希 → 稳定颜色
  const hash = hashCode(agentId)
  return AGENT_COLORS[hash % AGENT_COLORS.length]
}

每个 agent 一个颜色 —— UI 区分。

12.2 显示

// agentDisplay.ts 推测
export function formatAgentDisplay(agentId: AgentId, name: string): string {
  const color = getAgentColor(agentId)
  return chalk[color](`[${name}]`)
}

显示 —— 给 agent 消息加颜色前缀。


13. 多 agent 嵌套层级

Level 0: 主 Claude Code session
Level 1: Agent("explore")
Level 2: Agent("general-purpose")
Level 3: Agent("plan")

最多 3 层嵌套(CLAUDE.md 注释推测)。

为什么 3 层: - 防止无限递归 - 控制 token 消耗 - 调试可追踪


14. 关键设计

14.1 "async generator" 串联父子

// 父 agent 看到子 agent 的 yield
for await (const event of engine.submitMessage(prompt)) {
  // event 可能是 tool_use: { name: 'Agent', input: {...} }
  if (event.type === 'tool_use' && event.tool === 'Agent') {
    // 调 runAgent
    yield* runAgent(input, context)
  }
}

父 agent 看到子 agent 的实时进度 —— 透明。

14.2 "独立 QueryEngine" 隔离

子 agent 有自己的 QueryEngine 实例(推测)—— 不共享父的 mutableMessages

好处: - 隔离性 - 子 agent 失败不影响父 - 子 agent 可以独立 /resume

14.3 "工具子集" 限制

{
  name: 'explore',
  tools: [Read, Grep, Glob],  // 只读
}

子 agent 只能使用部分工具 —— 安全 + 性能。

14.4 "model 选择" 灵活

{
  name: 'explore',
  model: 'haiku',  // 用小模型,省钱
}

子 agent 可指定 model —— explore 用 haiku 省 90% 成本。

14.5 "3 个内置 agent" 是默认值

[explore, plan, general-purpose]

用户 0 配置就能用 —— 启动后立即可用。

14.6 "agent memory" 隔离

每个 agent 独立记忆 —— fork 时可选择继承。


15. 实战:写一个简化版 Agent 系统

// 简化版(~50 行)
async function* runSubAgent(prompt: string, type: string): AsyncGenerator<string> {
  yield `> Sub-agent (${type}) started\n`
  yield `> Sub-agent thinking...\n`
  yield `> Sub-agent result: I did ${prompt}\n`
  yield `> Sub-agent completed\n`
}

// 用法
for await (const event of runSubAgent('fix bug', 'general-purpose')) {
  console.log(event)
}

对比 Claude Code: - 简化版没有真 LLM - 简化版没有隔离 - 简化版没有工具子集 - Claude Code 多了 100+ 边界处理


16. 关键洞察

16.1 "Agent 是"用户可编程的 sub-LLM"

LLM = 主 agent
AgentTool = 启动 sub-LLM(带特定 prompt + tools + model)

用户可定义自己的 agent(写到 ~/.claude/agents/)。

16.2 "6072 行 = 完整子系统"

AgentTool 不是"调用一个函数",是启动一个完整子系统: - 独立 task - 独立 memory - 独立 tool 集 - 独立 model

等于 1 个 mini Claude Code

16.3 "3 层嵌套限制" 是安全设计

防止: - 无限递归("agent spawn agent spawn...") - Token 爆炸 - 调试噩梦

3 层 = 工程妥协

16.4 "工具子集"是权限系统

子 agent 不能用 BashTool → 不能 rm -rf
子 agent 只能用 Read-only tools安全

16.5 "builtInAgents" 是默认产品

3 个内置 agent = Claude Code 的"开箱即用价值"

用户0 配置就能: - /explore 探索代码 - /plan 制定计划 - /general-purpose 通用任务

16.6 "async *call" 完美匹配多 agent 模式

async *call(input, context) {
  yield* runAgent(input, context)  // 透传子 agent 的所有 yield
}

父 agent 看到子 agent 实时进度 —— 完美透明


17. 阅读清单

  1. ✅ 完整通读 src/tools/AgentTool/(6072 行 / 15 文件)
  2. ✅ 读 phase-05-tools.md § 5.9
  3. ✅ 读 phase-06-agent-loop.md § 6.7
  4. 📌 读 src/tasks/LocalAgentTask/(推测 500+ 行)
  5. 📌 读 src/tasks/InProcessTeammateTask/(推测 800+ 行)
  6. 📌 读 src/utils/swarm/ 多 agent 协调

18. 练习任务

  1. AgentTool/ 里所有 export 函数 —— 应该 10+
  2. 画 3 层嵌套的时序图 —— explore → general-purpose → plan
  3. 设计你自己的"sub-agent"系统 —— 简化版 50 行
  4. 思考:子 agent vs 调普通函数 vs 调 API,什么时候该用哪种