工具调用(Function Calling / Tool Use)是 Agent 的"手脚"——它让模型的决策真正落地到可执行的动作。本文拆解工具调用的底层机制、Schema 设计最佳实践,以及跨平台兼容性处理。
1. 工具调用的本质
当用户说 “查一下北京明天天气”,模型需要:
- 识别意图:用户想获取天气信息
- 匹配工具:存在
get_weather工具可用 - 参数推导:城市 = “北京”,日期 = “明天”
- 调用执行:实际调用 API
- 结果消化:将 API 返回的 JSON 转化为自然语言回答
用户输入 → 模型推理 → 工具调用决策 → 参数提取 → API 执行 → 结果反馈 → 回答生成
2. 六大工具调用标准对比
| 标准 | 代表 | Schema 格式 | 并行调用 | 返回格式 |
|---|---|---|---|---|
| OpenAI Function Calling | GPT-4o | JSON Schema | ✅ | tool_calls id |
| Claude Tool Use | Claude 3.5 | JSON Schema | ✅ | tool_use id |
| Google Function Declaration | Gemini | OpenAPI Schema | ✅ | functionCall |
| Mistral Function Calling | Mistral | JSON Schema | ✅ | tool_calls |
| Llama 3 Tool Use | Llama 3.1 | JSON Schema (简化) | ⚠️ | tool_calls |
| Anthropic Computer Use | Claude 3.5 | 伪代码 | ✅ | tool_use |
2.1 OpenAI Function Calling 详解
from openai import OpenAI
client = OpenAI()
# 定义工具
tools = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "获取指定城市的天气信息。当用户询问天气时使用。",
"parameters": {
"type": "object",
"properties": {
"city": {"type": "string", "description": "城市名称"},
"date": {"type": "string", "description": "日期(YYYY-MM-DD)"},
},
"required": ["city", "date"],
},
},
},
{
"type": "function",
"function": {
"name": "send_email",
"description": "发送一封邮件。",
"parameters": {
"type": "object",
"properties": {
"to": {"type": "string"},
"subject": {"type": "string"},
"body": {"type": "string"},
},
"required": ["to", "subject", "body"],
},
},
},
]
# 第一轮对话:请求工具调用
messages = [{"role": "user", "content": "北京明天天气怎么样?"}]
response = client.chat.completions.create(
model="gpt-4o",
messages=messages,
tools=tools,
tool_choice="auto", # auto | none | {"type": "function", "function": {"name": "xxx"}}
)
# model 决定调用 get_weather
print(response.choices[0].message) # role=assistant, content=None, tool_calls=[...]
# 解析工具调用
tool_call = response.choices[0].message.tool_calls[0]
print(f"调用的工具: {tool_call.function.name}")
print(f"参数: {tool_call.function.arguments}")
# 执行工具(模拟)
result = {"temp": 28, "weather": "晴", "humidity": 45}
# 第二轮:将结果归还模型
messages.append(response.choices[0].message) # 把模型请求加回去
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": tool_call.function.name,
"content": str(result),
})
final = client.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
print(final.choices[0].message.content) # "北京明天晴,28度..."
2.2 Claude Tool Use 详解
from anthropic import Anthropic
client = Anthropic()
# Claude 的工具声明与 OpenAI 几乎相同
tools = [
{
"name": "get_weather",
"description": "获取天气信息",
"input_schema": {
"type": "object",
"properties": {
"city": {"type": "string"},
"date": {"type": "string"},
},
"required": ["city"],
},
},
]
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1024,
messages=[{"role": "user", "content": "北京明天天气"}],
tools=tools,
)
# 检测是否使用了工具
if response.stop_reason == "tool_use":
tool_use = response.content[-1] # 最后一条 content block 是 tool_use
print(f"Claude 想调用: {tool_use.name},参数: {tool_use.input}")
3. Schema 设计最佳实践
3.1 description 是关键
模型通过 description 决定选哪个工具。这 100 字决定了成败:
# ❌ 差的描述
"description": "搜索功能"
# ✅ 好的描述
"description": "在商品数据库中根据关键词搜索商品信息并返回符合条件的 SKU 列表。适用于用户想要查找特定商品的讲候。不支持搜索用户个人信息。"
Schema Tips:
- 在 description 中明确包含"何时用这个工具"(触发条件)
- 提供约束条件:“日期格式必须是 YYYY-MM-DD”
- 提供枚举值:如果 city 只能是几个明确值,用
enum - 给出典型例子:使用
examples字段辅助理解
3.2 参数命名
# ❌ 模糊命名
"from": str # 从哪里?时间?地点?
# ✅ 自解释命名
"start_date": str
"origin_city": str
3.3 幂等工具
标记哪些工具是幂等的(可安全重复调用):
{
"name": "get_user_profile",
"description": "获取用户信息(幂等操作)。",
"parameters": {...},
"strict": True, # OpenAI 参数校验强化
}
3.4 限制并发
from typing import Callable, Dict
import asyncio
class RateLimitedTool:
def __init__(self, fn: Callable, max_concurrent: int = 5):
self.fn = fn
self.semaphore = asyncio.Semaphore(max_concurrent)
async def __call__(self, **kwargs):
async with self.semaphore:
return await self.fn(**kwargs)
4. 跨平台兼容层
class ToolRegistry:
"""跨 LLM 平台的工具注册器"""
def __init__(self):
self._tools: Dict[str, Callable] = {}
def register(self, name: str, description: str, fn: Callable, schema: dict):
self._tools[name] = {
"fn": fn,
"description": description,
"schema": schema,
}
return self
def to_openai_format(self):
return [
{
"type": "function",
"function": {
"name": name,
"description": meta["description"],
"parameters": meta["schema"],
},
}
for name, meta in self._tools.items()
]
def to_claude_format(self):
return [
{
"name": name,
"description": meta["description"],
"input_schema": meta["schema"],
}
for name, meta in self._tools.items()
]
def execute(self, tool_name: str, arguments: dict):
if tool_name not in self._tools:
raise ValueError(f"未知工具: {tool_name}")
# Schema 校验
import jsonschema
jsonschema.validate(arguments, self._tools[tool_name]["schema"])
return self._tools[tool_name]["fn"](**arguments)
# 使用
registry = ToolRegistry()
registry.register(
name="get_weather",
description="获取城市天气(当用户询问天气时使用)",
fn=lambda city, date: {"temp": 28, "weather": "晴"},
schema={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]},
)
# OpenAI 格式
openai_tools = registry.to_openai_format()
# Claude 格式
claude_tools = registry.to_claude_format()
5. 高级:自动工具发现
不需要手动注册所有工具——让 Agent 自己发现:
import inspect
class AutoToolDiscovery:
def __init__(self):
self.registry = {}
def discover(self, module):
"""扫描模块中所有 @tool 装饰的函数"""
for name, obj in inspect.getmembers(module, inspect.isfunction):
if hasattr(obj, "_tool_schema"):
self.registry[name] = {
"fn": obj,
"schema": obj._tool_schema,
"desc": obj._tool_desc,
}
# 装饰器用法
def tool(description, schema):
def decorator(fn):
fn._tool_schema = schema
fn._tool_desc = description
return fn
return decorator
# 应用
@tool(
description="获取天气信息",
schema={"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
)
def get_weather(city: str):
return f"{city} 今天晴,25度"
6. 错误处理与降级
class ResilientToolExecutor:
def execute(self, tool_name, arguments, fallback=None):
try:
result = self.registry.execute(tool_name, arguments)
return {"status": "success", "data": result}
except jsonschema.ValidationError as e:
return {
"status": "validation_error",
"message": f"参数错误: {e.message}",
"suggestion": f"请确保按此格式调用: {json.dumps(self.registry.get_schema(tool_name))}",
}
except Exception as e:
if fallback:
return fallback(arguments)
return {"status": "error", "message": str(e)}
7. 总结与选型建议
| 场景 | 推荐标准 | 理由 |
|---|---|---|
| 快速开发 MVP | OpenAI Function Calling | 文档最全、生态最广 |
| 长上下文 + 多模态 | Claude Tool Use | 200K 上下文,多模态优秀 |
| 控制成本 + 多 provider | Mistral Tool Use | API 成本最低 |
| 私有化部署 | Llama 3.1 + JSON Schema | 开源,离线运行 |
📂 继续阅读:
- AI 智能体架构设计 — 感知、推理、记忆、工具、执行五大组件
- Agent 工作流编排设计 — DAG 与状态机实现
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。