Claude Code 学习站

Agent SDK 参考:Agent Loop(智能体循环)工作原理

介绍 Claude Agent SDK 中智能体循环的消息生命周期、工具执行、上下文窗口管理与控制选项,附 TypeScript/Python 对照表与代码示例。

本页目录24
AI 摘要 · 已核查整理于 2026-08-16原文:How the agent loop works(Anthropic)Claude Agent SDK智能体循环TypeScript/Python上下文管理
要点速览
  • 循环核心是:接收 prompt → Claude 评估并可能请求工具调用 → SDK 执行工具并把结果喂回 → 重复直到 Claude 给出无工具调用的文本响应 → 返回 ResultMessage
  • 五种核心消息类型:SystemMessage、AssistantMessage、UserMessage、StreamEvent、ResultMessage,其中 ResultMessage 的 subtype 字段决定任务是否成功以及 result 字段是否可用
  • 可用 max_turns/maxTurns 和 max_budget_usd/maxBudgetUsd 限制回合数与花费上限,命中限制会返回对应的 error_max_turns / error_max_budget_usd 结果
  • permission_mode/permissionMode 提供 default、acceptEdits、plan、dontAsk、auto、bypassPermissions 六种模式,决定工具调用是否需要人工批准
  • 上下文窗口在会话内不会重置,系统提示词与工具定义等静态内容会被 prompt cache;临近上限时 SDK 会自动压缩(compaction),可通过 CLAUDE.md 指令、PreCompact 钩子或手动发送 /compact 定制
  • 子代理(subagent)以全新对话上下文启动,只把最终结果作为工具结果返回给父级,是控制上下文膨胀的关键手段

本文是对 Claude Agent SDK 官方文档页面「How the agent loop works」的中文整理,完整与最新内容以原文为准:https://code.claude.com/docs/en/agent-sdk/agent-loop

概述

Agent SDK 让你可以在自己的应用中嵌入 Claude Code 的自主智能体循环(agent loop)。该 SDK 是一个独立的包,提供了对工具、权限、成本限制和输出的编程控制。

TypeScript 和 Python 两种 SDK 都自带原生的 Claude Code 二进制文件,因此大多数安装不需要单独安装 Claude Code(需要单独安装的情况见 quickstart 的安装说明)。

启动一个 agent 时,SDK 会运行与 Claude Code 相同的执行循环:Claude 评估你的 prompt,调用工具执行动作,接收结果,并重复此过程直到任务完成。本文解释该循环内部发生了什么,以便你能有效地构建、调试和优化你的 agent。

循环概览

每个 agent 会话都遵循相同的周期:

  1. 接收 prompt。 Claude 接收你的 prompt,以及系统提示词、工具定义和对话历史。SDK 会产出一条 subtype 为 "init"SystemMessage,其中包含会话元数据。
  2. 评估并响应。 Claude 评估当前状态并决定如何继续。它可能以文本响应、请求一个或多个工具调用,或两者兼有。SDK 产出一条 AssistantMessage,包含文本内容和任何工具调用请求。
  3. 执行工具。 SDK 运行每个被请求的工具并收集结果。每一组工具结果会反馈给 Claude 用于下一步决策。你可以使用 hooks 来拦截、修改或阻止工具调用的执行。
  4. 重复。 步骤 2 和 3 循环往复。每个完整周期称为一个 turn(回合)。Claude 会持续调用工具并处理结果,直到它产出一个不包含工具调用的响应。
  5. 返回结果。 SDK 产出最后一条不含工具调用的文本 AssistantMessage,随后产出一条 ResultMessage,包含最终文本、token 用量、成本和会话 ID。

一个简单的问题(「这里有哪些文件?」)可能只需一两个回合调用 Glob 并返回结果。一个复杂任务(「重构 auth 模块并更新测试」)则可能跨多个回合链式调用数十次工具,读取文件、编辑代码、运行测试,Claude 会根据每次结果调整方案。

回合(Turn)与消息

一个 turn 是循环内的一次往返:Claude 产出包含工具调用的输出,SDK 执行这些工具,结果自动反馈给 Claude。这个过程不会把控制权交还给你的代码。回合会持续进行,直到 Claude 产出不含工具调用的输出,此时循环结束并返回最终结果。

以 prompt「Fix the failing tests in auth.ts」为例,一个完整会话大致如下:

SDK 首先把 prompt 发给 Claude,并产出一条含会话元数据的 SystemMessage。随后循环开始:

  1. Turn 1: Claude 调用 Bash 运行 npm test。SDK 产出一条带工具调用的 AssistantMessage,执行命令,然后产出一条 UserMessage,内容是输出(三个失败)。
  2. Turn 2: Claude 对 auth.tsauth.test.ts 调用 Read。SDK 返回文件内容并产出一条 AssistantMessage
  3. Turn 3: Claude 调用 Edit 修复 auth.ts,然后调用 Bash 重新运行 npm test。三个测试全部通过。SDK 产出一条 AssistantMessage
  4. 最后一个回合: Claude 产出纯文本响应,不含工具调用:「Fixed the auth bug, all three tests pass now.」SDK 产出最后一条含该文本的 AssistantMessage,随后产出一条 ResultMessage,包含相同文本以及成本和用量信息。

这一共是四个回合:三个含工具调用,一个纯文本回应。

可以用 max_turns / maxTurns 给循环设置上限,该值只统计含工具调用的回合。例如上例中 max_turns=2 会在编辑步骤之前就停止。也可以用 max_budget_usd / maxBudgetUsd 基于花费阈值限制回合。

若不设限制,循环会一直运行到 Claude 自行完成为止;这对范围明确的任务没问题,但在开放式 prompt(如「改进这个代码库」)上可能运行很久。为生产环境的 agent 设置预算是一个好的默认做法。选项参考见下文的「回合与预算」。

消息类型

循环运行期间,SDK 会产出一串消息流。每条消息都带有一个 type,标识它来自循环的哪个阶段。五种核心类型是:

  • SystemMessage: 会话生命周期事件。subtype 字段用于区分:

    • "init":本次运行的会话元数据。当 SessionStartSetup 钩子在会话启动期间运行时,其 hook 生命周期消息会先于 init 消息到达
    • "compact_boundary":在压缩(compaction)之后触发
    • "informational":来自循环的纯文本状态提示
    • "worker_shutting_down":由于宿主退出或 Remote Control 断开连接,循环将在当前回合结束后终止

    在 TypeScript 中,除 "init" 外的每个 subtype 在 SDKMessage 联合类型中都是独立的类型,而不是 SDKSystemMessage 的子类型。

  • AssistantMessage: 在 Claude 每次响应后产出,包括最后一条纯文本响应。包含该回合的文本内容块和工具调用块。

  • UserMessage: 在每次工具执行后产出,携带发回给 Claude 的工具结果内容。你在循环中途流式发送的任何用户输入也会产出此类型。

  • StreamEvent: 仅在启用了 partial messages 时产出,包含原始 API 流式事件(文本增量、工具输入片段)。

  • ResultMessage: 标志 agent 循环结束。包含最终文本结果、token 用量、成本和会话 ID。检查 subtype 字段以判断任务是成功还是触发了限制。少量后续系统事件(如 prompt_suggestion)可能在它之后到达,因此应遍历流直到结束,而不是在拿到 result 后就 break。

这五种类型覆盖了 agent 循环的完整生命周期。两种 SDK 还会产出诸如速率限制状态、任务通知等可观测性事件,这些不是驱动循环所必需的。完整列表见 Python 和 TypeScript 各自的消息类型参考。

处理消息

你需要处理哪些消息取决于你在构建什么:

  • 仅需最终结果: 处理 ResultMessage 以获取输出、成本,以及任务是成功还是触发了限制。
  • 进度更新: 处理 AssistantMessage 以查看 Claude 每个回合在做什么,包括调用了哪些工具。
  • 实时流式输出: 启用 partial messages(Python 中为 include_partial_messages,TypeScript 中为 includePartialMessages)以实时获取 StreamEvent 消息。

如何检查消息类型因 SDK 而异:

  • Python:isinstance() 对照从 claude_agent_sdk 导入的类来检查消息类型(例如 isinstance(message, ResultMessage))。
  • TypeScript: 检查字符串字段 type(例如 message.type === "result")。AssistantMessageUserMessage 把原始 API 消息包装在 .message 字段中,因此内容块位于 message.message.content,而不是 message.content

示例:检查消息类型并处理结果

import asyncio
from claude_agent_sdk import query, AssistantMessage, ResultMessage


async def main():
    try:
        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}")
    except Exception as error:
        # A single-shot query() raises after yielding an error result. If the
        # failure was an error result, the error subtype branches above have
        # already run; connection or process failures yield no result message.
        print(f"Session ended with an error: {error}")


asyncio.run(main())
import { query } from "@anthropic-ai/claude-agent-sdk";

try {
  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}`);
      }
    }
  }
} catch (error) {
  // A single-shot query() throws after yielding an error result. If the
  // failure was an error result, the error subtype branches above have
  // already run; connection or process failures yield no result message.
  console.log(`Session ended with an error: ${error}`);
}

工具执行

工具赋予你的 agent 采取行动的能力。没有工具,Claude 只能用文本响应。有了工具,Claude 可以读取文件、运行命令、搜索代码、与外部服务交互。

内置工具

SDK 内置了与 Claude Code 相同的工具:

类别工具作用
文件操作ReadEditWrite读取、修改和创建文件
搜索GlobGrep按模式查找文件、用正则搜索内容
执行Bash运行 shell 命令、脚本、git 操作
WebWebSearchWebFetch搜索网页、抓取并解析页面
发现ToolSearch按需动态查找并加载工具,而不是预先加载全部工具
编排AgentSkillAskUserQuestionTaskCreateTaskUpdate派生子代理、调用 skill、询问用户、跟踪任务

对于不提供任务跟踪工具的模型,Claude Code 仅在你显式选择启用(opt in)时才提供 TaskCreateTaskUpdate

除内置工具外,你还可以:

  • MCP servers 连接外部服务(数据库、浏览器、API)
  • 自定义工具处理器定义自定义工具
  • 通过 setting sources 加载项目级 skill,以复用工作流

工具权限

Claude 根据任务决定调用哪些工具,但你控制这些调用是否被允许执行。你可以自动批准特定工具、完全阻止某些工具,或要求对所有工具进行审批。三个选项协同起作用来决定运行什么:

  • allowed_tools / allowedTools 自动批准列出的工具。一个只读 agent 的允许列表若为 ["Read", "Glob", "Grep"],这些工具会在不提示的情况下运行。未列出的工具仍然可用,但需要权限。
  • disallowed_tools / disallowedTools 阻止列出的工具,无论其他设置如何。工具运行前规则的检查顺序见 Permissions 文档。
  • permission_mode / permissionMode 控制你想要多少人工监督。SDK 会按固定顺序,将当前模式与你的 allow/deny 规则一起评估,见「权限评估方式」部分。可用模式见下文「权限模式」。

你还可以用类似 "Bash(npm *)" 的规则对单个工具进行更细粒度的限定,仅允许特定命令。完整规则语法见 Permissions 文档。

工具被拒绝时,Claude 会收到一条拒绝消息作为工具结果,通常会尝试不同的方法或报告无法继续。

并行工具执行

当 Claude 在单个回合中请求多个工具调用时,两种 SDK 都可能并发或顺序执行它们,具体取决于工具类型。只读工具(如 ReadGlobGrep,以及标记为只读的 MCP 工具)可以并发运行。会修改状态的工具(如 EditWriteBash)会顺序执行以避免冲突。

自定义工具默认顺序执行。要为自定义工具启用并行执行,需要在其 annotations 中设置 readOnlyHint。TypeScript 和 Python SDK 都使用这个来自 MCP SDK 的字段名。

控制循环如何运行

你可以限制循环的回合数、花费、Claude 推理的深度,以及工具是否需要审批才能运行。这些都是 ClaudeAgentOptions(Python)/ Options(TypeScript)上的字段。

回合与预算

选项控制什么默认值
Max turns(max_turns / maxTurns)最大工具调用往返次数无限制
Max budget(max_budget_usd / maxBudgetUsd)停止前的最大花费无限制

任一限制被触发时,SDK 会返回一条带相应错误 subtype(error_max_turnserror_max_budget_usd)的 ResultMessage。如何检查这些 subtype 见「处理结果」部分,语法见 ClaudeAgentOptions / Options

预算上限也覆盖子代理(subagent):它们的花费计入总额。一旦花费达到上限,再派生一个子代理会失败,报错 Budget limit reached,Claude Code 会停止仍在运行的后台子代理。这些上限强制行为需要 Claude Code v2.1.217 或更高版本。

使用流式输入(streaming input)时,如果你在某个回合仍在运行时发送一条消息,该消息会在该回合因 max-turns 限制结束时保持排队,并作为一个拥有自己 max-turns 限制的新回合启动。

推理强度(Effort level)

effort 选项控制 Claude 应用多少推理。更低的 effort 级别每回合使用更少的 token,降低成本。并非所有模型都支持 effort 参数,支持情况见官方 Effort 文档。

级别行为适用场景
"low"最少推理,快速响应文件查找、列目录
"medium"均衡推理常规编辑、标准任务
"high"深入分析重构、调试
"xhigh"扩展推理深度编码与 agentic 任务;在 Fable 5、Opus 4.7+ 和 Sonnet 5 上推荐
"max"最大推理深度需要深度分析的多步骤问题

如果不设置 effort,两种 SDK 都不会设置该参数,而是沿用模型的默认行为。

说明: effort 是在每次响应内用延迟和 token 成本换取推理深度。Extended thinking(扩展思考)是一个独立功能,会产出 thinking 内容块;ThinkingConfig 上的 display 字段(Python/TypeScript)控制你是否接收其文本。两者相互独立:你可以在启用 extended thinking 的同时设置 effort: "low",也可以在不启用它的情况下设置 effort: "max"

对于做简单、范围明确任务(如列文件或运行单个 grep)的 agent,使用更低的 effort 以降低成本和延迟。可以在顶层 query() 选项中为整个会话设置 effort,也可以在 AgentDefinitioneffort 字段上针对单个子代理覆盖会话级设置。

权限模式(Permission mode)

权限模式选项(Python 中为 permission_mode,TypeScript 中为 permissionMode)控制 agent 在使用工具前是否需要请求批准:

模式行为使用场景
"default"未被 allow 规则覆盖的工具会触发你的 canUseTool 回调;没有回调则默认拒绝带有自定义审批回调的交互式应用
"acceptEdits"自动批准文件编辑和常见文件系统命令(mkdirtouchmvcp 等);其他 Bash 命令遵循默认规则你信任 Claude 的编辑并想要更快的迭代,比如原型开发或在隔离目录中工作时
"plan"Claude 探索和规划,但不编辑你的源文件;文件编辑永远不会自动批准,会通过你的 canUseTool 回调提示你希望 Claude 提出变更方案而不实际执行,比如代码审查,或需要在变更执行前先批准的场景
"dontAsk"从不提示。被权限规则预先批准的工具会运行;其他一律拒绝。AskUserQuestion、被组织设为 ask 的连接器工具,以及被标记 requiresUserInteraction 的 MCP 工具,即使你已允许,也会被拒绝你希望为无头(headless)agent 提供固定、明确的工具范围,并倾向于硬拒绝而不是默认在 canUseTool 缺失时静默处理
"auto"使用模型分类器来批准或拒绝权限提示仍希望在工具使用上保留安全护栏的自主 agent
"bypassPermissions"运行所有被允许的工具而不询问,除了被明确 ask 规则匹配的工具、被组织设为 ask 的连接器工具,以及需要用户交互的工具。跨会话消息安全防护措施仍然适用。TypeScript SDK 中还需要在 options 中设置 allowDangerouslySkipPermissions: true。在 Unix 上以 root 运行时不可用CI、容器或其他隔离环境

对于交互式应用,建议使用 "default" 配合工具审批回调来展示审批提示。对于开发机上的自主 agent,"acceptEdits" 会自动批准文件编辑和常见文件系统命令(mkdirtouchmvcp 等),同时仍将其他 Bash 命令置于 allow 规则的把关之下。"bypassPermissions" 应仅保留给 CI、容器或其他隔离环境使用。

模型(Model)

如果不设置 model,SDK 会使用 Claude Code 的默认模型,该默认值取决于你的认证方式和订阅情况。可显式设置(例如 model="claude-sonnet-5")以固定特定模型,或使用更小的模型来获得更快、更便宜的 agent。可用的模型 ID 见官方 models 文档。

上下文窗口

上下文窗口是会话期间 Claude 可用的信息总量。它在会话内的各回合之间不会重置。一切都会累积:系统提示词、工具定义、对话历史、工具输入和工具输出。在各回合间保持不变的内容(系统提示词、工具定义、CLAUDE.md)会被自动进行 prompt caching,从而降低重复前缀的成本和延迟。

什么会消耗上下文

SDK 中各组件对上下文的影响如下:

来源何时加载影响
系统提示词每次请求固定的小开销,始终存在
CLAUDE.md 文件会话开始时,通过 settingSources每次请求都携带完整内容(但会被 prompt-cache,因此只有第一次请求承担完整成本)
工具定义每次请求;MCP schema 默认延迟加载内置工具 schema 每次请求都会加载。Tool search 默认延迟加载 MCP 工具 schema,在不支持的模型和某些平台上会回退为提前加载
对话历史随回合累积每个回合都会增长:prompt、响应、工具输入、工具输出
Skill 描述会话开始时,通过 setting sources简短摘要;完整内容仅在被调用时加载

大的工具输出会消耗大量上下文。读取一个大文件或运行输出冗长的命令,可能在单个回合中就消耗数千个 token。上下文会随回合累积,因此调用工具较多的长会话比短会话累积明显更多的上下文。

自动压缩(Compaction)

当上下文窗口接近上限时,SDK 会自动压缩对话:将较早的历史总结,以腾出空间,同时保留最近的交流和关键决策。压缩发生时,SDK 会在消息流中产出一条 type: "system"subtype: "compact_boundary" 的消息(Python 中是 SystemMessage;TypeScript 中是独立的 SDKCompactBoundaryMessage 类型)。

压缩会用摘要替换较早的消息,因此对话早期的具体指示可能不会被保留。持久性规则应放在 CLAUDE.md 中(通过 settingSources 加载),而不是放在初始 prompt 中,因为 CLAUDE.md 内容会在每次请求中被重新注入。

你可以通过以下几种方式自定义压缩行为:

  • CLAUDE.md 中的摘要指令: 压缩器会像读取其他上下文一样读取你的 CLAUDE.md,因此可以加入一段告诉它在摘要时应保留什么内容。压缩器按意图匹配,因此该段的标题可以自由命名。
  • PreCompact 钩子: 在压缩发生前运行自定义逻辑,例如归档完整的会话记录。该钩子接收一个 trigger 字段(manualauto)。
  • 手动压缩:/compact 作为 prompt 字符串发送以按需触发压缩。以这种方式发送的命令属于普通的 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

保持上下文高效

对长时间运行的 agent 的几点策略:

  • 用子代理处理子任务。 每个子代理都以全新对话开始(没有先前的消息历史,但会加载自己的系统提示词和项目级上下文,如 CLAUDE.md)。它看不到父级的回合,只有它的最终响应会作为工具结果返回给父级。主 agent 的上下文只会因这条摘要而增长,而不是完整的子任务记录。
  • 对工具保持精简。 每个工具定义都会占用上下文空间。用 AgentDefinitiontools 字段,把子代理限定在其所需的最小工具集内。
  • 留意 MCP server 的开销。 MCP tool search 默认延迟加载 MCP 工具 schema 并按需加载。当 tool search 关闭或已回退为提前加载时,每个 MCP server 会把所有工具 schema 加进每次请求,因此几个工具很多的 server 就可能在 agent 开始工作前消耗大量上下文。
  • 对常规任务使用更低的 effort。 对只需要读文件或列目录的 agent,将 effort 设为 "low",以降低 token 用量和成本。

会话与连续性

每次与 SDK 的交互都会创建或延续一个会话(session)。从 ResultMessage.session_id(两种 SDK 都可用)获取会话 ID 以便之后恢复。TypeScript SDK 还在 init SystemMessage 上直接暴露该字段;Python 中它嵌套在 SystemMessage.data 里。

恢复会话时,先前回合的完整上下文会被恢复:已读取的文件、已执行的分析、已采取的动作。你也可以 fork 一个会话,以分支出不同的方案而不修改原会话。

要跨无状态容器或 serverless 主机恢复会话,可传入一个 session_store / sessionStore 适配器,让 SDK 把会话记录镜像到你自己的后端,供另一台主机恢复。Claude Code 子进程仍会先写入本地磁盘。

说明: 在 Python 中,ClaudeSDKClient 会在多次调用间自动处理会话 ID。

处理结果(Result)

循环结束时,ResultMessage 会告诉你发生了什么并给出输出。subtype 字段(两种 SDK 都有)是检查终止状态的主要方式。

Result subtype发生了什么result 字段是否可用?
successClaude 正常完成任务
error_max_turns在完成前触发了 maxTurns 限制
error_max_budget_usd在完成前触发了 maxBudgetUsd 限制
error_during_execution循环被错误中断(例如 API 故障或请求被取消)
error_max_structured_output_retries在配置的重试次数内没有产出有效的结构化输出:每次尝试都未通过校验,或某次模型 fallback 撤回了已完成的输出且没有后续成功重试

result 字段包含最终文本输出,仅在 success 变体中存在,因此读取前务必先检查 subtype。

所有 result subtype 都携带 total_cost_usdusagenum_turnssession_id,以便即使出错后也能追踪成本并恢复会话。需要注意两点:

  • 会话崩溃后,最终结果是一条 error_during_execution,其成本字段可能被清零,stop_reasonnull,进程会在产出该消息后退出。
  • 在 Python 中,total_cost_usdusagemodel_usage 的类型是可选的,读取前需检查它们不是 None

usage 字段仅覆盖主 agent 循环。若需要整棵调用树的 token 和成本统计,请使用 modelUsage(Python 中为 model_usage)。

说明: 当查询以错误结果结束时:

  • 单次调用的 query() 会先产出最终结果消息,然后抛出一个包含失败文本的错误,如 Reached maximum number of turns。这个抛出是有意为之的;如果你的代码需要在其后继续执行,请用 try 块包裹循环。底层的 Claude Code 进程也会以非零码退出。
  • 流式输入(streaming input)会话会保持存活,你可以继续发送消息,除非发生会话崩溃——那会产出最终的 error_during_execution 结果并退出进程。

result 还包含一个 stop_reason 字段(TypeScript 中为 string | null,Python 中为 str | None),表示模型在最后一个回合为何停止生成。常见取值有 end_turn(模型正常完成)、max_tokens(达到输出 token 上限)和 refusal(模型拒绝了该请求)。对于循环产生的错误结果,stop_reason 携带循环结束前最后一次 assistant 响应的值;而会话崩溃后 Claude Code 合成的结果中,stop_reasonnull

要检测拒绝(refusal),检查 stop_reason === "refusal"(TypeScript)或 stop_reason == "refusal"(Python)。

钩子(Hooks)

Hooks 是在循环特定节点触发的回调:工具运行前、返回后、agent 完成时等等。一些常用的钩子:

Hook何时触发常见用途
PreToolUse工具执行前校验输入、阻止危险命令
PostToolUse工具返回后审计输出、触发副作用
UserPromptSubmitprompt 被发送时向 prompt 中注入额外上下文
Stopagent 完成时校验结果、保存会话状态
SubagentStart / SubagentStop子代理派生或完成时跟踪并汇总并行任务结果
PreCompact上下文压缩前在摘要前归档完整会话记录

Hooks 运行在你的应用进程中,而不是在 agent 的上下文窗口内,因此不会消耗上下文。Hooks 还可以使循环短路:一个拒绝工具调用的 PreToolUse 钩子会阻止该调用执行,Claude 会收到拒绝消息而不是执行结果。

两种 SDK 都支持上述所有事件。TypeScript SDK 还包含一些 Python 尚不支持的额外事件。

综合示例

以下示例把本文的关键概念整合进一个用于修复失败测试的 agent。它为 agent 配置了允许工具列表(自动批准,使 agent 自主运行)、项目设置,以及回合数和推理强度上的安全限制。循环运行期间,它捕获会话 ID 以便后续恢复,处理最终结果,并打印总成本。

由于单次调用的 query() 会在产出错误结果后抛出异常,循环被包裹在 try 块中,以便命中限制时脚本能干净退出。

import asyncio
from claude_agent_sdk import query, ClaudeAgentOptions, ResultMessage


async def run_agent():
    session_id = None

    try:
        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}")
    except Exception as error:
        # A single-shot query() raises after yielding an error result. If the
        # failure was an error result, the error subtype branches above have
        # already run; connection or process failures yield no result message.
        print(f"Session ended with an error: {error}")


asyncio.run(run_agent())
import { query } from "@anthropic-ai/claude-agent-sdk";

let sessionId: string | undefined;

try {
  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)}`);
    }
  }
} catch (error) {
  // A single-shot query() throws after yielding an error result. If the
  // failure was an error result, the error subtype branches above have
  // already run; connection or process failures yield no result message.
  console.log(`Session ended with an error: ${error}`);
}

agent 成功完成时,示例会打印一行 Done: 及 agent 对修复的摘要,随后打印类似 Cost: $0.0312 的一行。

后续步骤

理解了这个循环之后,根据你在构建什么,可以继续阅读:

  • 还没运行过 agent? 从 quickstart 开始,安装 SDK 并看一个端到端运行的完整示例。
  • 准备接入你的项目? 加载 CLAUDE.md、skills 和文件系统钩子,让 agent 自动遵循你的项目约定。
  • 在构建交互式 UI? 启用 streaming,在循环运行时实时展示文本和工具调用。
  • 需要更严格地控制 agent 能做什么? 用 permissions 锁定工具访问,并用 hooks 在工具调用执行前审计、阻止或转换它们。
  • 运行长时间或高成本的任务? 把独立的工作交给 subagents,以保持主上下文精简。
  • 要部署为服务? 参见 Hosting the Agent SDK 了解容器和 serverless 部署指南,以及 Session storage 了解如何把会话持久化到你自己的后端。