工具循环是受控委托
Claude 可以提出动作建议。你的应用校验请求、授予能力、观察结果,并决定循环是否继续。
类型: Build 语言: Python 前置要求: Messages API 是一台状态机、结构化输出是不可信的契约预计时间: 约 130 分钟
学习目标
- 实现完整的
tool_use与tool_result协议循环 - 设计聚焦的工具契约,并选择其执行边界
- 将模型选择工具与确定性授权分开
- 比较手写循环、SDK Tool Runner 与托管 agent
- 将失败作为带类型的结果返回,并消费可操作的运行时事件
- 限制自主性;路径已知时选择固定工作流
重复付款的 Agent
一个账单助手收到“退还重复扣款”。Claude 请求 issue_refund,应用执行它。最终文本到达前响应连接中断,应用重试整个回合,Claude 再次请求该工具,客户收到了两次退款。
应用把语言生成与事务控制混为一谈,最终造成重复付款。
可靠的工具循环有两份契约:
- 模型可以用结构化参数提出一个具名能力。
- 确定性应用代码决定该能力是否、如何以及最多执行几次。
工具使用让 Claude 能触及能力,却不赋予 Claude 权限。
框架之前的线协议契约
客户端工具在请求中声明。每项声明向模型提供名称、描述和 JSON Schema 输入契约。
{
"name": "lookup_order",
"description": "Look up one order by its exact public order ID. Returns status and last update. This tool never changes an order.",
"input_schema": {
"type": "object",
"required": ["order_id"],
"additionalProperties": false,
"properties": {
"order_id": {
"type": "string",
"description": "Order ID in the form A-12345"
}
}
}
}Claude 可能返回:
{
"stop_reason": "tool_use",
"content": [
{
"type": "tool_use",
"id": "toolu_7f3",
"name": "lookup_order",
"input": {"order_id": "A-12345"}
}
]
}客户端保留这整段 assistant 内容,校验并运行工具,再追加:
{
"role": "user",
"content": [
{
"type": "tool_result",
"tool_use_id": "toolu_7f3",
"content": "{\"found\":true,\"status\":\"in_transit\"}"
}
]
}匹配 ID 用来把结果关联到具体请求。根据协议预期的对话历史,assistant 消息必须紧挨在结果序列之前。
请参阅实现客户端工具,了解当前 SDK 与 API 形态。
循环具有显式状态
stateDiagram-v2
[*] --> AskModel
AskModel --> InspectStopReason
InspectStopReason --> ValidateFinal: end_turn
InspectStopReason --> ValidateCalls: tool_use
InspectStopReason --> RecoverOrStop: other reason
ValidateCalls --> AuthorizeCalls
AuthorizeCalls --> ExecuteCalls: allowed
AuthorizeCalls --> ReturnDenial: denied
ExecuteCalls --> ReturnResults
ReturnDenial --> AppendResults
ReturnResults --> AppendResults
AppendResults --> CheckBudgets
CheckBudgets --> AskModel: budget remains
CheckBudgets --> Escalate: budget exhausted
ValidateFinal --> [*]
Escalate --> [*]每次状态转换都可能失败:响应可能遗漏工具 ID、工具名可能未知、参数可能违反 schema、授权可能拒绝调用、处理程序可能超时、结果可能过大,Claude 可能继续请求工具,最终答案也可能不符合输出契约。
不要用一个宽泛的 try/except 加通用重试隐藏这些状态。应分类,并按失败类别选择恢复方式。
工具设计就是接口设计
Claude 根据工具接口选择工具。人不读处理程序也应能判断每个工具何时使用。
每个工具只做一件事
manage_customer 很含糊:它可能搜索、编辑、退款、暂停或删除。窄工具目录更易选择,也更容易保护:
get_customer_profilelist_customer_invoicespropose_refundissue_approved_refund
提案与执行之间的分离很重要。低风险工具可以计算建议金额;高风险工具需要模型之外生成的、已认证的审批 token。
写选择描述,不写内部文档
有用的描述会说明工具做什么、何时用、何时不用以及结果意味着什么。粘贴整本 API 手册没有帮助。
差:
Calls GET /v3/orders/{id} in the Commerce service.更好:
Read the current status of one existing order from the commerce system.
Use only when the user supplies an exact order ID. This tool is read-only.
Do not use it to search by email or to modify shipment details.描述中的示例能澄清棘手格式,但每个 token 都会随工具目录重复。衡量示例是否足以改善选择,从而值得付出上下文成本。
让无效调用难以表达
使用枚举、必填字段、边界和 additionalProperties: false;拆分互斥模式。若狭窄的领域值足以满足需求,不要使用自由形式 shell 命令、SQL、URL 或文件系统路径。
schema 引导生成,处理程序仍要校验。绝不能因为模型输入由 schema 生成,就认为它安全。
保持工具目录小且差异明显
更多工具不总会产生更多能力。重叠名称和冗长目录会造成选择歧义,并消耗上下文。
从真实任务需要的最少工具开始。评估发现能力缺口时再添加;轨迹显示混淆时删除或合并。
可以问:
- 两个工具从名称和描述看是否可互换?
- 通用代码或 CLI 工具是否已能在 sandbox 中完成此任务?
- agent 是否每一回合都需要这项能力?
- 该能力能否放进 Skill,仅在相关时加载?
- 是否应让单独的 subagent 获得这个工具,而非主 agent?
- 标准化 MCP server 是否能让多个 host 安全共享它?
评估架构时要看工具选择和执行控制,工具数量没有意义。
授权发生在校验之后
安全执行边界遵循以下顺序:
- 通过 allowlist 解析工具名称。
- 校验输入类型和边界。
- 从应用绑定已认证身份与租户上下文,不能从模型参数取得。
- 检查能力范围和资源所有权。
- 对后果重大的动作要求审批。
- 应用幂等、超时、速率和大小限制。
- 在可用的最窄 sandbox 中执行。
- 返回 Claude 或日志前对结果脱敏。
若工具参数中含有 user_id,不要把它当作身份。将它与认证会话比对,或完全移出模型控制。
对于变更操作,审批记录应绑定用户、动作、规范化参数、过期时间和操作 ID。对话文本里的“用户之前说过同意”不是安全审批 token。
将失败作为结果返回
处理程序失败并不自动意味着应用崩溃。若收到简洁、真实的工具结果,Claude 可能恢复。
{
"type": "tool_result",
"tool_use_id": "toolu_7f3",
"is_error": true,
"content": "Order service timed out. No order state was changed. Retry is allowed once."
}好的错误内容会告诉模型:
- 什么失败了。
- 是否发生了副作用。
- 重试是否安全。
- 可以采取何种修正。
不要暴露堆栈跟踪、环境值、数据库查询、访问 token 或内部主机名。将它们保留在具有脱敏与访问控制的受保护遥测中。
校验失败可包含字段路径。策略拒绝不应诱导模型寻找绕过办法。“此 agent 不能发起退款”比罗列所有安全规则更安全。
未知工具应按你的设计成为关联错误结果或终止协议错误。绝不要动态导入并运行模型指定的处理程序。
多个与并行工具调用
Claude 一次响应可请求多个工具。只有调用彼此独立、只读且重排安全时,才并行执行。
两次搜索通常能并发。“创建发票”再“发送发票”存在依赖,必须保持顺序;对同一记录的两次写入可能冲突;付款和邮件可能需要事务或补偿工作流。
每个请求的 tool_use ID 都要返回一个 tool_result。保留足够顺序来重建轨迹。若一次并行调用失败,应报告每个结果,不要假装整批都成功。
产品说明,已于 2026-08-08 核实:自动工具执行和并行调用的辅助 API 随 SDK 而异,不会移除应用的授权责任。请检查当前工具使用概览。
限制 Agent
agent 循环除 end_turn 外还需要终止条件:
- 最大模型回合数。
- 全局和按工具计算的最大调用数。
- 墙钟截止时间。
- token 与金额预算。
- 最大连续错误数。
- 最大重复相同调用数。
- 用户取消。
- 必需的人工审批。
- 已验证的最终状态谓词。
最终状态谓词比判断响应是否“听起来完成”更可靠。部署 agent 只有在预期版本健康时才算成功;研究 agent 只有在必需主张都有可解析来源时才算成功。它们的自述和报告长度都不能证明任务完成。
记录轨迹:prompt 版本、模型、停止原因、工具名、规范化参数指纹、决策、延迟、结果类别和状态变化。对敏感值脱敏。
工作流还是 Agent
步骤与分支已知时用固定工作流;路径依赖观察且模型必须在工具间选择时用 agent。
| 任务 | 更好的默认选择 | 原因 |
|---|---|---|
| 提取字段、校验、存储 | 工作流 | 顺序已知,契约清晰 |
| 分类后路由到一个队列 | 工作流 | 分支集合有限 |
| 调查陌生仓库缺陷 | Agent | 搜索路径取决于发现 |
| 核验重复扣款后的退款 | 带审批的工作流 | 动作后果重大,控制已知 |
| 在变化的内部系统间收集证据 | 受限 agent | 工具选择取决于缺失证据 |
任务有价值、环境可被工具访问、错误可检测且可恢复时,自主性才有理由。若错误无法检测,增加 agent 回合只会掩盖风险。
选择要自行拥有多少循环
通过工作流关卡后,选择满足运行需求的最小 harness。
| Runtime | 它处理什么 | 应用仍需处理什么 | 适合场景 |
|---|---|---|---|
| 手写 Messages 循环 | 仅你实现的协议工作 | 完整历史、停止原因、schema 与策略、执行、重试、预算、trace 和恢复 | 需要线协议控制、受限 runtime、自定义状态机,或协议教学和测试 |
| SDK Tool Runner | 工具声明辅助、tool_use/tool_result 顺序、消息状态更新和可选逐回合流 | 授权、sandbox、幂等、错误披露、迭代上限、可观测性和最终状态证明 | 支持的 SDK 合适,且客户端工具仍在应用控制下运行 |
| Claude Managed Agents | 配有 sandbox、内置工具和事件驱动执行的远程 agent/session/environment harness | agent 配置、数据边界审批、自定义工具执行、确认决策、事件持久化、业务授权和结果核验 | 需要托管 session 与 sandbox 边界,并接受当前 beta、平台和事件契约 |
本课代码有意采用第一种。它暴露每次转换。迁移到 Tool Runner 会移除重复的循环样板,却不会让退款变安全。设置迭代上限,拦截或包装工具执行,保留应用审批,并校验最终状态。
产品说明,已于 2026-08-09 核实:Claude Managed Agents 当前为公开 beta,使用带版本的 beta 契约。它提供托管 agents、environments、sessions、内置工具和 server-sent event 流。应将 header、资源、事件类型、工具集、限制和 provider 可用性视为易变项;不要仅因为任务被称作 agent 就选择它。
托管 agent 集成是事件消费者,不是最终文本调用。应用发送用户事件,消费持久化的 session 与 agent 事件,并跟踪状态。自定义工具调用或需权限的工具会使 session 以 requires_action 暂停;应用用结果或确认决策解析被引用的事件 ID。断开的 SSE 连接并不代表成功。应对账持久化事件与终止状态。第 12 课用离线事件 fixture 实现该边界;当前来源见Session event stream。
第一方能力也有多种执行边界
按代码和数据在哪里执行、谁授权以及如何发现来分类能力。
| Surface | 执行和数据边界 | 适用场景 | 不要假设 |
|---|---|---|---|
| Messages server tool | Anthropic 执行 web search、web fetch、code execution、tool search 等支持的工具 | provider 端执行和数据策略符合要求的第一方能力 | 普通场景下应用会收到待执行的客户端 tool_use |
| Anthropic-schema client tool | Anthropic 定义训练内 schema;应用执行 bash、text editor、memory 或 computer use 等工具 | 标准 schema 能提升模型熟悉度,但客户端必须拥有执行权的常用操作 | 第一方 schema 表示 provider 执行或自动授权 |
| Managed-agent built-in | 配置的托管或自托管 agent environment 执行其工具集 | 符合该 runtime sandbox 与权限策略的仓库和 web 工作 | 启用工具集会授予业务权限或免去确认工作 |
| Custom client tool | 应用校验并执行你的 JSON Schema 契约 | 私有业务操作、狭窄领域 API 与精确应用策略 | schema 合法输入就是身份、授权或幂等证据 |
| Skill | 支持的 runtime 加载可复用指令、参考、脚本或资产 | 仅在相关时披露的流程 | Skill 本身是执行或授权边界 |
| MCP | MCP client 或连接器调用标准化外部 server | 由显式 server、身份与传输边界在兼容 host 间共享的能力或上下文 | server 发现会让每个返回工具都安全或相关 |
Skill 和工具往往互补。退款审核 Skill 可以传授流程,custom client tool 则暴露获批操作;当多个 host 需要同一标准接口时,MCP 可以承载该操作。只有网络、保留和结果语义符合数据要求时,才选 provider 执行的 server tool。只有 sandbox 与动作校验器已准备好执行时,才选 Anthropic-schema client tool。
当前执行分类记录于工具使用的工作方式,托管 harness 另有工具配置。版本与模型兼容性会变化,因此在 trace 中持久化所选工具类型和版本。
构建循环
code/main.py 实现工具注册表和原始循环,支持多调用、schema 检查、变更工具审批、处理程序错误、未知工具、关联 ID 和回合预算。离线决策实验根据显式需求分别选择工作流、手写循环、SDK Tool Runner 或 managed agents;它还可选配 Skill 来组合执行 surface,Skill 本身不充当工具。
cd certifications/claude/lessons/10-tool-use-and-agentic-loops/code
python3 main.py
python3 -m unittest discover tests -v阅读 demo 打印的 transcript,找到 assistant tool_use 块及其后 user tool_result。再检查后面打印的决策 fixture。将 managed-agent case 改为不接受 beta,使决策在任何 runtime 启动前失败。协议与架构正确性应可见,不能想当然。
交互实验
使用工具循环图分配回合、工具调用、时间和审批预算。触发重复调用或被拒绝的变更,观察哪项确定性终止条件停止循环。
10-tool-loop-budget实践实验
运行工具循环,然后测试未知工具、无效参数、被拒绝的变更、多调用、处理程序错误和耗尽的回合预算。确认每个结果保留它的 tool-use ID。接着按执行边界与授权所有者,分类 provider server tool、Anthropic-schema client tool、私有 custom tool、Skill 支持的流程和 MCP 服务。
交付产物
outputs/tool-loop-transcript.json 是 demo() 产出的已填充、关联执行 transcript。 outputs/runtime-and-tool-surface-decisions.json 是截至某日期、无 provider 依赖的四种 runtime 与四种能力组合比较。运行 python3 main.py 查看二者,并执行单元测试来核验产物、schema 边界、审批拒绝、runtime gate、执行边界、处理程序失败和失控预防。
验证
cd certifications/claude/lessons/10-tool-use-and-agentic-loops/code
python3 main.py
python3 -m unittest discover tests -v综合项目关联
测验测试提案与授权、工具描述、幂等、并行、最终状态检查和工作流选择。将已验证 transcript 带入 Developer 综合项目 30 和 Architect 综合项目 31、32,作为工具边界证据。
考试决策规则
- Claude 选择工具只是提案,绝不是授权。
- schema 校验在策略之前,策略在执行之前。
- 使用能清楚区分适用时机的窄名称和描述。
- 可以安全恢复时,返回简洁、关联的错误。
- 可重试的副作用要求幂等或对账。
- 只并行化顺序无关的独立调用。
- 在预算、重复调用、取消或无法识别的控制状态下停止。
- 路径已知时优先确定性工作流。
- 客户端执行合适且自定义线协议控制没有价值时,优先 SDK Tool Runner。
- 只有存在具体托管 runtime 需求、且接受 beta 与数据边界时才选择 managed agents。
- 将托管 session 视为事件状态机;按事件 ID 解析
requires_action,绝不根据断开的流推断成功。 - 按执行地点区分 server-executed tools、Anthropic-schema client tools、managed built-ins 和 custom client tools。
- 将 Skills 视为流程、MCP 视为连接边界;二者都不授予授权。
- 同时评估工具轨迹与最终状态。
练习
- 添加必须含审批 token 的
issue_refund。证明对话文本无法替代该 token。 - 在一个响应中添加两个只读调用并发执行,保留确定性结果关联。
- 让一个工具在发生副作用后超时。重试前添加幂等键和对账检查。
- 添加重复调用检测器,在同一规范化工具请求出现两次后停止。
- 将一个私有 custom tool 改为由两个 host 共享的 MCP 能力。确定认证、同意、结果过滤和可用性责任哪些移到 server 边界,哪些仍留在各 host。