📚 首页 / 04-tool-use / README.md

模块 4 — 工具使用

目标:掌握 工具使用 的完整回路,并建立本仓库最重要的架构判断力之一:什么由 Claude 决定、什么由确定性代码决定、何时需要人工审批。模块 11(安全)与模块 08(agents)都建立在这个地基上。 📚 模块 04 重要英文词汇

1. 工具使用是什么

🎯 学习重点:必考边界是 client tool 由应用执行并回传,server tool 由 Anthropic 基础设施执行;模型只提出调用,不获得执行权限。

官方定义:Tool use(也叫 function calling)让 Claude 调用你定义的函数或 Anthropic 提供的工具。Claude 根据用户请求与工具描述决定何时调用,返回一个结构化调用(tool_use block),由你的应用执行(client tools)或 Anthropic 基础设施执行(server tools)(来源: https://platform.claude.com/docs/en/agents-and-tools/tool-use/overview(访问于 2026-08-30))。

类别执行方例子
Client tools(用户自定义)你的应用get_weather、query_orders、任何你写的函数
Client tools(Anthropic 定义 schema)你的应用bash、text_editor、memory、computer use、browser use
Server toolsAnthropic 基础设施web_search、web_fetch、code_execution、tool_search、MCP connector

关键区分维度就一个:代码在哪里跑。Server tool 结果直接出现在响应里,你不用执行;client tool 必须你来执行并回传结果。

第一性理由(接模块 1):模型权重冻结、知识有 cutoff——实时事实与真实世界副作用只能通过工具获得/发生。

🔪 架构师犀利细节 01|“会调用”不等于“被授权”:tool_use 只是概率模型提出的结构化执行建议,不是 capability token。反模式是按工具名直接 dispatch;正解是每次执行前重新校验主体、租户、资源、动作、配额和审批状态,即使 schema 完全合法也可能因越权被拒绝。

2. 完整回路(本模块核心图)

🎯 学习重点:必须能复述定义→选择→调用→执行→回传→收尾六步,并记住 tool_result 的紧邻、顺序与原样回传约束。
sequenceDiagram participant U as 用户 participant A as 应用(确定性代码) participant C as Claude U->>A: "旧金山天气怎么样?" A->>C: POST /v1/messages<br/>(system + tools 定义 + messages) C-->>A: stop_reason: "tool_use"<br/>content: [tool_use {id, name, input}] Note over A: ① 权限校验/审批门(应用层!)<br/>② 执行工具(查 API/跑代码)<br/>③ 组装 tool_result(只回高信号字段) A->>C: 新请求:历史 + assistant(tool_use) +<br/>user([tool_result {tool_use_id, content, is_error}]) C-->>A: stop_reason: "end_turn"<br/>content: [text 最终回答] A->>U: 回答(引用了工具结果)

逐步拆解(来源: tool-use overview 与 handle-tool-calls(访问于 2026-08-30)):

  1. 定义:请求带 tools 数组(name + description + input_schema)。API 会把工具定义拼进一个特殊 system prompt。
  2. 选择:默认 tool_choice: {"type": "auto"} 下,Claude 自己决定调不调工具、调哪个——依据是"请求是否映射到工具描述的能力,且答案不在上下文里"。
  3. 调用:响应 stop_reason: "tool_use",content 含一个或多个 tool_use block:id(对账用)、name、input(符合 schema 的对象)。
  4. 执行:你的代码按 name 分发、按 input 执行。这一步在 Claude 之外——权限、重试、超时、审计都在这里。
  5. 回传:新请求把 assistant 的 tool_use 内容原样放回历史,紧跟一条 user 消息:tool_result block(tool_use_id + content + 可选 is_error)。
  6. 收尾:Claude 基于 tool_result 生成最终回答(end_turn),或再次调用工具(多步 agent 回路,模块 08)。

格式硬约束(违反直接 400):

🔪 架构师犀利细节 02|把回路当状态机,不要当聊天拼接:合法转换是 assistant(tool_use) → user(tool_result...),tool_result 必须最先出现且 thinking 要原样保留。中间插消息、漏结果或重排 block 都可能 400 或提前 end_turn;生产实现应按 tool_use_id 建待处理集合,在集合清空前禁止进入普通 user 文本分支。

3. 定义好工具(决定 Claude 选择质量的上游)

🎯 学习重点:工具名、3–4 句高信息描述、JSON Schema required 与 input_examples 共同决定选择和参数质量。

用户自定义工具的字段(来源: https://platform.claude.com/docs/en/agents-and-tools/tool-use/define-tools(访问于 2026-08-30)):

字段说明
name匹配 ^[a-zA-Z0-9_-]{1,64}$;跨服务要加前缀(github_list_prs)
description最重要。官方建议至少 3–4 句:做什么、何时用、何时不用、参数含义、限制
input_schemaJSON Schema;required 明确
input_examples可选;复杂/嵌套/格式敏感参数给示例

官方最佳实践:描述详细压倒一切;相关操作合并成带 action 参数的单个工具(少而强的工具面优于一柜子细粒度工具);返回高信号内容(语义化 ID、Claude 下一步需要的字段,别回整个数据库行)。

与 prompt 的边界:调不调工具的倾向可以用 system prompt 微调——"Use the tools to investigate before responding." 提高调用率;"Use your judgment..." 保守化(来源: tool-use overview "When Claude uses tools",同上)。

Bad vs Better(工具定义):

BadBetter
description: "Gets the stock price"(官方反面示例原文)什么股票、什么交易所、返回什么、何时该用/不该用
每个小操作一个工具(PR 的建/审/合三个工具)一个 manage_pr + action 参数
返回 2MB 原始 JSON返回 slug/UUID + 下一步需要的字段
工具名 get1 / do_thinggithub_list_prs / slack_send_message 命名空间化
🔪 架构师犀利细节 03|工具目录本身是 token 与歧义税:工具定义会进入特殊 system prompt;工具越多、描述越长,每轮前缀越大且候选越容易重叠。反模式是把 CRUD 每个动作拆成一个近义工具;正解是按同一权限域和业务对象合并为带 action 的工具,同时保持 tools/system 逐字节稳定以利用 prompt cache。

4. tool_choice:把"调不调"的控制权分级

🎯 学习重点:记住 auto/any/tool/none 四级控制及 forced tool use 与自然语言、thinking、缓存之间的副作用。

四个选项(来源: define-tools "Forcing tool use",同上):

值行为典型用途
{"type": "auto"}Claude 自主决定(提供 tools 时的默认)一般对话/agent
{"type": "any"}必须调某个工具强制走工具路径(如强制检索)
{"type": "tool", "name": "..."}强制调指定工具流水线固定步骤
{"type": "none"}禁止调工具(不提供 tools 时的默认)纯文本回合

细节与陷阱:

🔪 架构师犀利细节 04|强制路径仍不是安全保证:any 能保证“至少调用某个工具”,tool 能保证“调用指定工具”,strict 能保证“参数符合 schema”;三者都不能证明调用者有权限、业务状态允许或动作值得执行。权限/状态/额度校验必须在执行器,schema 只验证形状,不验证授权与业务真值。

5. 工具错误回传:错误也是上下文

🎯 学习重点:工具失败应通过 is_error: true 回传可行动信息,让 Claude 改参、改道或诚实告知用户。

工具执行失败不等于系统故障——把错误回传给 Claude 让它恢复(来源: https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls(访问于 2026-08-30)):

{
  "role": "user",
  "content": [{
    "type": "tool_result",
    "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
    "content": "ConnectionError: the weather service API is not available (HTTP 500). Retry after 60 seconds.",
    "is_error": true
  }]
}

三类错误三种处理:

错误处理
执行错误(网络 5xx、超时)is_error: true + 有指导性的错误消息(官方明确:别只回 "failed",要回"错在哪、下一步怎么办",如 "Rate limit exceeded. Retry after 60 seconds.")
调用无效(缺必填参数等)同样回传 is_error;Claude 通常自我修正重试 2–3 次后才向用户道歉;根治靠 strict: true
Server tool 错误(如 web_search 限流)Anthropic 侧透明处理,Claude 自行给替代回答;你不需要处理 server tool 的 is_error

Bad vs Better(错误回传):

BadBetter
工具崩了抛异常,整个请求 500 给用户捕获 → is_error: true + 可行动的错误文本 → Claude 改道或告知用户
"error" 一词了事带语义:错误类型 + 原因 + 建议动作
应用层对同一坏参数无限重试回传给 Claude 自我修正(它有 2–3 次的纠正行为);或 strict 模式根除

安全提示(模块 11 前菜):tool_result 里常有你控制不了的内容(网页、邮件、第三方 API 返回)。官方警告:把它们当不可信内容,留在 tool_result block 里,不要搬进 system prompt 或用户 text block——间接 prompt injection 的攻击面(来源: handle-tool-calls Warning,同上)。

🔪 架构师犀利细节 05|错误信息要可行动,但不能泄密:回传“HTTP 500,60 秒后重试”比“failed”更利于恢复;但原始堆栈、内部 URL、凭据和其他租户数据不应进入模型上下文。正解是错误分类映射:对模型返回安全的类型/原因/下一步,对日志保留受控诊断细节,并用内部 trace ID 关联二者。

6. 架构判断:谁决定什么(本模块的灵魂)

🎯 学习重点:模型负责判断,代码负责校验与强制,人负责高影响不可逆动作;prompt 不能替代权限门。

Claude(概率组件)负责"判断",确定性代码负责"执行与强制",人在环上负责"不可逆"。

决策归属机制
调不调工具、调哪个、传什么参数Claude(auto)tool_use block 本身
强制/禁止工具调用应用代码tool_choice(any/tool/none)
参数合法性API + 应用input_schema + strict: true
该不该执行(权限/配额/租户隔离)应用代码执行前的授权检查——LLM 的指令不是授权机制,Claude 说"调用 delete_all"不代表被允许
结果格式/裁剪应用代码组装高信号 tool_result
重试与超时应用代码工具执行器内做退避;错误回传给 Claude
不可逆/高代价动作人(审批门)执行前人工确认

何时需要 人工审批——经验判据(任一命中就该设门):

  1. 不可逆:删除数据、发送外部消息、支付、合并生产分支
  2. 高代价:花钱(下单)、发不可撤回的对外通信
  3. 低可解释性 × 高影响:批量修改客户数据
  4. 合规要求:金融/医疗场景的最终确认

审批门的实现位置在应用的执行器里(tool_use → 权限检查 → 人审队列 → 执行/拒绝),不在 prompt 里。prompt 说"先问用户"只是行为引导,不是强制——强制必须由代码拒绝执行(详见模块 11:LLM instruction vs application enforcement)。

flowchart TD TU[收到 tool_use] --> GATE{执行前门(应用层)} GATE -->|读操作·租户内·有权限| EXE[直接执行] GATE -->|写操作·可逆| EXE2[执行 + 审计日志] GATE -->|不可逆/高代价/合规| HUMAN[人工审批队列] HUMAN -->|批准| EXE2 HUMAN -->|拒绝| ERR[tool_result: is_error=true<br/>"权限被拒:需要人工批准,已被拒绝"] EXE --> RES[tool_result 成功] EXE2 --> RES ERR --> BACK[回传 Claude 继续对话] RES --> BACK

7. 并行工具调用与多步回路

🎯 学习重点:一轮多个 tool_use 必须逐一对账回传;多步回路要有步数、时间、token 与费用停止条件。
🔪 架构师犀利细节 06|并行只省墙钟时间,不省工作量:同轮 N 个 tool_use 仍要执行 N 次、返回 N 个结果并把结果全部计入后续上下文。反模式是无上限并发后把完整响应全塞回;正解是按工具配额限制 fan-out、设置单工具超时、按 tool_use_id 对账,并在回传前裁剪成完成下一步所需的高信号字段。

8. Bad vs Better:整体对比

🎯 学习重点:综合题的正确架构是“模型决策 + 确定性守门 + 可恢复错误 + 高信号结果 + 可观测回路”。

Bad(把模型当执行引擎):Claude 输出"执行 DELETE 的指令"→ 代码无检查直接执行;错误只打日志不回传;工具返回 2MB 原始行;每轮重新发明工具定义措辞(缓存全废)。

Better(模型当决策器,代码当守门员):schema 校验 + 执行前权限门 + 不可逆动作人审;错误以 is_error 回传并带建议;结果裁剪高信号;tools/system 前缀稳定参与缓存;并行调用按需开启。

9. 权衡 速查

🎯 学习重点:工具强制、strict、并行、错误回传、工具粒度和人审都以延迟、token、复杂度或人力为代价。
选择得到付出
auto vs any/tool灵活判断 vs 确定路径强制调用无自然语言;缓存 messages 部分作废
strict: true入参必过 schemaschema 子集限制(模块 05)
并行工具调用吞吐/延迟执行侧要支持并发与结果对账
错误回传 vs 应用层静默重试Claude 可自适应改道上下文消耗;错误消息质量决定恢复效果
细粒度工具 vs 合并工具表面清晰选择歧义与 token 成本(官方建议合并)
人审门不可逆动作的安全网延迟与人力成本——只对高风险动作开

10. 常见错误

🎯 学习重点:优先掌握会直接 400、空响应、越权执行、注入扩散和上下文爆炸的错误。
  1. ❌ tool_result 与 tool_use 中间插了别的消息(400:"tool_use ids were found without tool_result blocks immediately after")
  2. ❌ tool_result 之前塞 text(空响应/提前结束轮次)
  3. ❌ 修改 thinking block 后回传(400)
  4. ❌ 错误不回传、只打日志——Claude 失去自我修正机会
  5. ❌ 错误消息无指导性("failed"),Claude 只能瞎猜重试
  6. ❌ 权限检查缺失:以为"prompt 里说了只能读"就是访问控制(LLM 指令 ≠ 应用强制)
  7. ❌ 忽略 server tool 混合场景(同轮 client tool_use + 未完成 server tool 的回传规则)
  8. ❌ 工具返回全量原始数据,上下文被工具结果淹没

11. 重要英文术语表

🎯 学习重点:重点成对记忆 tool_use/tool_result、client/server、model decision/application enforcement、retry/idempotency。

完整分组版见:模块 04 重要英文词汇。

英文术语中文一句话解释
Tool Use / Function Calling工具使用/函数调用Claude 通过结构化协议调用外部能力
Client Tool / Server Tool客户端/服务端工具你的代码执行 vs Anthropic 基础设施执行
tool_use block工具调用块Claude 的调用请求:id + name + input
tool_result block工具结果块应用回传的执行结果(可含 is_error)
input_schema输入模式工具参数的 JSON Schema 契约
tool_choice工具选择auto/any/tool/none 四级控制
Parallel Tool Use并行工具调用一轮多个 tool_use;可禁用
is_error错误标记tool_result 的错误信号,错误内容回传 Claude
Strict Tool Use严格工具使用strict: true 保证入参过 schema
Human Approval / Human-in-the-loop人工审批/人在环不可逆动作的执行前确认门
Indirect Prompt Injection间接提示注入经 tool_result 注入的恶意指令
Agent Loop智能体回路tool_use ↔ tool_result 循环直至任务完成

12. 认证相关性

🎯 学习重点:工具回路题的得分点不只在格式,还在执行边界、恢复策略和不可逆动作控制。

13. 幂等性 (Idempotency) 与安全重试

🎯 学习重点:tool_use.id 只对账一次模型调用,跨模型重发和执行器重试必须依赖业务级幂等键。
本节补齐工具回路可靠性的最后一块拼图:§5 讲「错误要回传」,§6 讲「执行前要过门」,但重试本身也可能是事故——同一写操作被执行两次。

13.1 为什么工具重试天生危险

🎯 学习重点:API 客户端、应用执行器和模型三层都可能重试,写操作的重复副作用会跨层放大。

重试在系统里至少发生在三层:

层谁触发官方行为/建议
API 客户端层SDK 对 429/500/504/529 自动重试SDK 默认重试 2 次并尊重 retry-after;500 用指数退避;529 过载建议平缓爬坡(来源: https://platform.claude.com/docs/en/api/errors,访问于 2026-08-30)
应用执行器层你的工具执行代码(超时/瞬时故障退避重试)工程实践:退避 + 抖动 + 重试上限
模型层Claude 收到 is_error 后自我修正、重新发起调用官方行为:通常重试 2–3 次后才放弃(来源: https://platform.claude.com/docs/en/agents-and-tools/tool-use/handle-tool-calls,访问于 2026-08-30)

对只读工具(查天气、搜文档),重复执行只是浪费一次调用;对写工具(扣款、发邮件、建订单、删数据),任何一层重试都可能把同一逻辑操作执行两次——「重试让系统更可靠」在真实世界副作用面前会翻车成「重试让事故翻倍」。

一个容易混淆的边界:tool_use 的 id 不是幂等键。id 标识「模型发出的这一次调用」——应用执行器对同一次调用做内部重试时可以拿它对账;但 Claude 在收到 is_error 后重新发起的调用会产生新的 id。要跨层去重,必须用业务级操作标识(见下)。

13.2 幂等设计三件套

🎯 学习重点:幂等键、执行器去重、退避/抖动/熔断三件套共同控制重复副作用与重试风暴。

幂等性:同一操作执行一次与执行 N 次,效果相同。读操作天然幂等;写操作必须设计。这是分布式系统的通行做法(支付网关普遍要求调用方携带 idempotency key,属通用工程实践而非 Anthropic 官方文档专文主张),落到 agent 工具面即:

  1. 幂等键:调用方为每个「逻辑操作」生成唯一键(如 会话ID:意图:对象ID),随工具调用传给执行器;执行器把 键 → 执行结果 存入去重存储(带 TTL)。同键请求再次到达,直接返回首次结果,不再执行副作用。
  2. 写操作去重:执行器侧的最后一道闸——即使上游(模型重发、应用重试、重试框架)全部失守,服务端也挡得住。位置与 §6 的审批门同层:都在应用的执行器里,不在 prompt 里——「这个工具是幂等的,可放心重试」这类说明可以写进工具 description 帮助 Claude 恢复,但保证本身必须由代码兑现。
  3. 重试风暴防护:N 个客户端 × 每个重试 K 次 = 瞬时负载放大 N×K 倍——上游过载往往正是重试自己制造的。防护组合:指数退避 + 抖动、尊重 retry-after、每客户端重试预算/并发闸、熔断。官方对 529(API 过载)的专门建议是逐步恢复流量而非立即全量重试(来源: https://platform.claude.com/docs/en/api/errors,访问于 2026-08-30)。
🔪 架构师犀利细节 07|三层重试是乘法,不是加法:模型重发、执行器内部重试和 SDK 重试可嵌套发生;任一层把次数调大都会放大其余层。对写工具的正确顺序是先查询业务状态,再用同一幂等键重试;仅把 tool_use.id 写进日志无法阻止模型以新 id 重发同一业务动作。

13.3 与 §5/§6 的衔接:一张可靠性视图

🎯 学习重点:超时表示结果未知而非失败;写操作应先对账,再由幂等键决定重试或复用首次结果。
故障§5 错误回传本节幂等§6 审批门
只读工具 500 瞬时故障is_error + 指导性消息天然幂等,可直接重试—
写工具超时(结果未知)❗不能盲目回传「失败」——服务端可能已成功先对账(查订单/流水状态),幂等键兜底后再重试—
写工具重复调用—幂等键去重,返回首次结果人审队列同样按操作键关联:批准一次 ≠ 授权重复执行

超时是重试设计里最阴险的分支:超时 ≠ 失败。模块 03 讨论过的「计费不幂等」(重试可能对同一逻辑请求产生两次完成、两次计费)正是同款问题;工具层的版本更危险——重复的是真实世界副作用。正确顺序永远是「先对账,再决定重试或继续」,而非立即原样重发。

Bad vs Better(重试设计):

BadBetter
支付工具超时 → 立即原样重发 → 双重扣款幂等键 + 先查支付流水对账,确认未成功才重试
is_error: "payment failed" → Claude 盲目重调错误消息写明状态与建议:「已扣款,勿重试」或「未扣款,60s 后可重试」
上游 529 → 全体客户端立即各重试 3 次(风暴)指数退避 + 抖动 + 平缓爬坡(官方 529 指引)
人审通过后,代码重试又发一封邮件外发动作也走幂等键;审批与执行按键关联

13.4 本节术语

🎯 学习重点:能用 Idempotency Key、At-least-once Delivery、Retry Storm 和 Circuit Breaker 解释安全重试链。
英文术语中文一句话
Idempotency幂等性执行一次与执行 N 次效果相同
Idempotency Key幂等键标识逻辑操作的业务级唯一键,用于去重
Retry Storm重试风暴集体重试放大负载、加剧上游过载的正反馈
Exponential Backoff + Jitter指数退避+抖动重试间隔指数增长并加随机量,错开并发
Circuit Breaker熔断器连续失败时暂停调用,防止雪崩
At-least-once Delivery至少一次投递网络重试的默认语义;副作用需幂等兜底

14. 下一步

🎯 学习重点:完成实验后应能实现带 schema 校验、权限门、错误回传、幂等键和停止预算的最小工具回路。
📚 目录