什么是Agent工具调用?
想象一下,你是一个只会说话的AI,突然有人问你"北京现在天气怎么样?"——你无法访问实时数据,只能尴尬地说"我不知道"。但如果给你一个"天气查询工具",你就能瞬间回答。这就是工具调用(Tool Calling)的魔力。
在OpenClaw中,工具调用是Agent与外部世界交互的核心机制。它让AI从"只会说"进化成"会做事"——查询数据库、调用API、执行代码、读写文件,无所不能。
💡 核心概念:工具调用本质上是Agent与LLM之间的"契约"——Agent声明工具的能力,LLM决定何时调用、传什么参数,然后Agent执行并将结果返回给LLM。
工具调用的工作流程
OpenClaw的工具调用遵循一个优雅的循环:
- 工具注册 - Agent声明可用工具及其参数schema
- 意图识别 - LLM分析用户请求,判断是否需要调用工具
- 参数构建 - LLM生成符合schema的参数JSON
- 工具执行 - Agent执行工具并获取结果
- 结果整合 - LLM将工具结果融入最终回复
流程图解
用户请求 → LLM分析 → 需要工具?
↓是
生成工具调用参数
↓
Agent执行工具
↓
返回结果给LLM
↓
LLM生成最终回复 → 返回用户
OpenClaw工具调用实战
1. 定义工具Schema
{
"tools": [
{
"name": "get_weather",
"description": "查询指定城市的当前天气",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如:北京、上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,默认celsius"
}
},
"required": ["city"]
}
}
]
}
2. 在Agent中注册工具
// OpenClaw工具定义示例
const weatherTool = {
name: "get_weather",
description: "查询指定城市的当前天气",
parameters: {
type: "object",
properties: {
city: { type: "string", description: "城市名称" },
unit: { type: "string", enum: ["celsius", "fahrenheit"] }
},
required: ["city"]
},
handler: async (params: { city: string; unit?: string }) => {
// 调用天气API
const response = await fetch(
`https://api.weather.com/current?city=${params.city}`
);
return response.json();
}
};
// 注册到Agent
agent.registerTool(weatherTool);
3. 处理工具调用响应
// LLM返回的工具调用
const toolCall = {
id: "call_abc123",
name: "get_weather",
arguments: '{"city": "北京", "unit": "celsius"}'
};
// Agent执行工具
const result = await agent.executeToolCall(toolCall);
// 将结果返回给LLM
const messages = [
{ role: "user", content: "北京天气怎么样?" },
{ role: "assistant", toolCalls: [toolCall] },
{ role: "tool", toolCallId: "call_abc123", content: JSON.stringify(result) }
];
最佳实践
1. 工具描述要精准
好的工具描述能让LLM准确判断何时调用:
✅ 好的描述:"查询指定城市的当前天气,返回温度、湿度、风速等信息"
⚠️ 差的描述:"获取天气信息"(太模糊,LLM可能误用)
2. 参数校验要严格
永远不要信任LLM生成的参数:
handler: async (params) => {
// 参数校验
if (!params.city || typeof params.city !== 'string') {
throw new Error("city参数必须是字符串");
}
// 类型转换与边界检查
const city = params.city.trim().slice(0, 50);
// 执行工具逻辑
return await fetchWeather(city);
}
3. 错误处理要优雅
工具执行失败时,返回清晰的错误信息给LLM:
handler: async (params) => {
try {
return await fetchData(params);
} catch (error) {
return {
error: true,
message: `查询失败: ${error.message}`,
suggestion: "请检查参数是否正确,或稍后重试"
};
}
}
4. 避免工具调用循环
设置最大调用次数防止无限循环:
const MAX_TOOL_CALLS = 5;
let callCount = 0;
while (response.toolCalls && callCount < MAX_TOOL_CALLS) {
const results = await executeToolCalls(response.toolCalls);
response = await llm.chat(messages, results);
callCount++;
}
高级技巧
并行工具调用
当多个工具调用相互独立时,可以并行执行:
const toolCalls = response.toolCalls;
// 并行执行所有工具
const results = await Promise.all(
toolCalls.map(call => executeTool(call))
);
工具链式调用
某些场景需要前一个工具的输出作为后一个工具的输入:
// 先查询用户ID,再获取用户详情
const userId = await agent.callTool("get_user_id", { name: "张三" });
const userDetails = await agent.callTool("get_user_details", { id: userId });
💡 建议:对于复杂的链式调用,考虑使用OpenClaw的Workflow功能,它提供了更清晰的状态管理和错误处理。
常见问题
Q: 工具调用和函数调用有什么区别?
在OpenClaw中,这两个概念基本等价。不过"工具"更强调Agent层面,"函数"更偏向代码层面。
Q: 一个Agent可以注册多少个工具?
理论上没有限制,但建议控制在10-20个以内,太多会让LLM难以选择正确的工具。
Q: 工具调用会增加多少延迟?
取决于工具本身的执行时间。建议对每个工具设置超时,避免长时间等待。