文档索引
在此获取完整文档索引:https://code.claude.com/docs/llms.txt 使用此文件发现所有可用页面,然后再进一步探索。
智能体循环的工作原理
了解驱动 SDK 智能体的消息生命周期、工具执行、上下文窗口和架构。
Agent SDK 让您可以将 Claude Code 的自主智能体循环嵌入到自己的应用程序中。SDK 是一个独立包,为您提供对工具、权限、成本限制和输出的编程控制。使用它不需要安装 Claude Code CLI。
当您启动智能体时,SDK 运行与 Claude Code 相同的执行循环:Claude 评估您的提示、调用工具执行操作、接收结果,然后重复直到任务完成。本页解释该循环内部发生的事情,以便您可以有效地构建、调试和优化智能体。
循环概览
每个智能体会话遵循相同的循环:
- 接收提示。 Claude 接收您的提示以及系统提示、工具定义和对话历史。SDK 生成一个包含会话元数据的
SystemMessage,子类型为"init"。 - 评估和响应。 Claude 评估当前状态并决定如何继续。它可能以文本响应、请求一个或多个工具调用,或两者兼有。SDK 生成一个包含文本和任何工具调用请求的
AssistantMessage。 - 执行工具。 SDK 运行每个请求的工具并收集结果。每组工具结果反馈给 Claude 以进行下一次决策。您可以使用钩子在工具运行前拦截、修改或阻止工具调用。
- 重复。 步骤 2 和 3 作为一个循环重复。每个完整循环是一个轮次。Claude 继续调用工具和处理结果,直到产生没有工具调用的响应。
- 返回结果。 SDK 生成一个包含文本响应(无工具调用)的最终
AssistantMessage,然后是一个包含最终文本、令牌使用量、成本和会话 ID 的ResultMessage。
一个简单的问题("这里有什么文件?")可能只需要一两个轮次来调用 Glob 并返回结果。一个复杂的任务("重构 auth 模块并更新测试")可以在多个轮次中链接数十个工具调用,读取文件、编辑代码和运行测试,Claude 根据每个结果调整其方法。
轮次和消息
轮次是循环内的一次往返:Claude 产生包含工具调用的输出,SDK 执行这些工具,结果自动反馈给 Claude。这发生在不将控制权返回给您的代码的情况下。轮次持续进行,直到 Claude 产生没有工具调用的输出,此时循环结束并交付最终结果。
考虑提示 "Fix the failing tests in auth.ts" 的完整会话可能是什么样子。
首先,SDK 将您的提示发送给 Claude 并生成带有会话元数据的 SystemMessage。然后循环开始:
- 轮次 1: Claude 调用
Bash运行npm test。SDK 生成带有工具调用的AssistantMessage,执行命令,然后生成带有输出(三个失败)的UserMessage。 - 轮次 2: Claude 对
auth.ts和auth.test.ts调用Read。SDK 返回文件内容并生成AssistantMessage。 - 轮次 3: Claude 调用
Edit修复auth.ts,然后调用Bash重新运行npm test。所有三个测试通过。SDK 生成AssistantMessage。 - 最终轮次: Claude 产生一个没有工具调用的纯文本响应:"Fixed the auth bug, all three tests pass now." SDK 生成带有此文本的最终
AssistantMessage,然后是带有相同文本加上成本和使用量的ResultMessage。
这是四个轮次:三个有工具调用,一个最终纯文本响应。
您可以使用 max_turns / maxTurns 限制循环,这仅计算工具使用轮次。例如,上面循环中的 max_turns=2 会在编辑步骤之前停止。您也可以使用 max_budget_usd / maxBudgetUsd 基于支出阈值限制轮次。
没有限制时,循环会运行到 Claude 自行完成,这对于范围明确的任务是好的,但对于开放式提示("改进此代码库")可能会运行很长时间。设置预算是生产智能体的良好默认值。有关选项参考,请参阅下面的轮次和预算。
消息类型
循环运行时,SDK 生成一系列消息流。每条消息携带一个类型,告诉您它来自循环的哪个阶段。五种核心类型是:
SystemMessage: 会话生命周期事件。subtype字段区分它们:"init"是第一条消息(会话元数据),"compact_boundary"在压缩后触发。在 TypeScript 中,压缩边界是其自己的SDKCompactBoundaryMessage类型,而不是SDKSystemMessage的子类型。AssistantMessage: 在每次 Claude 响应后发出,包括最终的纯文本响应。包含该轮的文本内容块和工具调用块。UserMessage: 在每次工具执行后发出,包含反馈给 Claude 的工具结果内容。也会在循环中途为任何用户输入发出。StreamEvent: 仅在启用部分消息时发出。包含原始 API 流式事件(文本增量、工具输入块)。请参阅流式响应。ResultMessage: 标记智能体循环的结束。包含最终文本结果、令牌使用量、成本和会话 ID。检查subtype字段以确定任务是成功还是达到限制。少量尾随系统事件(如prompt_suggestion)可能在其之后到达,因此请将流迭代到完成而不是在结果处中断。请参阅处理结果。
这五种类型涵盖了两个 SDK 中完整的智能体循环生命周期。TypeScript SDK 还生成额外的可观察性事件(钩子事件、工具进度、速率限制、任务通知),提供额外细节但不是驱动循环所必需的。完整列表请参阅 Python 消息类型参考和 TypeScript 消息类型参考。
处理消息
您处理哪些消息取决于您正在构建的内容:
- 仅最终结果: 处理
ResultMessage以获取输出、成本以及任务是否成功或达到限制。 - 进度更新: 处理
AssistantMessage以查看 Claude 每轮在做什么,包括调用了哪些工具。 - 实时流式传输: 启用部分消息(Python 中的
include_partial_messages,TypeScript 中的includePartialMessages)以实时获取StreamEvent消息。请参阅实时流式响应。
检查消息类型的方式取决于 SDK:
- Python: 使用
isinstance()对从claude_agent_sdk导入的类进行消息类型检查(例如,isinstance(message, ResultMessage))。 - TypeScript: 检查
type字符串字段(例如,message.type === "result")。AssistantMessage和UserMessage将原始 API 消息包装在.message字段中,因此内容块在message.message.content,而不是message.content。
示例:检查消息类型和处理结果
from claude_agent_sdk import query, AssistantMessage, ResultMessage
async for message in query(prompt="Summarize this project"):
if isinstance(message, AssistantMessage):
print(f"Turn completed: {len(message.content)} content blocks")
if isinstance(message, ResultMessage):
if message.subtype == "success":
print(message.result)
else:
print(f"Stopped: {message.subtype}")
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({ prompt: "Summarize this project" })) {
if (message.type === "assistant") {
console.log(`Turn completed: ${message.message.content.length} content blocks`);
}
if (message.type === "result") {
if (message.subtype === "success") {
console.log(message.result);
} else {
console.log(`Stopped: ${message.subtype}`);
}
}
}
工具执行
工具赋予您的智能体执行操作的能力。没有工具,Claude 只能以文本响应。有了工具,Claude 可以读取文件、运行命令、搜索代码以及与外部服务交互。
内置工具
SDK 包含与 Claude Code 相同的工具:
| 类别 | 工具 | 功能 |
|---|---|---|
| 文件操作 | Read、Edit、Write | 读取、修改和创建文件 |
| 搜索 | Glob、Grep | 按模式查找文件、使用正则表达式搜索内容 |
| 执行 | Bash | 运行 shell 命令、脚本、git 操作 |
| 网络 | WebSearch、WebFetch | 搜索网页、获取和解析页面 |
| 发现 | ToolSearch | 按需动态查找和加载工具,而不是预加载所有工具 |
| 编排 | Agent、Skill、AskUserQuestion、TaskCreate、TaskUpdate | 生成子智能体、调用技能、询问用户、跟踪任务 |
除了内置工具,您还可以:
工具权限
Claude 根据任务决定调用哪些工具,但您控制这些调用是否被允许执行。您可以自动批准特定工具、完全阻止其他工具,或要求对所有内容进行审批。三个选项共同决定运行什么:
allowed_tools/allowedTools自动批准列出的工具。一个在允许工具列表中有["Read", "Glob", "Grep"]的只读智能体无需提示即可运行这些工具。未列出的工具仍然可用但需要权限。disallowed_tools/disallowedTools阻止列出的工具,无论其他设置如何。有关工具运行前规则检查的顺序,请参阅权限。permission_mode/permissionMode控制未被允许或拒绝规则覆盖的工具会发生什么。有关可用模式,请参阅权限模式。
您还可以使用 "Bash(npm *)" 等规则对单个工具进行范围限定,仅允许特定命令。有关完整规则语法,请参阅权限。
当工具被拒绝时,Claude 会收到拒绝消息作为工具结果,通常会尝试不同的方法或报告无法继续。
并行工具执行
当 Claude 在单个轮次中请求多个工具调用时,两个 SDK 都可以根据工具并发或顺序运行它们。只读工具(如 Read、Glob、Grep 和标记为只读的 MCP 工具)可以并发运行。修改状态的工具(如 Edit、Write 和 Bash)顺序运行以避免冲突。
自定义工具默认为顺序执行。要为自定义工具启用并行执行,请在其注释中设置 readOnlyHint。TypeScript 和 Python SDK 都使用 MCP SDK 中的此字段名称。
控制循环运行方式
您可以限制循环的轮次、成本、Claude 推理的深度以及工具是否需要审批才能运行。所有这些都是 ClaudeAgentOptions(Python)/ Options(TypeScript)上的字段。
轮次和预算
| 选项 | 控制内容 | 默认值 |
|---|---|---|
最大轮次(max_turns / maxTurns) | 最大工具使用往返次数 | 无限制 |
最大预算(max_budget_usd / maxBudgetUsd) | 停止前的最大成本 | 无限制 |
当达到任一限制时,SDK 返回一个带有相应错误子类型(error_max_turns 或 error_max_budget_usd)的 ResultMessage。有关如何检查这些子类型,请参阅处理结果,有关语法请参阅 ClaudeAgentOptions / Options。
推理强度
effort 选项控制 Claude 应用多少推理。较低的强度级别每轮使用更少的令牌并降低成本。并非所有模型都支持 effort 参数。有关哪些模型支持它,请参阅 Effort。
| 级别 | 行为 | 适用于 |
|---|---|---|
"low" | 最小推理,快速响应 | 文件查找、列出目录 |
"medium" | 平衡推理 | 常规编辑、标准任务 |
"high" | 深入分析 | 重构、调试 |
"xhigh" | 扩展推理深度 | 编码和智能体任务;推荐在 Opus 4.7 上使用 |
"max" | 最大推理深度 | 需要深入分析的多步骤问题 |
如果您不设置 effort,Python SDK 会保持参数未设置并延迟到模型的默认行为。TypeScript SDK 默认为 "high"。
effort 在每个响应内以延迟和令牌成本换取推理深度。扩展思维是一个单独的功能,在输出中产生可见的思维链块。它们是独立的:您可以设置 effort: "low" 并启用扩展思维,或设置 effort: "max" 而不启用它。
对于执行简单、范围明确的任务(如列出文件或运行单个 grep)的智能体,使用较低的强度以减少成本和延迟。在顶层 query() 选项中为整个会话设置 effort,或通过 AgentDefinition 上的 effort 字段为每个子智能体设置以覆盖会话级别。
权限模式
权限模式选项(Python 中的 permission_mode,TypeScript 中的 permissionMode)控制智能体在使用工具前是否请求审批:
| 模式 | 行为 |
|---|---|
"default" | 未被允许规则覆盖的工具触发您的审批回调;无回调意味着拒绝 |
"acceptEdits" | 自动批准文件编辑和常见文件系统命令(mkdir、touch、mv、cp 等);其他 Bash 命令遵循默认规则 |
"plan" | 只读工具运行;Claude 探索并制定计划而不编辑您的源文件 |
"dontAsk" | 从不提示。由权限规则预批准的工具运行,其他一切都被拒绝 |
"auto"(仅 TypeScript) | 使用模型分类器批准或拒绝每个工具调用。有关可用性和行为,请参阅自动模式 |
"bypassPermissions" | 无需询问即可运行所有允许的工具。在 Unix 上以 root 运行时不能使用。仅在智能体操作无法影响您关心的系统的隔离环境中使用 |
对于交互式应用程序,使用带有工具审批回调的 "default" 来显示审批提示。对于开发机器上的自主智能体,"acceptEdits" 自动批准文件编辑和常见文件系统命令(mkdir、touch、mv、cp 等),同时仍将其他 Bash 命令置于允许规则之后。保留 "bypassPermissions" 用于 CI、容器或其他隔离环境。完整详情请参阅权限。
模型
如果您不设置 model,SDK 使用 Claude Code 的默认值,这取决于您的身份验证方法和订阅。显式设置它(例如,model="claude-sonnet-4-6")以固定特定模型或使用更小的模型以获得更快、更便宜的智能体。有关可用 ID,请参阅模型。
上下文窗口
上下文窗口是会话期间 Claude 可用的信息总量。它不会在会话内的轮次之间重置。一切都会累积:系统提示、工具定义、对话历史、工具输入和工具输出。跨轮次保持不变的内容(系统提示、工具定义、CLAUDE.md)会自动进行提示缓存,从而减少重复前缀的成本和延迟。
什么消耗上下文
以下是每个组件如何影响 SDK 中的上下文:
| 来源 | 加载时机 | 影响 |
|---|---|---|
| 系统提示 | 每次请求 | 小的固定成本,始终存在 |
| CLAUDE.md 文件 | 会话开始时,通过 settingSources | 每次请求中的完整内容(但经过提示缓存,因此只有第一次请求支付完整成本) |
| 工具定义 | 每次请求;MCP 模式默认延迟 | 内置工具模式每次请求加载。工具搜索默认延迟 MCP 工具模式,在 Vertex AI 或非第一方 ANTHROPIC_BASE_URL 上回退到预先加载。有关完整矩阵,请参阅配置工具搜索 |
| 对话历史 | 跨轮次累积 | 随每轮增长:提示、响应、工具输入、工具输出 |
| 技能描述 | 会话开始时,通过设置来源 | 简短摘要;完整内容仅在调用时加载 |
大型工具输出消耗大量上下文。读取大文件或运行带有详细输出的命令可以在单个轮次中使用数千个令牌。上下文跨轮次累积,因此具有许多工具调用的较长会话比短会话构建更多上下文。
自动压缩
当上下文窗口接近其限制时,SDK 自动压缩对话:它总结较旧的历史以释放空间,保持您最近的交流和关键决策不变。当这种情况发生时,SDK 在流中发出一条 type: "system" 和 subtype: "compact_boundary" 的消息(在 Python 中这是 SystemMessage;在 TypeScript 中它是单独的 SDKCompactBoundaryMessage 类型)。
压缩用摘要替换较旧的消息,因此对话早期的特定指令可能不会被保留。持久规则应放在 CLAUDE.md 中(通过 settingSources 加载),而不是在初始提示中,因为 CLAUDE.md 内容在每次请求时重新注入。
您可以通过多种方式自定义压缩行为:
- CLAUDE.md 中的摘要指令: 压缩器像读取任何其他上下文一样读取您的 CLAUDE.md,因此您可以包含一个告诉它在摘要时保留什么的部分。部分标题是自由格式的(不是魔法字符串);压缩器按意图匹配。
PreCompact钩子: 在压缩发生前运行自定义逻辑,例如存档完整记录。钩子接收trigger字段(manual或auto)。请参阅钩子。- 手动压缩: 发送
/compact作为提示字符串以按需触发压缩。以这种方式发送的命令是 SDK 输入,而不是仅 CLI 的快捷方式。请参阅 SDK 中的命令。
示例:CLAUDE.md 中的摘要指令
在项目的 CLAUDE.md 中添加一个告诉压缩器保留什么的部分。标题名称不特殊;使用任何清晰的标签。
# Summary instructions
When summarizing this conversation, always preserve:
- The current task objective and acceptance criteria
- File paths that have been read or modified
- Test results and error messages
- Decisions made and the reasoning behind them
保持上下文高效
长时间运行的智能体的一些策略:
- 使用子智能体处理子任务。 每个子智能体以全新的对话开始(没有先前的消息历史,尽管它确实加载了自己的系统提示和项目级上下文如 CLAUDE.md)。它看不到父级的轮次,只有其最终响应作为工具结果返回给父级。主智能体的上下文按该摘要增长,而不是按完整的子任务记录。有关详情,请参阅子智能体继承的内容。
- 选择性使用工具。 每个工具定义占用上下文空间。使用
AgentDefinition上的tools字段将子智能体限定到它们所需的最小集合。 - 注意 MCP 服务器成本。 MCP 工具搜索默认延迟 MCP 工具模式并按需加载。当工具搜索关闭、在 Vertex AI 上或在非第一方
ANTHROPIC_BASE_URL后面时,每个 MCP 服务器将其所有工具模式添加到每个请求中,因此几个具有许多工具的服务器可以在智能体做任何工作之前消耗大量上下文。 - 对常规任务使用较低的强度。 将强度设置为
"low",用于只需要读取文件或列出目录的智能体。这减少了令牌使用量和成本。
有关每功能上下文成本的详细细分,请参阅了解上下文成本。
会话和连续性
与 SDK 的每次交互都会创建或继续一个会话。从 ResultMessage.session_id(两个 SDK 中都可用)捕获会话 ID 以稍后恢复。TypeScript SDK 还将其作为 init SystemMessage 上的直接字段公开;在 Python 中它嵌套在 SystemMessage.data 中。
当您恢复时,先前轮次的完整上下文会被恢复:已读取的文件、已执行的分析和已采取的操作。您也可以分叉会话以分支到不同的方法而不修改原始会话。
有关恢复、继续和分叉模式的完整指南,请参阅会话管理。
在 Python 中,ClaudeSDKClient 自动处理跨多次调用的会话 ID。详情请参阅 Python SDK 参考。
处理结果
当循环结束时,ResultMessage 告诉您发生了什么并为您提供输出。subtype 字段(两个 SDK 中都可用)是检查终止状态的主要方式。
| 结果子类型 | 发生了什么 | result 字段是否可用? |
|---|---|---|
success | Claude 正常完成任务 | 是 |
error_max_turns | 在完成前达到 maxTurns 限制 | 否 |
error_max_budget_usd | 在完成前达到 maxBudgetUsd 限制 | 否 |
error_during_execution | 错误中断了循环(例如,API 失败或取消的请求) | 否 |
error_max_structured_output_retries | 结构化输出验证在配置的重试限制后失败 | 否 |
result 字段(最终文本输出)仅存在于 success 变体上,因此在读取它之前始终检查子类型。所有结果子类型都携带 total_cost_usd、usage、num_turns 和 session_id,以便您可以跟踪成本并在错误后恢复。在 Python 中,total_cost_usd 和 usage 被类型化为可选的,在某些错误路径上可能是 None,因此在格式化之前进行保护。有关解释 usage 字段的详情,请参阅跟踪成本和使用量。
结果还包括 stop_reason 字段(TypeScript 中的 string | null,Python 中的 str | None),指示模型在最终轮次停止生成的原因。常见值是 end_turn(模型正常完成)、max_tokens(达到输出令牌限制)和 refusal(模型拒绝了请求)。在错误结果子类型上,stop_reason 携带循环结束前最后一个助手响应的值。要检测拒绝,请检查 stop_reason === "refusal"(TypeScript)或 stop_reason == "refusal"(Python)。完整类型请参阅 SDKResultMessage(TypeScript)或 ResultMessage(Python)。
钩子
钩子是在循环中特定点触发的回调:工具运行前、返回后、智能体完成时等。一些常用的钩子是:
| 钩子 | 触发时机 | 常见用途 |
|---|---|---|
PreToolUse | 工具执行前 | 验证输入、阻止危险命令 |
PostToolUse | 工具返回后 | 审计输出、触发副作用 |
UserPromptSubmit | 发送提示时 | 向提示注入额外上下文 |
Stop | 智能体完成时 | 验证结果、保存会话状态 |
SubagentStart / SubagentStop | 子智能体生成或完成时 | 跟踪和聚合并行任务结果 |
PreCompact | 上下文压缩前 | 在摘要前存档完整记录 |
钩子在您的应用程序进程中运行,不在智能体的上下文窗口内,因此它们不消耗上下文。钩子还可以短路循环:拒绝工具调用的 PreToolUse 钩子会阻止其执行,Claude 会收到拒绝消息。
两个 SDK 都支持上述所有事件。TypeScript SDK 包含 Python 尚不支持的额外事件。有关完整事件列表、每个 SDK 的可用性和完整回调 API,请参阅使用钩子控制执行。
综合运用
此示例将本页的关键概念组合到一个修复失败测试的智能体中。它使用允许的工具(自动批准以便智能体自主运行)、项目设置以及轮次和推理强度的安全限制来配置智能体。循环运行时,它捕获会话 ID 以备恢复,处理最终结果,并打印总成本。
import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage
async def run_agent():
session_id = None
async for message in query(
prompt="Find and fix the bug causing test failures in the auth module",
options=ClaudeAgentOptions(
allowed_tools=[
"Read",
"Edit",
"Bash",
"Glob",
"Grep",
], # Listing tools here auto-approves them (no prompting)
setting_sources=[
"project"
], # Load CLAUDE.md, skills, hooks from current directory
max_turns=30, # Prevent runaway sessions
effort="high", # Thorough reasoning for complex debugging
),
):
# Handle the final result
if isinstance(message, ResultMessage):
session_id = message.session_id # Save for potential resumption
if message.subtype == "success":
print(f"Done: {message.result}")
elif message.subtype == "error_max_turns":
# Agent ran out of turns. Resume with a higher limit.
print(f"Hit turn limit. Resume session {session_id} to continue.")
elif message.subtype == "error_max_budget_usd":
print("Hit budget limit.")
else:
print(f"Stopped: {message.subtype}")
if message.total_cost_usd is not None:
print(f"Cost: ${message.total_cost_usd:.4f}")
asyncio.run(run_agent())
import { query } from "@anthropic-ai/claude-agent-sdk";
let sessionId: string | undefined;
for await (const message of query({
prompt: "Find and fix the bug causing test failures in the auth module",
options: {
allowedTools: ["Read", "Edit", "Bash", "Glob", "Grep"], // Listing tools here auto-approves them (no prompting)
settingSources: ["project"], // Load CLAUDE.md, skills, hooks from current directory
maxTurns: 30, // Prevent runaway sessions
effort: "high" // Thorough reasoning for complex debugging
}
})) {
// Save the session ID to resume later if needed
if (message.type === "system" && message.subtype === "init") {
sessionId = message.session_id;
}
// Handle the final result
if (message.type === "result") {
if (message.subtype === "success") {
console.log(`Done: ${message.result}`);
} else if (message.subtype === "error_max_turns") {
// Agent ran out of turns. Resume with a higher limit.
console.log(`Hit turn limit. Resume session ${sessionId} to continue.`);
} else if (message.subtype === "error_max_budget_usd") {
console.log("Hit budget limit.");
} else {
console.log(`Stopped: ${message.subtype}`);
}
console.log(`Cost: ${message.total_cost_usd.toFixed(4)}`);
}
}
后续步骤
现在您已经了解了循环,以下是根据您正在构建的内容的去向:
- 还没有运行过智能体? 从快速入门开始,安装 SDK 并查看完整的端到端运行示例。
- 准备好接入您的项目? 加载 CLAUDE.md、技能和文件系统钩子,让智能体自动遵循您的项目规范。
- 构建交互式 UI? 启用流式传输以在循环运行时显示实时文本和工具调用。
- 需要对智能体可以做什么进行更严格的控制? 使用权限锁定工具访问,并使用钩子在执行前审计、阻止或转换工具调用。
- 运行长时间或昂贵的任务? 将隔离的工作卸载到子智能体以保持主上下文精简。
有关智能体循环的更广泛概念图(非 SDK 特定),请参阅 Claude Code 的工作原理。