工具调用:Agent的双手
没有工具调用能力的LLM只是一个"会说话的大脑"。真正的Agent需要调用API、执行代码、读写文件——这些都需要工具调用机制。
演进历程
阶段一:Prompt工程时代(2022-2023)
最早的工具调用靠Prompt引导:
你可以使用以下工具:
1. search(query): 搜索网页
2. calculator(expr): 数学计算
如果需要使用工具,请输出以下格式:
<tool>search("天气")</tool>
这种方式的问题:
- 格式不稳定,模型经常不遵守
- 参数提取容易出错
- 无法处理多步工具调用
阶段二:Function Calling时代(2023-2024)
OpenAI在2023年6月推出Function Calling,将工具调用作为原生API能力:
response = openai.chat.completions.create(
model="gpt-4",
messages=[{"role": "user", "content": "北京天气"}],
tools=[{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取城市天气",
"parameters": {
"type": "object",
"properties": {"city": {"type": "string"}},
"required": ["city"]
}
}
}]
)
# response.choices[0].message.tool_calls
模型输出结构化的工具调用请求,API层解析后执行。
阶段三:标准化时代(2024-2026)
MCP协议将工具调用标准化,实现"一次开发,处处可用"。
Function Calling的技术细节
工作流程
1. 用户消息 + 工具定义 → LLM
2. LLM输出 → tool_call(name, args)
3. 应用层执行工具
4. 工具结果 → LLM
5. LLM基于结果生成回答
关键参数
tools定义:
{
"type": "function",
"function": {
"name": "string",
"description": "string",
"parameters": {
// JSON Schema格式
}
}
}
tool_choice控制:
auto:模型自主决定是否调用none:禁止调用required:必须调用{"function": {"name": "xxx"}}:指定调用特定工具
并行调用
现代模型支持在单次响应中输出多个tool_call:
用户: "比较北京和上海的房价"
模型输出:
- tool_call: get_price("北京")
- tool_call: get_price("上海")
应用层并行执行两个调用,将结果一起返回
工具调用的挑战
1. 参数准确性
模型生成的参数可能有误:
- 类型错误(字符串而非数字)
- 缺失必需参数
- 参数值不合法
解决策略:
- 在JSON Schema中添加详细的description和enum约束
- 应用层做参数验证和类型转换
- 错误信息返回给模型让其修正
2. 工具选择
当工具集很大时(几十上百个),模型难以选择正确的工具。
解决策略:
- 工具检索:根据用户查询embedding检索相关工具
- 分层路由:先分类到工具组,再在组内选择
- 工具描述优化:description要清晰且独特
3. 多步调用
复杂任务需要多步工具调用,每步依赖前一步的结果。
解决策略:
- ReAct循环:Thought→Action→Observation→Thought…
- 计划-执行分离:先规划全部步骤,再逐步执行
- 动态重规划:执行中发现计划不可行,重新规划
4. 错误处理
工具调用可能失败:网络超时、参数错误、权限不足。
解决策略:
- 错误信息返回给模型,让模型决定重试还是换方案
- 重试策略:指数退避+最大重试次数
- 降级方案:工具不可用时使用备用工具或告知用户
最佳实践
工具定义原则
好的工具定义:
{
"name": "search_papers",
"description": "在arXiv上搜索学术论文。当用户需要查找学术研究、论文或技术报告时使用。输入应为英文关键词。",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "搜索关键词,英文,例如 'transformer attention'"
},
"max_results": {
"type": "integer",
"description": "返回结果数量,默认5",
"default": 5
}
},
"required": ["query"]
}
}
关键要素:
- description说明何时使用(不只是做什么)
- 参数描述给出示例值
- 合理的默认值
- required字段明确
错误信息设计
好的错误信息:
"API调用失败:城市名'Beiijing'不存在。请检查拼写,支持的城市包括:北京、上海、广州..."
不好的错误信息:
"Error 500"
好的错误信息帮助模型自我修正。模型能从描述性错误信息中推断下一步行动。
工具粒度
- 粗粒度:一个工具完成复杂操作(如
analyze_code(file)) - 细粒度:拆分为多个工具(
read_file+parse_syntax+check_style)
粗粒度更可靠(模型调用步骤少),细粒度更灵活(可以组合)。实践中推荐中等粒度——平衡可靠性和灵活性。
MCP带来的改变
MCP协议标准化了工具调用接口后:
- 工具开发一次即用:开发一个MCP Server,所有MCP兼容客户端都能用
- 动态工具发现:Agent运行时自动发现可用工具
- 工具复用:同一个工具服务被多个Agent框架共享
- 统一接口:不再需要为每个框架写适配层
总结
工具调用是Agent从"能说"到"能做"的关键桥梁。从Prompt工程到Function Calling再到MCP标准化,每一代演进都在降低开发门槛、提升可靠性。2026年,随着MCP生态成熟,工具开发将成为独立的生态——就像今天的手机App生态一样丰富。