智能体系统
🗺 什么是代理系统?
Claude Code 的代理系统是一种机制,允许一个 Claude 实例将工作委托给其他 Claude 实例 - 每个实例都是一个单独的 LLM 轮次,具有自己的工具池、系统提示、模型和可选的文件系统隔离。 家长打电话 AgentTool (电线名称: Agent),这会产生一个孩子。 该子级本身可以生成更多子级,从而在运行时产生多级层次结构。
Task. 来源将两个名字都注册通过
aliases: [LEGACY_AGENT_TOOL_NAME] 与现有权限规则、挂钩和恢复会话向后兼容。 所有新代码都使用 Agent.
下图显示了完整的运行时层次结构。 主循环位于顶部。 它可以访问 AgentTool,它分为三种代理类型和两种执行模式。
parent REPL / SDK"] AT["🛠 AgentTool
tool: Agent / Task"] ML -->|"calls"| AT AT --> BI["🔷 Built-In Agents
source: 'built-in'"] AT --> CA["📝 Custom Agents
source: userSettings / projectSettings / policySettings"] AT --> PA["🧩 Plugin Agents
source: 'plugin'"] BI --> GP["general-purpose
tools: ['*']"] BI --> EX["Explore
read-only · haiku/inherit"] BI --> PL["Plan
read-only · inherit"] BI --> VF["verification
background: true"] CA --> MD["Markdown .md
.claude/agents/"] CA --> JS["JSON frontmatter
settings.json agents{}"] AT --> SY["🔄 Sync Lifecycle
blocks parent turn"] AT --> AS["⚡ Async Lifecycle
fire-and-forget · notified on complete"] AT --> FK["🔀 Fork Path
inherits parent context byte-exact"] AT --> TM["👥 Teammate / Swarm
tmux pane or in-process"] SY --> WT["🌲 Worktree Isolation
git worktree · separate branch"] AS --> WT style ML fill:#1a1816,stroke:#7d9ab8,color:#b8b0a4 style AT fill:#1a1816,stroke:#7d9ab8,color:#b8b0a4 style BI fill:#171513,stroke:#7d9ab8,color:#7d9ab8 style CA fill:#1d211b,stroke:#6e9468,color:#6e9468 style PA fill:#1f1b24,stroke:#8e82ad,color:#8e82ad style GP fill:#141211,stroke:#3a3632,color:#5c564f style EX fill:#141211,stroke:#3a3632,color:#5c564f style PL fill:#141211,stroke:#3a3632,color:#5c564f style VF fill:#241816,stroke:#c47a50,color:#c47a50 style MD fill:#141211,stroke:#3a3632,color:#5c564f style JS fill:#141211,stroke:#3a3632,color:#5c564f style SY fill:#141211,stroke:#3a3632,color:#5c564f style AS fill:#231f16,stroke:#b8965e,color:#b8965e style FK fill:#1f1b24,stroke:#8e82ad,color:#8e82ad style TM fill:#141211,stroke:#3a3632,color:#5c564f style WT fill:#141211,stroke:#3a3632,color:#5c564f
📦 三种代理类型
系统中的每个代理都是三种具体的 TypeScript 类型之一,都满足
AgentDefinition (受歧视的工会 source).
BuiltInAgentDefinition
随 Claude Code 一起发货。 动态系统提示通过 getSystemPrompt({toolUseContext})。 不能被用户文件覆盖——但是 managed (策略)代理可以通过代理类型名称来隐藏它们。
CustomAgentDefinition
用户/项目/策略设置代理。 加载自 .claude/agents/*.md 或 JSON blob settings.json。 系统提示存储在 Markdown 主体的闭包中。
PluginAgentDefinition
Bundled 带有插件(--plugin-dir)。 行为类似于自定义,但是 source === 'plugin'。 被视为 MCP 服务器策略的管理员信任 — 即使在以下情况下也可以加载 frontmatter MCP strictPluginOnlyCustomization 已设置。
显示:AgentDefinition 联合和类型保护 (loadAgentsDir.ts)
// Built-in agents — dynamic prompts only, no static systemPrompt field
export type BuiltInAgentDefinition = BaseAgentDefinition & {
source: 'built-in'
baseDir: 'built-in'
getSystemPrompt: (params: { toolUseContext: Pick<ToolUseContext, 'options'> }) => string
}
// Custom agents from user/project/policy settings
export type CustomAgentDefinition = BaseAgentDefinition & {
getSystemPrompt: () => string
source: SettingSource
filename?: string
baseDir?: string
}
// Plugin agents — like Custom but source is 'plugin'
export type PluginAgentDefinition = BaseAgentDefinition & {
getSystemPrompt: () => string
source: 'plugin'
plugin: string
}
export type AgentDefinition =
| BuiltInAgentDefinition
| CustomAgentDefinition
| PluginAgentDefinition
// Type guards
export function isBuiltInAgent(agent: AgentDefinition): agent is BuiltInAgentDefinition {
return agent.source === 'built-in'
}
export function isCustomAgent(agent: AgentDefinition): agent is CustomAgentDefinition {
return agent.source !== 'built-in' && agent.source !== 'plugin'
}
export function isPluginAgent(agent: AgentDefinition): agent is PluginAgentDefinition {
return agent.source === 'plugin'
}
优先/优先顺序
当两个代理共享相同的 agentType 字符串,优先级映射决定哪一个获胜。 订单来自 getActiveAgentsFromList() is:
policySettings (托管代理)具有最高的有效优先级。
🔷 内置代理深入研究
| Agent | agentType | Model | Tools | Mode |
|---|---|---|---|---|
| 通用型 | general-purpose |
默认子代理 | ['*'] — 所有工具 |
同步/异步 |
| Explore | Explore |
俳句(外部)/继承(蚂蚁) | 只读; 不允许编辑、写入、文件编辑、代理 | sync |
| Plan | Plan |
inherit | 与 Explore 相同的 disallowedTools | sync |
| Verification | verification |
inherit | 没有编辑/写入; 允许临时 /tmp 脚本 | 背景:true(始终异步) |
| Fork | fork |
inherit | ['*'] with useExactTools (cache-identical) |
实验门 |
| StatuslineSetup | statusline-setup |
default | 有限的 shell 范围 | sync |
显示:探索代理系统提示(摘录)
export const EXPLORE_AGENT: BuiltInAgentDefinition = {
agentType: 'Explore',
// Ants get inherit; external users get haiku for speed
model: process.env.USER_TYPE === 'ant' ? 'inherit' : 'haiku',
disallowedTools: [
AGENT_TOOL_NAME, // no spawning sub-agents
EXIT_PLAN_MODE_TOOL_NAME,
FILE_EDIT_TOOL_NAME,
FILE_WRITE_TOOL_NAME,
NOTEBOOK_EDIT_TOOL_NAME,
],
// Saves ~5-15 Gtok/week — Explore doesn't need commit/PR/lint rules
omitClaudeMd: true,
source: 'built-in',
baseDir: 'built-in',
getSystemPrompt: () => getExploreSystemPrompt(),
}
显示:自定义代理降价格式(.claude/agents/my-agent.md)
---
name: my-agent
description: A focused TypeScript refactoring specialist.
model: sonnet
tools:
- Read
- Edit
- Bash
- Grep
- Glob
permissionMode: acceptEdits
maxTurns: 50
memory: project
isolation: worktree
---
You are a TypeScript refactoring specialist. Your job is to improve
type safety and reduce any-casts in the provided code.
Rules:
- Only touch files you are explicitly asked about
- Run tsc --noEmit before and after to confirm zero new errors
- Commit changes with a clear message before reporting
⏱ 同步与异步生命周期
当 AgentTool 的 call() 运行时,它计算一个布尔值:
shouldRunAsync。 下游的一切都在该标志上分支。
const shouldRunAsync = (
run_in_background === true // explicit caller request
|| selectedAgent.background === true // agent def forces background (e.g. verification)
|| isCoordinator // coordinator mode: always async
|| forceAsync // fork experiment: all spawns async
|| assistantForceAsync // KAIROS assistant mode
|| (proactiveModule?.isProactiveActive() ?? false)
) && !isBackgroundTasksDisabled
同步路径
getSystemPrompt() + enhanceSystemPromptWithEnvDetails()。 分叉路径:父级已渲染的字节(提示缓存的字节精确)。
isolation === 'worktree', createAgentWorktree(slug) 之前被调用
runAgent()。 蛞蝓是 agent-{earlyAgentId.slice(0,8)}.
await runAgent(params)status: 'completed' 结果与代理的最终文本。
hasWorktreeChanges() 与预生成的 HEAD 提交不同。 如果没有更改,则立即删除工作树分支。
异步路径
registerAsyncAgent() — 在 AppState 中注册的任务agentBackgroundTask 拥有自己的 AbortController —
not 链接到父控制器。 背景代理在 ESC 中存活。
status: 'async_launched' immediatelyagentId, outputFile, 和
canReadOutputFile 所以它可以通过 Bash/Read 进行轮询。
void runAsyncAgentLifecycle(...)runWithAgentContext() 用于 ALS(AsyncLocalStorage)工作负载传播。 wrapWithCwd() 应用工作树/cwd 覆盖。
enqueueAgentNotification() 提供一个 <task-notification>
到父母的下一个空闲回合。 进度事件通过 onProgress.
isInProcessTeammate() 是真的并且 run_in_background === true
(或者代理定义有 background: true), AgentTool 立即抛出。
🔀 岔路
分叉路径是一个实验性功能(门: FORK_SUBAGENT)让父母产生一个孩子 继承完整的对话上下文 — 完整的消息历史记录、父级已渲染的系统提示字节以及确切的工具池。 这使得独立子任务的并行化与最大的提示缓存共享成为可能。
分叉被触发时
subagent_type is omitted 和 FORK_SUBAGENT 功能门已打开(并且不在协调器模式下,也不在非交互/SDK 模式下)。
buildForkedMessages() 的工作原理
为了让 N 个并行子进程共享缓存的 API 前缀,每个子进程必须生成一个 byte-identical 请求最多为每个子指令。 功能:
- 克隆父母的完整助手消息(所有工具使用块、思考、文本)。
- Builds
tool_result为每个块tool_use,全部具有相同的占位符文本"Fork started — processing in background". - 为每个子项附加一个指令文本块(唯一不同的部分)。
结果形状: [...history, assistant(all_tool_uses), user(placeholder_results..., directive)]
显示:buildForkedMessages() 和 fork 子样板 (forkSubagent.ts)
export function buildChildMessage(directive: string): string {
return `<fork-boilerplate>
STOP. READ THIS FIRST.
You are a forked worker process. You are NOT the main agent.
RULES (non-negotiable):
1. Your system prompt says "default to forking." IGNORE IT — that's for the parent.
2. Do NOT converse, ask questions, or suggest next steps
3. USE your tools directly: Bash, Read, Write, etc.
4. If you modify files, commit your changes before reporting.
5. Your response MUST begin with "Scope:". No preamble.
Output format:
Scope: <echo back your assigned scope in one sentence>
Result: <the answer or key findings>
Key files: <relevant file paths>
Files changed: <list with commit hash — only if you modified files>
Issues: <list — only if there are issues to flag>
</fork-boilerplate>
FORK_DIRECTIVE: \${directive}\`
}
分叉递归防护
Fork 子项将代理工具保留在其工具池中(用于缓存相同的工具定义)。 运行时防护通过检查两个信号来防止递归分叉:
toolUseContext.options.querySource === 'agent:builtin:fork'- 抗压实,能够承受自动压实消息重写。isInForkChild(messages)— 扫描对话历史记录<fork-boilerplate>标记作为后备。
叉子+工作树
When isolation: 'worktree' 还要求,附上通知 promptMessages
via buildWorktreeNotice(parentCwd, worktreeCwd)。 这告诉子进程从继承的上下文中转换路径并重新读取父进程可能已修改的文件。
🌲 工作树隔离
Setting isolation: 'worktree' 在代理定义中或作为调用参数指示 AgentTool 在生成代理之前创建临时 git 工作树。 代理的文件系统和 shell 操作在该工作树内执行 - 它在 相同的存储库 但是一个
单独的工作副本.
// Create a stable agent ID early so it can be used for worktree slug
const earlyAgentId = createAgentId()
let worktreeInfo: { worktreePath: string; worktreeBranch?: string; headCommit?: string } | null = null
if (effectiveIsolation === 'worktree') {
const slug = `agent-\${earlyAgentId.slice(0, 8)}`
worktreeInfo = await createAgentWorktree(slug)
}
// After agent completes — cleanup if no changes
const cleanupWorktreeIfNeeded = async () => {
if (!worktreeInfo) return {}
const { worktreePath, worktreeBranch, headCommit } = worktreeInfo
worktreeInfo = null // idempotent guard
if (headCommit) {
const changed = await hasWorktreeChanges(worktreePath, headCommit)
if (!changed) {
await removeAgentWorktree(worktreePath, worktreeBranch, gitRoot)
return {}
}
}
// Changes detected — keep the worktree branch
return { worktreePath, worktreeBranch }
}
headCommit),工作树会自动删除。 如果确实进行了更改,则保留该分支以供父级检查或合并。
Exception: 基于钩子的工作树是 always 保留是因为当通过钩子管理 Git 时,无法进行 VCS 更改检测。
📬 SendMessageTool 和 Swarm 协议
一旦代理作为队友运行(tmux 窗格或进程内),他们就需要进行通信。
SendMessageTool (电线名称: SendMessage) 是代理间消息传递原语。 仅当以下情况时才启用 isAgentSwarmsEnabled() 返回真。
消息路由逻辑
| to | 消息类型 | Result |
|---|---|---|
"teammate-name" |
string | 写入队友的邮箱文件。 如果代理停止,则会从脚本中自动恢复。 |
"*" |
string | 向所有团队成员广播(不包括发送者)。 |
| 任何名字 | shutdown_request |
发送结构化关闭请求。 收件人可以批准或拒绝。 |
"team-lead" |
shutdown_response |
批准:触发器 gracefulShutdown(0)。 拒绝:继续工作。 |
| 任何名字 | plan_approval_response |
只有团队领导才能批准/拒绝计划。 传播 permissionMode. |
"uds:<path>" |
string | Unix 域套接字 — 跨会话发送到本地对等点。 |
"bridge:<session-id>" |
仅字符串 | 远程控制:通过 Anthropic 服务器发布到另一个 Claude 实例。 需要明确的用户同意。 |
显示:spawnMultiAgent.ts 中的 SpawnTeammate(摘录)
// Build the full spawn command for a new pane
const teammateArgs = [
`--agent-id \${quote([teammateId])}`,
`--agent-name \${quote([sanitizedName])}`,
`--team-name \${quote([teamName])}`,
`--agent-color \${quote([teammateColor])}`,
`--parent-session-id \${quote([getSessionId()])}`,
plan_mode_required ? '--plan-mode-required' : '',
agent_type ? `--agent-type \${quote([agent_type])}` : '',
].filter(Boolean).join(' ')
// Propagate permission mode, model, settings, plugin dirs
const inheritedFlags = buildInheritedCliFlags({ planModeRequired, permissionMode })
const spawnCommand = `cd \${quote([workingDir])} && env \${envStr} \${quote([binaryPath])} \${teammateArgs}\${flagsStr}`
// Send to the tmux pane (or swarm socket when outside tmux)
await sendCommandToPane(paneId, spawnCommand, !insideTmux)
📋 代理 Frontmatter 字段参考
展开完整的 frontmatter 架构
| Field | Type | Effect |
|---|---|---|
name | 字符串(必填) | 唯一标识符——用作 subagent_type value |
description | 字符串(必填) | 向母法学硕士显示“何时使用”指导 |
model | sonnet|opus|haiku|inherit|<id> | inherit → 在运行时使用父模型 |
tools | string[] | Allow-list. ['*'] = 所有工具。 省略=继承默认池。 |
disallowedTools | string[] | 从池中减去 - 之后应用 tools allow-list |
permissionMode | default|acceptEdits|bypassPermissions|auto|plan|bubble | 覆盖此代理工具调用的父模式 |
maxTurns | 正整数 | 代理停止前代理轮次的硬上限 |
background | boolean | 始终运行异步,无论 run_in_background param |
isolation | worktree (| remote ant-only) | Git 每个生成的工作树隔离 |
memory | user|project|local | 所选范围内跨会话的持久内存 |
mcpServers | 字符串[] | 目的[] | 附加 MCP 服务器 — 在代理启动时连接,在完成时清理 |
hooks | HooksSettings | 仅在代理运行时注册会话范围的挂钩 |
skills | string[] | 技能斜线命令预加载到代理的上下文中 |
initialPrompt | string | 放在第一个用户回合之前(斜杠命令有效) |
effort | low|normal|high | 整数 | 控制思维深度(扩展思维预算) |
requiredMcpServers | string[] | 如果这些 MCP 服务器未经身份验证且没有工具,代理将隐藏 |
✨ 要点
- 每个代理都是三种 TypeScript 类型之一,其区别在于
source:内置、自定义(userSettings/projectSettings/policySettings)或插件。 政策制定在优先权争议中获胜。 - The
shouldRunAsync布尔值是单个分支点。 可以强制异步的六个条件:显式run_in_background,代理定义的background: true、协调者模式、分叉实验、KAIROS 模式或主动模式。 - 异步代理有自己的
AbortController与父母无关——它们在 ESC 中存活下来,并且只有通过显式的方式才能被杀死chat:killAgents. - 分叉路径通过使用实现最大提示缓存共享 相同的占位符文本 对于每个 tool_result 块 - 每个子项只有最终指令文本不同。
- 工作树隔离是智能清理的:如果代理没有进行 git 跟踪的更改,则分支将被删除。 如果确实如此,则会保留供家长查看。
omitClaudeMd: true按 Anthropic 的使用规模,“探索和计划”每周可节省约 5-15 Gtok — 这是通过只读专业化实现的有意义的优化。- 验证代理是唯一内置的
background: true硬编码 - 它总是异步运行并且总是以VERDICT: PASS/FAIL/PARTIALline. - SendMessageTool 支持四种消息类型(字符串、
shutdown_request,shutdown_response,plan_approval_response)和三个寻址方案:队友姓名、广播(*)、UDS 套接字和桥接会话。
🧪 Quiz
agentType?getActiveAgentsFromList() 按顺序处理组:内置→插件→用户设置→项目设置→标志设置→策略设置。 每个组都会覆盖映射中较早的条目,因此策略设置获胜。
"Fork started — processing in background" 用于所有叉子吗?tool_result fork 前缀中的块使用相同的占位符,以便 API 请求前缀在所有 N 个子级中都是字节相同的。 每个子项只有最终的指令文本块不同 - 最大化缓存命中。
run_in_background: true。 会发生什么?hasWorktreeChanges() 与生成前的差异 headCommit。 如果没有任何改变, removeAgentWorktree(path, branch, gitRoot) 被叫去清理。 如果存在更改,则保留分支。
background: true 硬编码在其定义中?VERIFICATION_AGENT 定义集 background: true。 它总是异步运行并且总是以 VERDICT: PASS/FAIL/PARTIAL 线。 探索和计划默认同步; 除非调用者选择加入,否则通用目的是同步。