Claude Code 源码:Agent 工具 --- 多 Agent 的路由与定义机制
导航
术语说明 :queryLoop 是 Claude Code 的核心执行循环,负责处理 LLM 的推理与工具调用闭环。详见 006-query-loop。
- 🎯 为什么需要多 Agent? → 核心问题
- ⚡ 何时触发 Agent? → 触发机制
- 🔀 如何选择 Agent? → 路由机制 --- AgentTool 的决策树
- 🧬 如何定义 Agent? → 定义结构 --- 三个配置维度
- 🔧 如何实现差异化? → 实现机制 --- 工具过滤 + 提示词注入
- 📦 Built-in vs Custom → 加载机制
目录
第一部分:为什么需要多 Agent
- 核心问题
- [子 Agent 的价值](#子 Agent 的价值 "#value-proposition")
- [何时使用 Agent?如何触发?](#何时使用 Agent?如何触发? "#when-and-how-to-trigger")
第二部分:路由机制(核心)
第三部分:Agent 定义结构(核心)
第四部分:实现机制(核心)
第五部分:加载机制
- [Built-in Agents](#Built-in Agents "#builtin-agents")
- [Custom Agents](#Custom Agents "#custom-agents")
- 加载优先级
第六部分:派生流程
- [派生 Agent 的基本流程](#派生 Agent 的基本流程 "#spawn-flow")
- [queryTracking 调用链追踪](#queryTracking 调用链追踪 "#query-tracking")
为什么需要多 Agent?
极限场景
用户说"实现用户认证功能,包括前端登录页、后端 API、数据库迁移、单元测试"。
单 Agent 串行执行:
makefile
主 Agent 的串行流程:
① 实现后端 API(10 分钟)
② 实现前端页面(10 分钟)
③ 编写数据库迁移(5 分钟)
④ 编写单元测试(10 分钟)
总耗时: 35 分钟
上下文污染: 所有文件内容都在主 Agent 的上下文中
多 Agent 并行执行:
makefile
主 Agent 的并行编排:
① 派生 backend-agent(10 分钟)
② 派生 frontend-agent(10 分钟,同时进行)
③ 派生 test-agent(10 分钟,同时进行)
总耗时: 10 分钟(并行)
上下文隔离: 每个 Agent 只看到相关文件
子 Agent 的价值
| 问题 | 子 Agent 的解决方案 |
|---|---|
| 上下文限制 | 每个 Agent 有独立的上下文窗口 |
| 任务分治 | 按领域拆分(前端/后端/测试) |
| 并行执行 | 多个 Agent 同时工作 |
| 专业化 | 不同 Agent 有不同的工具集和提示词 |
何时使用 Agent?如何触发?
触发机制:LLM 的工具选择
核心流程:
用户请求 → LLM 分析任务 → 选择工具 → 调用 AgentTool
LLM 如何知道要用 AgentTool?
核心机制 :LLM 的工具选择是声明式匹配,而非硬编码规则。
less
工具描述(What) + 系统提示词(When) = LLM 自主决策
设计哲学:
- 不是"if 用户说探索 then 调用 Explore"(硬编码)
- 而是"给 LLM 工具说明书,让它自己判断"(声明式)
通过两个关键信息:
1. 工具描述(告诉 LLM 这个工具是干什么的)
typescript
AgentTool.description = `
Launch a new agent to handle complex, multi-step tasks.
Agent types:
- Explore: Fast codebase exploration (read-only)
- Plan: Design implementation plans
- general-purpose: Full capabilities
Use for:
- Open-ended questions spanning the codebase
- Tasks requiring specialized expertise
- Parallel work (launch multiple agents concurrently)
`
2. 系统提示词(告诉 LLM 何时应该用)
markdown
## When to Use the Agent Tool
✅ Use Agent when:
- Exploring unfamiliar code → Agent (Explore)
- Planning implementation → Agent (Plan)
- Parallel tasks (frontend + backend) → Agent × 2
❌ Don't use Agent when:
- Reading a single file → Use Read
- Simple search → Use Grep
- Direct code edit → Use Edit
何时触发 Agent?
| 场景 | LLM 决策 | 原因 |
|---|---|---|
| "读取 auth.ts" | 使用 Read |
单一文件,不需要 Agent |
| "探索认证系统实现" | 使用 Agent (Explore) |
多文件探索,系统提示词明确指导 |
| "设计新功能方案" | 使用 Agent (Plan) |
规划任务,需要专业 Agent |
| "实现前后端功能" | 使用 Agent × 2(并行) |
可并行,系统提示词建议并行 |
| "搜索 'login' 关键词" | 使用 Grep |
简单搜索,不需要 Agent |
触发示例
css
用户: "探索这个项目的认证系统"
LLM 分析:
1. 关键词: "探索" → 不是单一文件读取
2. 范围: "认证系统" → 涉及多个文件
3. 匹配工具描述: "Explore agent for codebase exploration"
4. 匹配系统提示词: "Exploring code → Use Agent (Explore)"
LLM 返回:
{
"name": "Agent",
"input": {
"subagent_type": "Explore",
"prompt": "探索认证系统的实现,找出相关文件和架构"
}
}
关键设计点
核心问题:如何让 LLM 知道何时使用 AgentTool?
三种可能的实现方案
方案 A:在主 Agent 提示词中硬编码规则
markdown
系统提示词:
"当用户说'探索'时,调用 Agent 工具,subagent_type='Explore'
当用户说'设计'时,调用 Agent 工具,subagent_type='Plan'
当用户说'实现前后端'时,并行调用两个 Agent 工具"
问题:
- ❌ 规则固定,无法适应语义变化("了解代码" ≈ "探索",但规则匹配不到)
- ❌ 添加新 Agent 类型需要修改主提示词
- ❌ 规则越来越多,提示词膨胀
- ❌ 无法处理复杂场景("探索并修改" → 应该用哪个 Agent?)
方案 B:工具描述 + 系统提示词指导(Claude Code 采用)
typescript
// 工具描述(声明能力)
AgentTool.description = `
Launch agent for complex tasks.
- Explore: codebase exploration (read-only)
- Plan: design implementation plans
Use for: open-ended questions, parallel work
`
// 系统提示词(使用指导)
"Use Agent (Explore) when exploring unfamiliar code
Use Agent (Plan) when planning implementation
Launch multiple agents for parallel tasks"
优势:
- ✅ LLM 理解语义,灵活匹配("了解代码" → 自动识别为探索场景)
- ✅ 添加新 Agent 只需更新工具描述,主提示词不变
- ✅ 描述简洁,不会膨胀
- ✅ LLM 可以根据上下文综合判断
方案 C:只有工具描述,没有系统提示词指导
typescript
// 只提供工具描述
AgentTool.description = `Launch a new agent to handle tasks`
问题:
- ❌ 描述太模糊,LLM 不知道何时应该用
- ❌ LLM 可能过度使用或不使用 Agent
- ❌ 缺少场景指导,决策不稳定
Claude Code 为什么选择方案 B?
| 维度 | 方案 A(硬编码规则) | 方案 B(描述+指导)⭐ | 方案 C(只有描述) |
|---|---|---|---|
| 语义理解 | ❌ 只能匹配关键词 | ✅ 理解任务意图 | ⚠️ 理解但缺少指导 |
| 扩展性 | ❌ 修改主提示词 | ✅ 只更新工具描述 | ✅ 只更新工具描述 |
| 决策稳定性 | ✅ 规则明确 | ✅ 指导明确 | ❌ 决策不稳定 |
| 维护成本 | ❌ 规则膨胀 | ✅ 描述简洁 | ✅ 描述简洁 |
| 灵活性 | ❌ 固定规则 | ✅ 上下文判断 | ⚠️ 过于灵活 |
方案 B 的核心优势:
makefile
工具描述(What): 告诉 LLM 这个工具能做什么
+
系统提示词(When): 告诉 LLM 什么场景下应该用
↓
平衡了灵活性和稳定性
工具描述 = LLM 的"使用手册"
typescript
// ✅ 好的描述(明确用途和场景)
description: `Launch agent for complex tasks.
Use for codebase exploration, planning, parallel work.`
// ❌ 不好的描述(太模糊)
description: `Agent tool`
系统提示词 = LLM 的"决策指南"
markdown
## ✅ 明确的指导
"Codebase exploration → Use Agent (Explore)"
"Parallel tasks → Launch multiple agents"
## ❌ 模糊的指导
"You can use agents if needed"
完整触发流程
markdown
① 用户发送请求
"探索认证系统"
② queryLoop 调用 LLM
- 传入系统提示词(包含 Agent 使用指导)
- 传入工具列表(包含 AgentTool 描述)
③ LLM 分析任务
- 任务类型: 代码探索
- 复杂度: 多文件
- 匹配工具: Agent (Explore)
④ LLM 返回工具调用
{ "name": "Agent", "input": {...} }
⑤ queryLoop 执行 AgentTool
- 路由到 spawnBuiltInAgent('Explore')
- 过滤工具集(只读工具)
- 启动子 queryLoop
⑥ 子 Agent 完成任务
- 返回探索结果
⑦ 主 Agent 继续执行
- 根据结果决定下一步
小结
触发机制的本质:
声明式工具选择 = 工具描述(能力声明) + 系统提示词(使用指导) + LLM 推理
三层决策模型:
diff
第一层:能力匹配
- LLM 读取工具描述
- 判断工具能否完成任务
第二层:场景匹配
- LLM 读取系统提示词
- 判断当前场景是否适合使用
第三层:成本评估
- 单一工具 vs 派生 Agent
- 串行执行 vs 并行执行
何时用 Agent?
- 多文件探索 → Explore Agent
- 实现规划 → Plan Agent
- 并行任务 → 多个 Agent
- 复杂任务 → general-purpose Agent
如何触发?
- LLM 根据工具描述 和系统提示词自动选择
- 不需要用户显式指定(LLM 自动决策)
不用 Agent 的情况?
- 单文件操作 → Read/Write/Edit
- 简单搜索 → Grep/Glob
- 直接修改 → Edit
AgentTool 的路由机制
核心问题:AgentTool 如何根据参数选择不同的 Agent?
路由决策树
typescript
// src/tools/AgentTool/AgentTool.ts(简化)
export const AgentTool = buildTool({
name: 'Agent',
async call(input, context) {
const { subagent_type, team_name, prompt } = input
// 💡 路由逻辑
if (team_name) {
// ① 团队成员 Agent(异步通信)
return spawnTeammate(input, context)
}
if (subagent_type) {
// ② 检查是否是 Built-in Agent
const builtInAgent = BUILT_IN_AGENTS[subagent_type]
if (builtInAgent) {
return spawnBuiltInAgent(subagent_type, input, context)
}
// ③ 检查是否是 Custom Agent
const customAgent = await loadCustomAgent(subagent_type, context)
if (customAgent) {
return spawnCustomAgent(customAgent, input, context)
}
// ④ 未找到指定的 Agent 类型
throw new Error(`Unknown agent type: ${subagent_type}`)
}
// ⑤ 默认:派生 general-purpose Agent
return spawnAgent(input, context)
}
})
路由流程图
scss
Agent 工具调用
↓
有 team_name?
├─ 是 → spawnTeammate()(团队成员 Agent)
│
└─ 否 → 有 subagent_type?
├─ 是 → 是 Built-in Agent?
│ ├─ 是 → spawnBuiltInAgent()
│ └─ 否 → 是 Custom Agent?
│ ├─ 是 → spawnCustomAgent()
│ └─ 否 → 抛出错误
│
└─ 否 → spawnAgent()(general-purpose)
参数组合矩阵
| team_name | subagent_type | 路由结果 |
|---|---|---|
| ✅ 有 | 任意 | spawnTeammate() --- 团队成员 Agent |
| ❌ 无 | "Explore" |
spawnBuiltInAgent() --- Built-in Explore Agent |
| ❌ 无 | "Plan" |
spawnBuiltInAgent() --- Built-in Plan Agent |
| ❌ 无 | "my-agent" |
spawnCustomAgent() --- Custom Agent(如果存在) |
| ❌ 无 | ❌ 无 | spawnAgent() --- general-purpose Agent |
关键点 :team_name 优先级最高,如果指定了团队,subagent_type 会被忽略。
路由函数的职责
| 函数 | 职责 | 同步/异步 |
|---|---|---|
spawnTeammate() |
创建团队成员 Agent,立即返回 | 异步(不等待完成) |
spawnBuiltInAgent() |
启动 Built-in Agent,等待完成 | 同步(等待完成) |
spawnCustomAgent() |
启动 Custom Agent,等待完成 | 同步(等待完成) |
spawnAgent() |
启动 general-purpose Agent | 同步(等待完成) |
为什么 spawnTeammate 是异步的?
typescript
// spawnTeammate 立即返回
const result = await spawnTeammate(input, context)
// result = { message: "Teammate has been spawned" }
// Agent 在后台运行,通过 SendMessage 通信
// 其他函数等待完成
const result = await spawnBuiltInAgent(type, input, context)
// result = Agent 的完整输出
异步错误处理 :如果子 Agent 报错退出,父 Agent 如何感知?这涉及团队通信机制,详见 010-team-collaboration。
Agent 定义结构
核心问题:如何让同一个 queryLoop 表现出不同行为?
三个配置维度
| 维度 | 作用 | 实现方式 |
|---|---|---|
| 工具集 | 控制 Agent 能调用哪些工具 | disallowedTools 过滤 |
| 提示词 | 控制 Agent 的行为和专业性 | prompt 注入到系统消息 |
| 模型 | 控制 Agent 使用的 LLM | model 参数覆盖 |
Agent 定义的完整结构
typescript
// Agent 定义的类型
interface AgentDefinition {
name: string // Agent 名称
description: string // Agent 描述
prompt: string // 系统提示词
disallowedTools?: string[] // 禁用的工具列表
model?: string // 使用的模型(可选)
}
// 示例:Explore Agent
const ExploreAgent: AgentDefinition = {
name: 'Explore',
description: '快速探索代码库的专用 Agent',
// 维度 1:工具集
disallowedTools: [
'Agent', // 不能派生子 Agent
'Edit', // 不能修改文件
'Write', // 不能创建文件
'NotebookEdit', // 不能修改 Notebook
],
// 维度 2:提示词
prompt: `你是一个代码探索专家。你的任务是快速探索代码库,找出相关文件和架构模式。
使用 Grep 搜索关键词,使用 Read 理解代码结构,使用 Glob 查找文件。
重点关注:
- 文件组织结构
- 命名约定
- 架构模式
- 依赖关系
不要修改任何文件,只进行只读探索。`,
// 维度 3:模型(可选,默认继承父 Agent)
// model: 'claude-sonnet-4-6'
}
从定义到运行的流程
javascript
① 用户调用 Agent 工具
{
"name": "Agent",
"input": {
"subagent_type": "Explore",
"prompt": "探索认证系统"
}
}
② AgentTool 路由到 spawnBuiltInAgent()
- 查找 BUILT_IN_AGENTS['Explore']
- 获取 Agent 定义
③ 过滤工具列表
availableTools = context.tools.filter(tool => {
return !agentDef.disallowedTools.includes(tool.name)
})
// 结果:只有 Read, Grep, Glob, Bash 等只读工具
④ 构建消息序列
messages = [
{ role: 'system', content: agentDef.prompt }, // 专业提示词
{ role: 'user', content: input.prompt } // 用户任务
]
⑤ 选择模型
model = agentDef.model ?? context.model
⑥ 启动 queryLoop
context.deps.query({
messages,
tools: availableTools, // 过滤后的工具
model, // 选择的模型
agentId: uuid(), // 新的 agentId
})
⑦ queryLoop 运行
- LLM 收到系统提示词:"你是一个代码探索专家..."
- LLM 只能看到 availableTools 中的工具
- LLM 调用工具(只能调用 Read, Grep, Glob 等)
- LLM 返回探索结果
⑧ 返回结果给父 Agent
实现机制
维度 1:工具集过滤
实现原理 :在启动 queryLoop 前,过滤掉 disallowedTools 中的工具。
typescript
// src/tools/AgentTool/spawnBuiltInAgent.ts(简化)
export async function spawnBuiltInAgent(type, input, context) {
const agentDef = BUILT_IN_AGENTS[type]
// 💡 关键:过滤工具列表(LLM 视角的最终有效载荷)
const availableTools = context.tools.filter(tool => {
return !agentDef.disallowedTools?.includes(tool.name)
})
// 启动 queryLoop,传入过滤后的工具
const result = await context.deps.query({
messages: [...],
tools: availableTools, // ← LLM 只能看到和调用这些工具
})
return result
}
效果:
typescript
// Explore Agent 的定义
{
disallowedTools: ['Agent', 'Edit', 'Write', 'NotebookEdit']
}
// 过滤后的工具列表(LLM 视角的最终有效载荷)
availableTools = [
'Read', // ✅ 可用
'Grep', // ✅ 可用
'Glob', // ✅ 可用
'Bash', // ✅ 可用
// 'Edit', // ❌ 被过滤
// 'Write', // ❌ 被过滤
// 'Agent', // ❌ 被过滤
]
LLM 的视角:
diff
当 Explore Agent 的 queryLoop 运行时:
- LLM 收到的工具列表中没有 Edit、Write
- LLM 无法调用这些工具(因为不在可用列表中)
- 即使 LLM 尝试调用,系统也会拒绝
维度 2:提示词注入
实现原理 :将 Agent 的 prompt 作为系统消息注入到 queryLoop。
typescript
// src/tools/AgentTool/spawnBuiltInAgent.ts(简化)
export async function spawnBuiltInAgent(type, input, context) {
const agentDef = BUILT_IN_AGENTS[type]
// 💡 关键:构建系统提示词
const systemPrompt = agentDef.prompt
// 启动 queryLoop
const result = await context.deps.query({
messages: [
{ role: 'system', content: systemPrompt }, // ← Agent 的专业提示词
{ role: 'user', content: input.prompt } // ← 用户的任务描述
],
})
return result
}
效果对比:
typescript
// Explore Agent 的提示词
prompt: `你是一个代码探索专家。你的任务是快速探索代码库...`
// LLM 收到的消息序列
[
{ role: 'system', content: '你是一个代码探索专家...' }, // ← 专业身份
{ role: 'user', content: '探索认证系统的实现' } // ← 具体任务
]
// general-purpose Agent 的提示词
prompt: `你是一个通用的代码助手,可以执行各种任务。`
// LLM 收到的消息序列
[
{ role: 'system', content: '你是一个通用的代码助手...' }, // ← 通用身份
{ role: 'user', content: '探索认证系统的实现' }
]
为什么提示词很重要?
diff
同样的任务"探索认证系统":
general-purpose Agent:
- 可能会尝试修改代码
- 可能会深入实现细节
- 可能会偏离探索目标
Explore Agent:
- 专注于只读探索
- 关注架构和模式
- 输出结构化的探索结果
维度 3:模型选择
实现原理:Agent 可以指定使用的模型,覆盖默认模型。
typescript
// src/tools/AgentTool/spawnCustomAgent.ts(简化)
export async function spawnCustomAgent(agentDef, input, context) {
// 💡 关键:使用 Agent 指定的模型(如果有)
const model = agentDef.model ?? context.model
// 启动 queryLoop
const result = await context.deps.query({
messages: [...],
model, // ← 使用指定的模型
})
return result
}
模型选择优先级:
markdown
1. Agent 定义中的 model 字段(最高优先级)
↓
2. 父 Agent 的 model
↓
3. 全局默认模型(最低优先级)
示例:
typescript
// Custom Agent 定义
{
name: 'frontend-specialist',
model: 'claude-opus-4-6', // 💡 指定使用 Opus
}
// 主 Agent 使用 Sonnet
context.model = 'claude-sonnet-4-6'
// 派生 frontend-specialist Agent
{
"name": "Agent",
"input": {
"subagent_type": "frontend-specialist",
"prompt": "实现登录页面"
}
}
// 结果:frontend-specialist 使用 Opus,而不是 Sonnet
加载机制
Built-in Agents
Claude Code 预定义了几种常用的 Agent 类型:
typescript
// src/tools/AgentTool/builtInAgents.ts(简化)
export const BUILT_IN_AGENTS: Record<string, BuiltInAgentDefinition> = {
'general-purpose': {
name: 'general-purpose',
description: '通用任务执行 Agent,拥有所有工具',
disallowedTools: [],
prompt: `你是一个通用的代码助手,可以执行各种任务。`
},
'Explore': {
name: 'Explore',
description: '快速探索代码库的专用 Agent',
disallowedTools: ['Agent', 'Edit', 'Write', 'NotebookEdit'],
prompt: `你是一个代码探索专家...`
},
'Plan': {
name: 'Plan',
description: '制定实现计划的专用 Agent',
disallowedTools: ['Agent', 'Edit', 'Write', 'NotebookEdit'],
prompt: `你是一个架构设计专家...`
},
}
Built-in Agents 对比:
| Agent 类型 | 工具集 | 提示词重点 | 典型用途 |
|---|---|---|---|
general-purpose |
所有工具 | 通用任务执行 | 默认 Agent |
Explore |
Read, Grep, Glob | 代码探索,理解架构 | 探索陌生代码库 |
Plan |
只读工具 + Write(plan) | 制定实现计划 | 复杂任务的规划 |
Custom Agents
用户可以在 .claude/agents/ 目录中定义自己的 Agent 类型。
文件格式:
markdown
<!-- .claude/agents/backend-specialist.md -->
---
name: backend-specialist
description: 专注于后端开发的 Agent
model: claude-sonnet-4-6
disallowedTools:
- Agent
---
你是一个后端开发专家,专注于:
- API 设计和实现
- 数据库操作
- 性能优化
请遵循项目规范:
- 代码风格:参考 docs/coding-style.md
- API 设计:参考 docs/api-design.md
加载逻辑:
typescript
// src/tools/AgentTool/loadCustomAgent.ts(简化)
export async function loadCustomAgent(name: string, context) {
// ① 构建文件路径
const agentPath = join(getAgentsDirectory(), `${name}.md`)
// ② 检查文件是否存在
if (!fs.existsSync(agentPath)) {
return null
}
// ③ 读取文件内容
const fileContent = fs.readFileSync(agentPath, 'utf-8')
// ④ 解析 frontmatter
const { data: frontmatter, content } = matter(fileContent)
// ⑤ 返回 Agent 定义
return {
name: frontmatter.name,
description: frontmatter.description,
model: frontmatter.model,
disallowedTools: frontmatter.disallowedTools ?? [],
content, // 💡 Markdown 内容作为系统提示词
}
}
加载优先级
vbnet
① 检查 team_name(最高优先级)
↓
② 检查 Built-in Agents
↓
③ 检查 Custom Agents
↓
④ 默认 general-purpose Agent
为什么 Built-in 优先于 Custom?
diff
防止用户意外覆盖系统 Agent:
- 如果用户创建了 .claude/agents/Explore.md
- 系统仍然使用 Built-in Explore Agent
- 避免破坏系统行为
派生 Agent 的基本流程
typescript
// src/tools/AgentTool/spawnAgent.ts(简化)
export async function spawnAgent(input, context) {
// ① 生成 agentId
const agentId = context.deps.uuid()
// ② 构建 queryTracking
const queryTracking = context.queryTracking
? {
chainId: context.queryTracking.chainId,
depth: context.queryTracking.depth + 1,
}
: {
chainId: context.deps.uuid(),
depth: 0,
}
// ③ 启动 Agent(使用所有工具)
const result = await context.deps.query({
messages: [{ role: 'user', content: input.prompt }],
agentId,
queryTracking,
tools: context.tools, // 💡 所有工具
})
return result
}
queryTracking 调用链追踪
作用:追踪 Agent 的调用链和深度。
typescript
interface QueryTracking {
chainId: string // 调用链 ID(同一链上的所有 Agent 共享)
depth: number // 调用深度(主 Agent = 0,子 Agent = 1,孙 Agent = 2)
}
示例:
ini
主 Agent (depth=0, chainId=abc)
├─ 子 Agent A (depth=1, chainId=abc)
│ └─ 孙 Agent A1 (depth=2, chainId=abc)
└─ 子 Agent B (depth=1, chainId=abc)
用途:
- 日志追踪:通过
chainId关联同一任务的所有 Agent - 深度限制:防止无限递归(如限制
depth < 3) - 性能监控:统计每个深度的 Agent 数量
深度限制与熔断 :如何防止 Agent 无限递归?Token 预算如何继承?详见 012-agent-lifecycle。
并发冲突 :多个 Agent 同时修改同一文件时如何处理?文件锁、状态快照机制详见 011-concurrency。
总结
核心要点
- 路由机制 :AgentTool 根据
team_name和subagent_type参数路由到不同的函数 - 定义结构:Agent 通过三个维度配置:工具集、提示词、模型
- 实现机制 :
- 工具集过滤:
disallowedTools过滤工具列表 - 提示词注入:
prompt作为系统消息 - 模型选择:
model覆盖默认模型
- 工具集过滤:
- 加载机制:Built-in Agents 优先于 Custom Agents
设计优势
- 简单而强大:只需三个配置维度就能创建各种专业化的 Agent
- 可组合:自由组合工具集、提示词、模型
- 易于扩展:添加新 Agent 类型不需要修改 queryLoop 核心逻辑
高级应用模式
审计者模式 :如何用高级 Agent 审计低级 Agent 的输出?验证步骤、Schema 校验等高级模式详见 013-agent-patterns。
本系列后续文章
- 008-tool-explore:深入 Explore Agent 的实现细节
- 009-tool-plan:深入 Plan Agent 的实现细节
- 010-team-collaboration:团队协作和 SendMessage 机制
- 011-concurrency:并发控制与状态一致性
- 012-agent-lifecycle:Agent 生命周期管理
- 013-agent-patterns:高级 Agent 应用模式
常见问题 FAQ
Q1: Agent 和 Tool 有什么区别?
A: Agent 是 queryLoop 的实例,Tool 是 Agent 可以调用的能力。
diff
Tool(工具):
- Read, Write, Edit, Bash 等
- 执行具体操作
- 被 Agent 调用
Agent(代理):
- 运行 queryLoop 的实例
- 可以调用多个 Tool
- 有独立的上下文和工具集
Q2: 为什么 Command 不直接转换为 Tool?
A : 因为 Command 返回的是 prompt,不是操作结果。
diff
Tool 的特点:
- 执行操作,返回结果
- 例如:Read 返回文件内容
Command 的特点:
- 返回 prompt 文本
- 例如:commit 返回 "Create a git commit following..."
- 需要 LLM 看到 prompt 后再决定如何执行
SkillTool 是桥梁,负责:
- 执行 Command(调用
getPromptForCommand) - 将 prompt 注入到对话(通过
newMessages) - LLM 看到 prompt 后自己决定调用哪些 Tool
Q3: 如何创建自定义 Agent?
A : 在 .claude/agents/ 目录创建 Markdown 文件:
markdown
<!-- .claude/agents/my-agent.md -->
---
name: my-agent
description: 我的自定义 Agent
model: claude-sonnet-4-6
disallowedTools:
- Agent
- Edit
---
你是一个专门做代码审查的 Agent。
重点关注:
- 类型安全
- 错误处理
- 测试覆盖率
调用方式:
typescript
{
"name": "Agent",
"input": {
"subagent_type": "my-agent",
"prompt": "审查这段代码"
}
}
Q4: Built-in Agent 和 Custom Agent 的优先级?
A: Built-in 优先级更高。
diff
加载顺序:
① 检查 Built-in Agents(Explore, Plan, general-purpose)
② 检查 Custom Agents(.claude/agents/*.md)
如果同名:
- Built-in 优先
- 防止用户意外覆盖系统 Agent
Q5: Agent 可以无限递归吗?
A: 理论上可以,但有保护机制:
typescript
// queryTracking 追踪深度
{
chainId: 'abc',
depth: 2 // 当前深度
}
// 系统可以限制最大深度
if (queryTracking.depth >= MAX_DEPTH) {
throw new Error('Max agent depth exceeded')
}
详细的深度限制和 Token 熔断机制见 012-agent-lifecycle。
Q6: 如何调试 Agent 的工具过滤?
A: 在 Agent 定义中打印可用工具:
typescript
// spawnBuiltInAgent.ts
const availableTools = context.tools.filter(tool => {
return !agentDef.disallowedTools?.includes(tool.name)
})
console.log('Available tools:', availableTools.map(t => t.name))
// 输出: ['Read', 'Grep', 'Glob', 'Bash']
或者在 Custom Agent 的 prompt 中要求 LLM 报告:
markdown
---
name: debug-agent
---
首先列出你可以使用的所有工具,然后执行任务。
Q7: Agent 之间可以通信吗?
A : 可以,通过 团队模式(Team):
typescript
// 创建团队
{
"name": "Agent",
"input": {
"team_name": "my-team",
"name": "backend-agent",
"prompt": "实现后端 API"
}
}
// 另一个成员
{
"name": "Agent",
"input": {
"team_name": "my-team",
"name": "frontend-agent",
"prompt": "实现前端页面"
}
}
// 通过 SendMessage 通信
{
"name": "SendMessage",
"input": {
"to": "frontend-agent",
"message": "后端 API 已完成"
}
}
相关文章:
- Claude Code 源码的 Agent queryLoop运作引擎 --- queryLoop 的核心机制
- 005-tool-system --- 工具系统的设计
源码位置:
src/tools/AgentTool/AgentTool.ts--- 路由逻辑src/tools/AgentTool/builtInAgents.ts--- Built-in Agents 定义src/tools/AgentTool/loadCustomAgent.ts--- Custom Agents 加载