来源:Anthropic Tool Use · OpenAI Function Calling · MCP Specification | 整理时间:2026-05-14
什么是 Tool Use?
Tool Use(也叫 Function Calling)让 LLM 不只是生成文本,还能调用外部工具。这是 Agent 从"只能聊天"变成"能做事"的关键能力。
没有 Tool Use:
用户 → LLM → 文本回复
有 Tool Use:
用户 → LLM → 决定调用工具 → 执行工具 → LLM 处理结果 → 回复
底层机制
Step 1:告诉模型有哪些工具
{
"tools": [
{
"name": "get_weather",
"description": "获取指定城市的天气预报",
"input_schema": {
"type": "object",
"properties": {
"city": { "type": "string", "description": "城市名" },
"unit": { "type": "string", "enum": ["celsius", "fahrenheit"] }
},
"required": ["city"]
}
}
]
}
关键要素:
- name:工具名称,模型用它来选择工具
- description:工具描述,模型根据它判断什么时候用
- input_schema:JSON Schema 定义参数类型和约束
Step 2:模型决定是否调用工具
模型收到用户消息 + 工具列表后,有三种选择:
| 选择 | 模型输出 | 说明 |
|---|---|---|
| 直接回答 | 文本 | 问题不需要工具 |
| 调用一个工具 | tool_use block |
需要外部信息 |
| 调用多个工具 | 多个 tool_use blocks |
并行获取多个信息 |
Step 3:执行工具并返回结果
// 模型输出:调用工具
{
"type": "tool_use",
"name": "get_weather",
"input": { "city": "上海" }
}
// 你执行工具,返回结果
{
"type": "tool_result",
"tool_use_id": "toolu_xxx",
"content": "上海:晴,28°C,东南风 3 级"
}
// 模型根据结果生成最终回复
"上海今天天气晴朗,气温 28°C,适合外出。"
Step 4:多轮工具调用
Agent 的核心循环——工具调用可以链式进行:
用户: "帮我查一下明天上海适不适合跑步"
Agent 思考链:
1. 调用 get_weather("上海", "明天") → 多云,15°C
2. 调用 check_air_quality("上海", "明天") → AQI 45,优
3. 综合判断 → "适合,气温适宜,空气质量好"
跨模型的差异
不同模型提供商的 Tool Use 实现有差异:
| 维度 | Anthropic (Claude) | OpenAI (GPT) |
|---|---|---|
| 参数名 | tools |
tools / functions |
| Schema 格式 | JSON Schema | JSON Schema |
| 并行调用 | 支持 | 支持 |
| 强制调用 | tool_choice: {"name": "xxx"} |
tool_choice: {"function": {"name": "xxx"}} |
| 禁止调用 | tool_choice: {"type": "none"} |
tool_choice: "none" |
| 自动选择 | tool_choice: {"type": "auto"} |
tool_choice: "auto" |
| 返回格式 | tool_use content block |
tool_calls in message |
跨模型通用写法
# 定义工具(两边通用)
tools = [{
"name": "search",
"description": "搜索文档",
"input_schema": {
"type": "object",
"properties": {
"query": {"type": "string"}
},
"required": ["query"]
}
}]
# 执行工具(通用逻辑)
def execute_tool(name: str, params: dict) -> str:
if name == "search":
return search_docs(params["query"])
raise ValueError(f"Unknown tool: {name}")
调试工具调用问题
问题一:模型选错工具
原因:工具描述不够清晰,或工具之间功能重叠
解决:
// 差的描述
{"name": "search", "description": "搜索"}
// 好的描述
{"name": "search_docs", "description": "在项目文档目录中按关键词搜索 Markdown 和文本文件"}
问题二:参数格式错误
原因:模型生成的参数不符合 JSON Schema
解决:
# 在执行前验证
import jsonschema
def safe_execute_tool(name, params):
schema = get_tool_schema(name)
try:
jsonschema.validate(params, schema)
except jsonschema.ValidationError as e:
return f"参数错误: {e.message}"
return execute_tool(name, params)
问题三:工具循环调用
原因:模型反复调用同一个工具,陷入死循环
解决:
MAX_TOOL_CALLS = 10
for i in range(MAX_TOOL_CALLS):
response = call_llm(messages, tools)
if not response.tool_calls:
break # 模型不再调用工具
# 执行工具...
else:
# 超过限制,强制终止
return "Agent 达到最大工具调用次数,请简化请求"
问题四:并行调用的依赖
原因:模型想并行调用两个有依赖关系的工具
解决:在设计工具时避免依赖,或者拆分成串行步骤
// 避免:两个有依赖的工具
{"name": "create_file", ...}
{"name": "write_to_file", ...} // 需要 create_file 先完成
// 改进:合并为一个
{"name": "create_and_write_file", ...}
Tool Use vs MCP
Tool Use 是模型层面的能力,MCP 是协议层面的标准:
Tool Use(模型能力)
└─ 模型如何决定调用什么工具、传什么参数
MCP(协议标准)
└─ 工具怎么注册、怎么发现、怎么通信
关系:MCP 建立在 Tool Use 之上
MCP Server 暴露工具 → 客户端注册为可用工具 → 模型通过 Tool Use 调用
| 维度 | Tool Use | MCP |
|---|---|---|
| 解决的问题 | 模型怎么调用工具 | 工具怎么被发现和注册 |
| 层级 | 模型推理层 | 协议通信层 |
| 通用性 | 所有 LLM 都有 | MCP 生态专用 |
| 开发者需要 | 定义工具 schema | 实现 MCP Server |
设计工具的最佳实践
工具粒度
太粗: manage_issues(action, data) // 一个工具做所有事
太细: create_issue_title(title), create_issue_body(body), ... // 拆太碎
正好: create_issue(title, body, labels) // 一个工具完成一个完整操作
命名和描述
| 规则 | 好例子 | 差例子 |
|---|---|---|
| 动词开头 | search_issues |
issue_search |
| 描述具体 | "按关键词搜索 GitHub Issues" | "搜索" |
| 参数有描述 | query: "搜索关键词,支持 GitHub 搜索语法" |
query: "关键词" |
| 包含边界 | "返回最多 30 条结果" |
"返回结果" |
错误处理
@mcp.tool()
def create_issue(title: str, repo: str) -> str:
"""在指定仓库创建 Issue。"""
if not title.strip():
return "错误:标题不能为空" # 返回有意义的错误消息
try:
issue = github.create_issue(repo, title)
return f"Issue #{issue.number} 已创建"
except PermissionError:
return "错误:没有该仓库的写权限"
except Exception as e:
return f"创建失败: {str(e)}"
如果你要把 Tool Use 变成可复用的工具生态,下一步读 MCP 协议详解;如果问题是跨轮对话保留状态,再看 Agent 记忆系统。