概念:必要基础

Tool Use / Function Calling 原理

Tool Use(也叫 Function Calling)让 LLM 不只是生成文本,还能调用外部工具。这是 Agent 从"只能聊天"变成"能做事"的关键能力。

来源: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 记忆系统