跳转到内容

Single Agent Model

此内容尚不支持你的语言。

Claude Code’s agent system starts with a simple abstraction: a single agent is a self-contained unit with its own system prompt, tool set, model preference, and behavioral constraints. Every sub-agent — whether built-in or user-defined — is an instance of this model.

All agents share a common base type defined in src/tools/AgentTool/loadAgentsDir.ts:

// src/tools/AgentTool/loadAgentsDir.ts — BaseAgentDefinition
export type BaseAgentDefinition = {
agentType: string // Unique identifier (e.g., "general-purpose", "Explore")
whenToUse: string // Description for the parent agent to decide when to spawn
tools?: string[] // Allowed tools (undefined = all, ['*'] = all)
disallowedTools?: string[] // Explicitly blocked tools
skills?: string[] // Skill names to preload
mcpServers?: AgentMcpServerSpec[]
hooks?: HooksSettings
color?: AgentColorName
model?: string // "inherit" = parent's model, or specific model ID
effort?: EffortValue
permissionMode?: PermissionMode
maxTurns?: number
memory?: AgentMemoryScope // 'user' | 'project' | 'local'
isolation?: 'worktree' | 'remote'
background?: boolean
omitClaudeMd?: boolean // Skip CLAUDE.md injection (saves tokens for read-only agents)
// ...
}

Three concrete types extend this base:

classDiagram
class BaseAgentDefinition {
+agentType: string
+whenToUse: string
+tools?: string[]
+model?: string
+memory?: AgentMemoryScope
+isolation?: string
}
class BuiltInAgentDefinition {
+source: "built-in"
+getSystemPrompt(params): string
}
class CustomAgentDefinition {
+source: SettingSource
+getSystemPrompt(): string
+filename?: string
}
class PluginAgentDefinition {
+source: "plugin"
+getSystemPrompt(): string
+plugin: string
}
BaseAgentDefinition <|-- BuiltInAgentDefinition
BaseAgentDefinition <|-- CustomAgentDefinition
BaseAgentDefinition <|-- PluginAgentDefinition

Built-in agents are registered in src/tools/AgentTool/builtInAgents.ts:

src/tools/AgentTool/builtInAgents.ts
export function getBuiltInAgents(): AgentDefinition[] {
if (
isEnvTruthy(process.env.CLAUDE_AGENT_SDK_DISABLE_BUILTIN_AGENTS) &&
getIsNonInteractiveSession()
) {
return [] // SDK users can start with a blank slate
}
if (feature('COORDINATOR_MODE')) {
if (isEnvTruthy(process.env.CLAUDE_CODE_COORDINATOR_MODE)) {
const { getCoordinatorAgents } = require('../../coordinator/workerAgent.js')
return getCoordinatorAgents() // Completely different agent set
}
}
const agents: AgentDefinition[] = [
GENERAL_PURPOSE_AGENT,
STATUSLINE_SETUP_AGENT,
]
// Conditionally added...
if (areExplorePlanAgentsEnabled()) {
agents.push(EXPLORE_AGENT, PLAN_AGENT)
}
// ...
return agents
}

The six built-in agents serve distinct roles:

AgentModelToolsKey Trait
General Purposedefault sub-agent['*'] (all)Workhorse for multi-step tasks
Explorehaiku (external) / inherit (ant)Read-only (no Edit/Write/Agent)Fast codebase search, omitClaudeMd: true
PlaninheritRead-only (same as Explore)Architecture planning, omitClaudeMd: true
VerificationinheritRead-only + Bash + WebFetchAdversarial testing, background: true
Claude Code GuidehaikuGlob/Grep/Read/WebFetch/WebSearchDocumentation lookup, permissionMode: 'dontAsk'
Statusline SetupsonnetRead/Edit onlyStatus line configuration, color: 'orange'

Read-only enforcement. Both Explore and Plan agents achieve read-only mode through disallowedTools, not through a special “read-only” flag:

src/tools/AgentTool/built-in/exploreAgent.ts
export const EXPLORE_AGENT: BuiltInAgentDefinition = {
agentType: 'Explore',
disallowedTools: [
AGENT_TOOL_NAME, // No spawning sub-agents
EXIT_PLAN_MODE_TOOL_NAME,
FILE_EDIT_TOOL_NAME, // No editing
FILE_WRITE_TOOL_NAME, // No writing
NOTEBOOK_EDIT_TOOL_NAME,
],
omitClaudeMd: true, // Saves ~5-15 Gtok/week across 34M+ Explore spawns
// ...
}

Token efficiency via omitClaudeMd. Read-only agents don’t need commit/PR/lint guidelines from CLAUDE.md. The omitClaudeMd flag strips this from the agent’s userContext, a significant saving at scale.

Adversarial prompting. The Verification agent has the most sophisticated system prompt (~130 lines) specifically designed to counter LLM tendencies to rubber-stamp results:

src/tools/AgentTool/built-in/verificationAgent.ts
const VERIFICATION_SYSTEM_PROMPT = `You are a verification specialist.
Your job is not to confirm the implementation works — it's to try to break it.
You have two documented failure patterns. First, verification avoidance:
when faced with a check, you find reasons not to run it...`

Custom agents are loaded from markdown files with frontmatter or JSON definitions. The loading pipeline in getAgentDefinitionsWithOverrides():

flowchart LR
A[loadMarkdownFilesForSubdir 'agents'] --> B[parseAgentFromMarkdown]
C[loadPluginAgents] --> D[Plugin agents]
E[getBuiltInAgents] --> F[Built-in agents]
B --> G[All agents list]
D --> G
F --> G
G --> H[getActiveAgentsFromList]
H --> I[Active agents<br/>deduplicated by priority]

When multiple agents share the same agentType, the priority chain determines which one wins:

// src/tools/AgentTool/loadAgentsDir.ts — getActiveAgentsFromList
const agentGroups = [
builtInAgents, // lowest priority
pluginAgents,
userAgents, // ~/.claude/agents/
projectAgents, // .claude/agents/ in repo
flagAgents, // feature-flag managed
managedAgents, // policy/enterprise managed (highest priority)
]
const agentMap = new Map<string, AgentDefinition>()
for (const agents of agentGroups) {
for (const agent of agents) {
agentMap.set(agent.agentType, agent) // Later groups overwrite earlier
}
}

This means a project-level agent definition can override a built-in agent, and a managed/policy agent can override everything.

A custom agent in .claude/agents/code-reviewer.md:

---
name: code-reviewer
description: "Reviews code changes for quality and correctness"
tools:
- Read
- Glob
- Grep
- Bash
model: inherit
permissionMode: bypassPermissions
maxTurns: 50
memory: project
---
You are a code review specialist. Review the provided code changes...

The frontmatter is parsed with Zod validation (AgentJsonSchema), and the markdown body becomes the system prompt. When memory is set, the system prompt is dynamically appended with agent-specific memory content:

getSystemPrompt: () => {
if (isAutoMemoryEnabled() && memory) {
return systemPrompt + '\n\n' + loadAgentMemoryPrompt(agentType, memory)
}
return systemPrompt
}

Agents can declare their own MCP server requirements:

// Two forms of MCP server spec
export type AgentMcpServerSpec =
| string // Reference by name: "slack"
| { [name: string]: McpServerConfig } // Inline: { "my-server": { command: "..." } }

Agents with requiredMcpServers are filtered out when their servers aren’t available:

export function hasRequiredMcpServers(
agent: AgentDefinition,
availableServers: string[],
): boolean {
return agent.requiredMcpServers.every(pattern =>
availableServers.some(server =>
server.toLowerCase().includes(pattern.toLowerCase()),
),
)
}

The Agent tool (src/tools/AgentTool/) is how the main agent spawns sub-agents. When the main Claude instance decides to use a sub-agent, it calls the Agent tool with a subagent_type parameter matching one of the active agent definitions. The tool then:

  1. Resolves the agent definition from activeAgents
  2. Constructs the system prompt via getSystemPrompt()
  3. Selects the model (inherit, specific ID, or default sub-agent model)
  4. Forks a new conversation with the agent’s tool set and prompt
  5. Returns the agent’s final response to the parent

This creates a clean parent→child relationship where each agent operates in its own context with its own constraints, while the parent retains full control over dispatch and result interpretation.