Deep Dive | tools/AgentTool/ 6072 行多 Agent 入口拆解¶
重要性:⭐⭐⭐⭐⭐(多 Agent 协作的入口 —— Claude Code "能开子 agent" 的关键) 真实位置:
src/tools/AgentTool/(6072 行 / 15 文件) 角色:让 LLM 能启动子 agent(explore / plan / 自定义)跑独立任务 关联:phase-05-tools.md § 5.9、phase-06-agent-loop.md § 6.7、topics/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 关键设计¶
- 子 agent 自己的 QueryEngine —— 子 agent 独立于父 session
- task 系统集成 —— 通过
tasks/LocalAgentTask/或InProcessTeammateTask/ - 异步生成器 —— 父 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 "工具子集" 限制¶
子 agent 只能使用部分工具 —— 安全 + 性能。
14.4 "model 选择" 灵活¶
子 agent 可指定 model —— explore 用 haiku 省 90% 成本。
14.5 "3 个内置 agent" 是默认值¶
用户 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 模式¶
父 agent 看到子 agent 实时进度 —— 完美透明。
17. 阅读清单¶
- ✅ 完整通读
src/tools/AgentTool/(6072 行 / 15 文件) - ✅ 读 phase-05-tools.md § 5.9
- ✅ 读 phase-06-agent-loop.md § 6.7
- 📌 读
src/tasks/LocalAgentTask/(推测 500+ 行) - 📌 读
src/tasks/InProcessTeammateTask/(推测 800+ 行) - 📌 读
src/utils/swarm/多 agent 协调
18. 练习任务¶
- 数
AgentTool/里所有 export 函数 —— 应该 10+ - 画 3 层嵌套的时序图 —— explore → general-purpose → plan
- 设计你自己的"sub-agent"系统 —— 简化版 50 行
- 思考:子 agent vs 调普通函数 vs 调 API,什么时候该用哪种?