Claude 智能体工具循环的三种选型
文章比较手写 Messages API 循环、Anthropic SDK Tool Runner 和 Claude Agent SDK 的控制边界及适用产品。提供三套可运行 TypeScript 示例、实测令牌用量,并注明验证日期与依赖版本。
AI 深度解读
给产品接入 Claude Agent,第一道选择题是:工具调用的循环由谁负责。原文比较了手写 Messages API 循环、Anthropic SDK 的 Tool Runner,以及把 Claude Code 执行环境带进产品的 Claude Agent SDK。
作者的建议很明确:业务型 Agent 优先考虑 Tool Runner,需要精细控制时再手写循环;处理代码仓库和文件任务,才更适合 Agent SDK。这个区分值得看,因为三者省下的工作不同,带来的部署约束和上下文成本也不同。
三种方案,接管的是不同层次的工作
工具循环的过程很简单:模型提出工具调用,程序执行,把结果交回模型,再由模型决定继续还是结束。
手写方案通过 client.messages.create() 发请求,自己检查 stop_reason、执行工具、补齐消息历史。作者的订单查询示例约 50 行,每次请求都由业务代码掌握。
Tool Runner 使用 client.beta.messages.toolRunner() 接管这段循环。工具仍由你定义,它负责调用工具并回传结果。它属于常规 API SDK,目前是 beta 辅助能力。
Claude Agent SDK 则通过 query() 启动 Claude Code 的执行环境。npm 包内包含 Claude Code binary,同时带有自己的系统提示词、文件读写、Shell、网页搜索、权限、hooks、子代理和会话能力。
Tool Runner 省的是循环代码,Agent SDK 提供的是一整套任务执行环境。
两者都不会替你托管服务,代码仍需运行在自己的环境里。原文称,示例于 2026-11-03 使用 @anthropic-ai/sdk 1.4.0 和 @anthropic-ai/claude-agent-sdk 0.3.317 对照文档并调用线上 API 检查过;本文按作者提供的信息解读。
业务工具已经明确,为什么优先考虑 Tool Runner
客服查订单、调用业务 API,这类任务的核心是你自己的工具。为它引入文件编辑和命令执行环境,通常增加不了多少业务价值。
下面保留原文的 Tool Runner 示例,它把 Zod 输入定义和执行函数放在一起,省掉手动维护消息循环的代码:
import Anthropic from "@anthropic-ai/sdk";
import { betaZodTool } from "@anthropic-ai/sdk/helpers/beta/zod";
import { z } from "zod";
const client = new Anthropic();
const MODEL = process.env.MODEL ?? "claude-sonnet-5-5";
const getOrder = betaZodTool({
name: "get_order",
description: "Look up an order by id. Returns status and total in USD.",
inputSchema: z.object({ order_id: z.string() }),
run: async ({ order_id }) => JSON.stringify({ order_id, status: "shipped", total_usd: 99 }),
});
const runner = client.beta.messages.toolRunner({
model: MODEL,
max_tokens: 4096,
max_iterations: 10,
system: "You are a support agent for an online shop.",
tools: [getOrder],
messages: [{ role: "user", content: "Where is order A-1042 and how much was it?" }],
});
// 每轮迭代产出一个 API 响应,仍可按请求计量。
for await (const message of runner) {
console.log(message.stop_reason, message.usage);
}
const final = await runner.done();
console.log(final.content.filter((b) => b.type === "text").map((b) => b.text).join(""));
重点看 max_iterations: 10 和 for await:前者限制循环次数,后者保留逐次获取响应、记录用量的入口。只要最终答案时,可以用 await runner.runUntilDone();需要审批的操作,也可以在工具的 run 内检查,用户拒绝就返回拒绝结果。
手写循环仍有价值,但要接住几个容易漏掉的细节:
- 同一轮的全部工具结果应放进一条 user 消息,原文提醒,拆成多条可能影响模型继续并行调用工具的行为。
- 工具失败也要返回
tool_result,并设置is_error: true,让模型知道失败发生了。 - 设置
maxTurns = 10等上限,避免失败工具与模型反复循环。 - 原文使用
strict: true约束工具输入符合 schema,但业务权限仍要由应用检查。
原始循环只把 tool_use 当作继续条件。上线时还应分别处理 max_tokens 截断、refusal,以及加入网页搜索等服务端工具后可能出现的 pause_turn。
Tool Runner 的代价是 beta 接口稳定性。作者建议锁定 SDK 版本,升级前阅读 changelog;迁回手写循环通常只是小幅重构,因为底层请求相同。
文件任务适合 Agent SDK,但要读懂权限参数和成本
原文给 Agent SDK 换成了一个文件任务:列出 src/ 下全部 TODO,附文件名和行号。这也说明它真正擅长的场景。
import { query } from "@anthropic-ai/claude-agent-sdk";
for await (const message of query({
prompt: "List every TODO comment under src/ with file and line number.",
options: {
model: process.env.MODEL ?? "claude-sonnet-5-5",
cwd: process.env.REPO_DIR ?? process.cwd(),
allowedTools: ["Read", "Glob", "Grep"], // 只读:没有 Edit、Bash
maxTurns: 10,
},
})) {
if (message.type === "result") {
console.log(message.subtype === "success" ? message.result : message.subtype);
console.log({ turns: message.num_turns, cost_usd: message.total_cost_usd, usage: message.usage });
}
}
要特别看 allowedTools 那一行:虽然代码注释写着只读,作者随后明确解释,这个参数只是预先批准工具执行,并不限制模型上下文里有哪些工具。真正限定工具集合要用 tools,移除特定工具则用 disallowedTools。
运行环境要求 Node.js 18.14 或更高版本。SDK 从环境变量读取 ANTHROPIC_API_KEY,不会自动加载 .env;最终结果提供 num_turns、usage 和美元成本估算 total_cost_usd。
成本方面,作者用 MODEL=claude-haiku-5-5,让三个示例各运行一次。他们观察到,Agent SDK 每次请求会携带约 43,000 tokens 的系统提示词与工具定义,并用 1 小时缓存复用前缀。
原文给出的 2026-11-03 标价均按每百万 tokens 计算:
| 模型 | 输入 | 1 小时缓存写入 | 缓存读取 | 输出 |
|---|---|---|---|---|
| Claude Haiku 5.5 | $0.10 | $0.20 | $0.01 | $0.50 |
| Claude Sonnet 5.5 | $2 | $4 | $0.10 | 原文未列出 |
按作者估算,Sonnet 的这段前缀每小时首次写入约 $0.17,后续每次读取约 $0.004,尚未计入业务自身的 tokens。
手写循环与 Tool Runner 在请求相同时成本相同。另一个细节是:示例里的短系统提示词没有命中缓存,cache_read_input_tokens: 0。加上 cache_control: { type: "ephemeral" } 也不保证缓存生效,内容还需达到模型要求的最小可缓存长度,原文未给出具体阈值。
我的判断
作者最有价值的判断,是按任务选择执行环境。订单查询需要业务工具;修复 lint、分析 CI、迁移仓库配置,则能直接利用文件搜索、编辑、命令执行和会话恢复。
但成本实验只能说明开销结构。三种示例执行的任务不同,每种只跑一次,提供的正文也没有逐请求用量明细,无法据此推导成本倍数或任务成功率。作者所说的「周末做完,对比手写一个月」,同样是经验判断,没有对照数据。
部署结论也要收窄。没有 Shell、不能运行 binary、执行时间短的 edge 或 serverless 环境,更适合普通 HTTP 工具循环;不能据此认定所有 serverless 都无法承载 Agent SDK,仍需看运行时条件。
我赞同把 Tool Runner 作为普通业务 Agent 的起点。不过,「每次响应可见」只提供了计量入口,用户账单、套餐额度和限流仍要自己实现。模型路由可以从免费用户用 Haiku 5.5、付费用户用 Sonnet 5.5 开始,实际效果还需要业务验证。
能拿来做什么
- 给 Node 或 Next.js 产品增加订单查询、业务 API 调用时,可以先用 Tool Runner,把鉴权和审批放进工具执行函数,并逐次记录
usage。 - 必须控制每次请求的额度、重试和结束状态时,可以手写循环,同时补齐错误结果、轮次上限和不同
stop_reason的处理。 - 做仓库 TODO 扫描、lint 修复或配置迁移时,可以评估 Agent SDK;需要只读执行就检查
tools、disallowedTools和权限配置,别只看allowedTools。 - 估算上线成本时,用同一业务任务重复测量,分别记录首次缓存写入与后续读取,再决定是否值得引入完整执行环境。