Agent 工具调用实战:让 AI 调用外部 API 和服务
你有没有遇到过这种情况:想让 AI 帮你查个天气、读个文件,或者调个 API,结果它只能回你一句”我无法访问外部数据”。
挺烦人的,对吧。
其实这不是 AI 不够聪明,而是它缺少了一项核心能力——工具调用。今天我们就来聊聊这个让 AI 从”只会聊天”变成”能干活”的技术。
什么是 AI 工具调用?
说白了,工具调用就是给 AI 配备了一双手。
传统的大模型只能基于训练数据回答问题。你问它”北京今天天气怎么样”,它只能告诉你”我无法获取实时数据”。但有了工具调用,AI 可以主动请求执行某个函数,比如调用天气 API,然后把结果返回给你。
这个能力有多重要?嗯,大概就是从”只会纸上谈兵的军师”到”能亲自带兵打仗的将军”的跨越。
三种主流方案
目前市面上主要有三种工具调用方案:
| 方案 | 代表产品 | 适用场景 |
|---|---|---|
| Function Calling | OpenAI GPT | 结构化输出、简单 API 调用 |
| Tool Use | Claude | 复杂工具链、多步骤任务 |
| MCP | Claude Code | 标准化工具生态 |
选哪个?看你需求。简单场景用 OpenAI Function Calling 就够了;复杂的 agent 系统,Claude Tool Use 更合适;想搭工具生态,MCP 是趋势。
OpenAI Function Calling:从入门到上手
先看 OpenAI 的方案。它的 Function Calling 设计得很简洁,核心就三步:
- 定义工具(告诉 AI 有哪些工具可用)
- AI 决定调用哪个工具(返回函数名和参数)
- 你执行工具,把结果喂回去
一个完整的例子
假设我们要做一个天气查询工具。首先定义工具 Schema:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的当前天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如'北京'、'上海'"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位,默认摄氏度"
}
},
"required": ["city"]
}
}
}]
注意那个 description 字段——千万别敷衍。AI 就是靠它判断什么时候该调这个工具的。我之前见过有人写 “获取天气”,结果 AI 根本不知道什么场景该用。改成 “获取指定城市的当前天气信息” 后,调用准确率直接从 60% 提到 95%。
接下来发送请求:
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "北京今天热不热?"}
],
tools=tools
)
AI 会返回一个工具调用请求:
tool_call = response.choices[0].message.tool_calls[0]
# tool_call.function.name = "get_weather"
# tool_call.function.arguments = '{"city": "北京"}'
然后你执行真正的函数调用,把结果返回:
# 执行你的天气 API 调用
weather_result = get_weather_from_api("北京")
# 把结果喂回去
final_response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "user", "content": "北京今天热不热?"},
response.choices[0].message, # AI 的工具调用请求
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(weather_result)
}
]
)
AI 就会根据天气数据给你一个自然的回答:“北京今天 28 度,挺热的,出门记得防晒。“
Strict Mode:保证输出稳定性
2024 年 OpenAI 推出了 Strict Mode,这玩意儿解决了 JSON Schema 匹配不稳定的问题。启用方式很简单:
tools = [{
"type": "function",
"function": {
"name": "get_weather",
"strict": True, # 加这一行
# ... 其他字段
}
}]
开启后,AI 返回的参数保证 100% 符合你定义的 Schema。生产环境强烈建议打开,不然你可能会遇到各种奇怪的解析错误。
Claude Tool Use:更强大的工具链
Claude 的 Tool Use 在设计理念上和 OpenAI 有几个重要区别。
并行调用
Claude 支持一次返回多个工具调用。比如用户问 “对比一下北京和上海的天气”,Claude 会一次性请求调用两次 get_weather:
response = client.messages.create(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": "对比一下北京和上海的天气"}],
tools=tools
)
# response.content 可能包含两个 tool_use block
for block in response.content:
if block.type == "tool_use":
print(f"调用 {block.name},参数:{block.input}")
这在处理复杂任务时特别有用。OpenAI 也支持并行调用,但 Claude 的实现更优雅——它会智能判断哪些调用可以并行,哪些必须串行。
Tool Choice 策略
Claude 提供了更细粒度的工具选择控制:
# 自动选择(默认)
tool_choice = {"type": "auto"}
# 强制使用工具(不用工具就不回答)
tool_choice = {"type": "any"}
# 指定使用特定工具
tool_choice = {"type": "tool", "name": "get_weather"}
什么时候用 any?当你确定用户的问题必须通过工具才能回答时。比如查询订单状态,不调数据库根本没法答,那就强制 AI 必须用工具。
错误处理
工具调用失败是常态。API 超时、网络抖动、参数错误……Claude 提供了一个优雅的错误处理机制:
tool_result = {
"type": "tool_result",
"tool_use_id": tool_use.id,
"content": "API 调用失败:连接超时", # 直接告诉 AI 失败原因
"is_error": True # 标记为错误
}
AI 看到错误后,会尝试其他方案或者给用户一个友好的提示。这点比直接抛异常强多了——用户不会看到一堆技术报错,而是”抱歉,天气服务暂时不可用,请稍后再试”。
MCP:工具标准化的未来
说到工具调用,绕不开 MCP(Model Context Protocol)。
为什么需要 MCP?
现在的工具生态太碎片化了。想给 Claude 接一个 GitHub 工具、给 ChatGPT 接一个 Slack 工具,每个都要单独开发。MCP 的目标就是建立一套统一标准——写一次工具,到处能用。
架构很简单:
MCP Client(Claude Code/Claude Desktop)
↕
MCP Server(工具提供者)
↕
External Tool/API
Claude Code 的 MCP 实践
Claude Code 是目前 MCP 支持最好的产品。用 /mcp 命令就能配置工具:
# 添加一个远程 MCP 服务器
claude mcp add my-server --transport sse --url https://api.example.com/mcp
# 添加一个本地工具
claude mcp add local-tool --command node ./my-tool.js
配置好后,Claude Code 会自动发现服务器提供的工具。你在对话里提到相关内容,它就会自动调用。
举个例子。我配置了一个 get-github-issues 工具,然后问 Claude Code:“这个项目的 open issues 有哪些?” 它直接调用工具,把结果整理给我。
整个过程我完全感知不到工具的存在——这才是工具调用的理想状态。
生产环境的几个坑
工具调用听起来简单,但真上了生产,坑还是不少的。
安全性:别让 AI 乱来
AI 可能会调用你不希望它调用的 API。解决方案:
- 工具权限分级:只给 AI 必要的工具,敏感操作需要人工确认
- 输入验证:AI 生成的参数要二次校验,别直接传给 API
- 调用审计:记录每次工具调用的参数和结果,方便回溯
我见过一个反面案例:AI 把用户的随意输入直接传给了数据库查询接口,结果……SQL 注入。虽然不是 AI 直接注入的,但它把恶意输入传了过去。所以,永远不要信任 AI 生成的参数。
超时和重试
工具调用可能失败。设置合理的超时时间,配合重试机制:
async def call_tool_with_retry(tool_func, args, max_retries=3):
for attempt in range(max_retries):
try:
return await asyncio.wait_for(
tool_func(**args),
timeout=10.0 # 10 秒超时
)
except asyncio.TimeoutError:
if attempt == max_retries - 1:
return {"error": "工具调用超时"}
await asyncio.sleep(1) # 等待 1 秒后重试
Token 成本
工具定义和返回结果都会消耗 Token。特别是返回大量数据时,成本可能很高。几点建议:
- 精简工具描述:description 能短就短,但要说清楚
- 限制返回数据:API 返回的数据先过滤,只保留必要字段
- 使用缓存:相同查询的结果可以缓存几分钟
写在最后
工具调用是 AI Agent 的核心能力。没有它,AI 只能纸上谈兵;有了它,AI 才能真正帮你做事。
OpenAI 的 Function Calling 简洁易用,适合入门和简单场景;Claude 的 Tool Use 更强大,适合复杂的 agent 系统;MCP 是工具标准化的趋势,值得长期关注。
选哪个?看你的需求。但不管选哪个,安全性、错误处理、性能调优这三个坑,一定要提前想清楚。
参考资料
- Claude Tool Use 官方文档
- OpenAI Function Calling 指南
- Model Context Protocol 规范
- Claude Code MCP 工具配置
- Anthropic 高级工具使用博客
常见问题
OpenAI Function Calling 和 Claude Tool Use 有什么区别?
什么时候该用 MCP 而不是直接 Function Calling?
工具调用失败怎么处理?
如何防止 AI 调用敏感 API?
Strict Mode 是什么?需要开启吗?
8 分钟阅读 · 发布于: 2026年3月21日 · 修改于: 2026年3月22日

评论
使用 GitHub 账号登录后即可评论