语音摘要 — 点击播放以跟读:正在朗读的行会保持在顶部附近。
您现有的工作流程依赖于多种技术——项目管理软件、数据库、文件。Claude 无法自行检查这些内容。它需要依赖工具来访问外部数据和操作。
工具的定义
简单来说,工具就是您定义并暴露给Claude的函数。您描述其功能及所需输入,Claude决定何时调用它。
关键要理解:Claude不执行工具——由您的代码执行。流程如下:
- Claude请求调用工具
- 您的代码执行该函数
- 结果返回给Claude,流程继续
如何定义工具
工具是包含三部分的JSON模式:名称、描述和输入模式。您将它们作为tools数组传递给Claude。
描述是Claude决定是否调用工具的依据。模糊的描述会导致工具使用不当。这是代理误操作或未使用可用工具的首要原因。务必具体明确。
工具定义示例:
{ "name": "lookup_building_code", "description": "通过标识符查询特定建筑规范条款。返回该条款的完整文本。", "input_schema": { "type": "object", "properties": { "section": { "type": "string", "description": "要查询的建筑规范条款" } }, "required": ["section"] } }
使用此工具时会发生什么?假设我们发送合规报告给代理。第一轮,Claude返回stop_reason: "tool_use"——这是我们的信号。响应示例如下:
API响应,stop_reason设为tool_use,包含命名工具及Claude调用参数的tool_use内容块
我们的循环用Claude请求的参数调用lookup_building_code,然后将结果作为工具结果反馈——包含tool_result块的用户消息,该块与工具调用ID关联:
用户消息包含tool_result块,其中tool_use_id及查询到的建筑规范文本作为内容
Claude继续处理。此时我们可以持续调用工具并将结果返回给Claude,直到它获得所需信息。
多工具:让Claude选择
单一工具有用,但更有趣的是给Claude多个工具,观察它选择使用哪个及顺序。
设想场景:您为三天丹佛之行打包行李,需要今天天气和未来几天预报。我们声明两个工具:
const tools = [ { name: "get_weather", description: "获取城市当日天气。", input_schema: { type: "object", properties: { city: { type: "string", description: "要查询的城市" } }, required: ["city"] } }, { name: "get_forecast", description: "获取城市未来几天天气预报。", input_schema: { type: "object", properties: { city: { type: "string", description: "要查询的城市" } }, required: ["city"] } } ];
循环逻辑与之前相同。新增的是runTool函数,通过switch语句根据工具名分发——这段代码是实际执行处:
function runTool(name, input) { switch (name) { case "get_weather": return getWeather(input. city); case "get_forecast": return getForecast(input. city); } }
while (true) { const response = await client. messages. create({ model: "claude-sonnet-4-6", max_tokens: 1024, messages, tools, });
if (response. stop_reason ! == "tool_use") { // Claude完成——这是最终答案 break; }
messages. push({ role: "assistant", content: response. content });
const toolResults = response. content filter((block) => block. type === "tool_use") .map((block) => ({ type: "tool_result", tool_use_id: block. id, content: runTool(block. name, block. input), }));
messages. push({ role: "user", content: toolResults }); }
这就是完整模式。想要第三个工具?添加到数组,在switch中添加case即可。
运行时您会看到Claude调用get_weather和get_forecast——有时同轮次,有时先后调用。然后回答:准备分层衣物,今天有雪,本周逐渐回暖。
注意Claude的选择方式:它读取描述,将您的提示映射到"今日天气"和"未来几天",并选择正确工具。这就是工具描述至关重要的原因。
工具运行器:跳过样板代码
您可能已发现两个问题:
- 两个简单查询需要大量代码
- 实际代码中不想为每个函数手写JSON模式,就像重复编码
工具运行器解决这些问题。它内置在Claude SDK的TypeScript、Python和Ruby版本中。运行器读取您的实际函数,通过类型和文档构建模式,并内部处理整个工具调用/结果循环。
您的代码简化为:描述工具,发送提示,等待结果。相同天气演示通过工具运行器实现:
// 相同的两个查询——普通TypeScript函数 function getWeather(city: string) { // ... 现有查询 }
function getForecast(city: string) { // ... 现有查询 }
const runner = client. beta. messages. toolRunner({ model: "claude-sonnet-4-6", max_tokens: 1024, messages: [ { role: "user", content: "我在为三天丹佛之行打包行李。今天和未来几天天气如何?", }, ], tools: [getWeather, getForecast], });
// 返回所有工具交互完成后的最终助手消息 const finalMessage = await runner. untilDone();
相同场景,代码量大幅减少:
- 无while循环,无停止原因判断,无手动推送工具结果——运行器全处理
- 无JSON模式,避免重复编码
- 两个函数与之前手动运行的查询相同,只是普通TypeScript
- runner. untilDone()在所有交互完成后返回最终助手消息
运行获得相同答案。
真实工具封装现有代码
实际应用中,工具不会硬编码天气数据,而是封装应用中已有的函数。
以合规审查代理为例:其工具是代码库中已有lookup_building_code和search_building_code函数的轻量封装。通过工具运行器直接传入这些函数,代理能在每条发现中引用具体规范条款——无需编写模式:
合规审查应用显示结构化报告及代理发现,每个标记项都注明其检查的具体建筑规范条款
回顾
- 工具让Claude访问您的系统。工具是您定义并暴露的函数;Claude决定调用时机,您的代码执行它
- 工具是包含名称、描述和输入模式的JSON模式,作为tools数组传递
- 编写具体描述。模糊描述是代理误操作的主因
- stop_reason: "tool_use"是运行工具并将结果作为工具结果反馈的信号
- 多工具时根据工具名分发。添加工具意味着扩展数组和添加case
- SDK的工具运行器(TypeScript/Python/Ruby)从实际函数构建模式并处理整个循环——或您可自行控制循环
- 您执行或委托循环。最终形态是托管代理,将整个代理委托给Anthropic