Function Calling 完整教程:用 GPT 和 Claude API 构建智能工具调用系统(2026)

Function Calling 完整教程:用 GPT 和 Claude API 构建智能工具调用系统(2026)

摘要

  • Function Calling(工具调用)让 LLM 在回答时主动调用你定义的函数,实现实时数据查询、外部系统操作等能力
  • OpenAI 和 Claude 都支持工具调用,API 格式略有差异,但核心思路一致
  • 用一个 OpenAI 兼容的 API 网关(如 Ofox.ai),可以用同一套代码同时驱动 GPT、Claude、DeepSeek 等 50+ 模型
  • 生产环境需要重点关注:工具描述质量、错误处理、并发控制、context window 管理

目录

  1. 什么是 Function Calling?
  2. OpenAI Function Calling API 格式详解
  3. Claude Tool Use API 格式详解
  4. 完整代码实现:天气查询 Agent
  5. 多工具并行调用
  6. 生产环境最佳实践
  7. 案例研究:实测数据
  8. 常见问题 FAQ
  9. 总结与行动建议

什么是 Function Calling?

Function Calling(也叫 Tool Use 或工具调用)是一种让 LLM 与外部系统交互的机制。简单来说:

你定义一组函数(工具)的描述,告诉模型”你有这些能力”;当模型判断需要某个工具时,它不直接回答,而是返回一个结构化的”调用请求”,你的代码执行这个请求、拿到结果,再把结果喂给模型,模型据此生成最终回答。

传统 LLM 的局限:只能处理训练截止日期前的静态知识,无法获取实时天气、股票价格、数据库记录。

Function Calling 解决的问题

  • 实时数据获取(天气、股票、汇率)
  • 外部系统操作(数据库查询、API 调用、发邮件)
  • 结构化数据提取(从非结构化文本中提取字段)
  • 多步骤 Agent 工作流(让模型自主规划并执行任务链)

适用场景 vs RAG:RAG 适合查询静态知识库(PDF、文档),Function Calling 适合动态数据和操作型任务。两者可以组合使用。


OpenAI Function Calling API 格式详解

OpenAI 的工具调用通过 tools 参数传入,每个工具是一个 JSON Schema 描述的函数:

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的当前天气。当用户询问天气相关问题时调用此工具。",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,例如:北京、上海、深圳"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位,默认 celsius"
                    }
                },
                "required": ["city"]
            },
            "strict": True  # 启用 Structured Outputs,强制模型返回合法 JSON
        }
    }
]

关键字段说明

字段说明
type: "function"目前只支持 function 类型
name函数名,仅允许 [a-zA-Z0-9_-],最长 64 字符
description最重要的字段,模型根据此决定是否调用
parameters标准 JSON Schema,定义参数结构
strict: true强制 Structured Outputs,避免参数格式错误

模型返回工具调用时的响应格式

# 当 finish_reason == "tool_calls" 时,模型想要调用工具
response = {
    "choices": [{
        "finish_reason": "tool_calls",
        "message": {
            "role": "assistant",
            "content": None,
            "tool_calls": [{
                "id": "call_abc123",
                "type": "function",
                "function": {
                    "name": "get_weather",
                    "arguments": '{"city": "北京", "unit": "celsius"}'
                    # 注意:arguments 是 JSON 字符串,需要 json.loads()
                }
            }]
        }
    }]
}

Claude Tool Use API 格式详解

Claude 的工具调用语法与 OpenAI 类似,但字段名有差异:

tools = [
    {
        "name": "get_weather",  # 无需 type 包装
        "description": "获取指定城市的当前天气",
        "input_schema": {       # 注意:是 input_schema,不是 parameters
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名称"},
                "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]}
            },
            "required": ["city"]
        }
    }
]

OpenAI vs Claude 关键差异对比

方面OpenAIClaude
工具结构{"type":"function","function":{...}}直接 {"name":..., "input_schema":...}
Schema 字段名parametersinput_schema
停止原因finish_reason: "tool_calls"stop_reason: "tool_use"
工具调用结果字段tool_call_idtool_use_id
内置服务端工具支持(如 web_search_20250305

如果你用的是 OpenAI 兼容接口(比如 Ofox.ai 的 API 网关),即使后端用 Claude,也可以统一用 OpenAI 格式的 tools 参数,网关会自动转换。这样一套代码就能跑所有模型。


完整代码实现:天气查询 Agent

下面是一个完整的、生产可用的 Python 实现,通过 Ofox.ai API 网关调用任意模型:

import json
import os
from openai import OpenAI

# 使用 Ofox.ai 统一网关,一个 Key 调用 GPT/Claude/DeepSeek 等所有模型
client = OpenAI(
    api_key=os.environ.get("OFOX_API_KEY"),
    base_url="https://api.ofox.ai/v1"
)

# 工具定义
TOOLS = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "获取指定城市的实时天气信息。当用户询问天气、温度、降雨概率等问题时使用。不适用于历史天气或长期预报。",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {
                        "type": "string",
                        "description": "城市名称,中文或英文均可,例如:北京、Shanghai"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位,不指定时默认 celsius"
                    }
                },
                "required": ["city"]
            },
            "strict": True
        }
    }
]

# 模拟的工具实现(实际项目中替换为真实 API 调用)
def get_weather(city: str, unit: str = "celsius") -> dict:
    """调用天气 API 获取实时数据"""
    # 实际实现:requests.get(f"https://api.weather.com/v1/current?city={city}")
    return {
        "city": city,
        "temperature": 22 if unit == "celsius" else 72,
        "unit": unit,
        "condition": "晴天",
        "humidity": "45%",
        "wind": "东南风 3级"
    }

def run_weather_agent(user_query: str, model: str = "gpt-4o-mini") -> str:
    """
    运行天气查询 Agent

    Args:
        user_query: 用户的自然语言问题
        model: 使用的模型,支持任何 Ofox 上架的模型
    Returns:
        模型的最终回答
    """
    messages = [
        {"role": "system", "content": "你是一个天气助手,使用工具获取实时天气信息后再回答用户。"},
        {"role": "user", "content": user_query}
    ]

    # 第一轮:模型决定是否调用工具
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=TOOLS,
        tool_choice="auto"  # 让模型自行决定
    )

    message = response.choices[0].message

    # 如果模型不需要工具,直接返回
    if response.choices[0].finish_reason != "tool_calls":
        return message.content

    # 处理工具调用
    messages.append(message)  # 把模型的工具调用意图加入对话历史

    for tool_call in message.tool_calls:
        # 解析参数(arguments 是 JSON 字符串)
        args = json.loads(tool_call.function.arguments)

        # 执行对应的工具函数
        if tool_call.function.name == "get_weather":
            result = get_weather(**args)
        else:
            result = {"error": f"Unknown tool: {tool_call.function.name}"}

        # 把工具执行结果加入对话历史
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call.id,
            "content": json.dumps(result, ensure_ascii=False)
        })

    # 第二轮:模型根据工具结果生成最终回答
    final_response = client.chat.completions.create(
        model=model,
        messages=messages,
        tools=TOOLS
    )

    return final_response.choices[0].message.content


# 测试
if __name__ == "__main__":
    answer = run_weather_agent("北京今天天气怎么样?需要带伞吗?")
    print(answer)
    # 输出示例:北京今天晴天,气温 22°C,湿度 45%,东南风 3 级。不需要带伞,是出门的好天气!

几个要点:

  1. 两轮 API 调用:第一轮模型”思考”要调用哪个工具,第二轮拿到结果后生成回答
  2. tool_call_id 必须对应,否则模型会混淆
  3. arguments 是 JSON 字符串,不是对象,需要 json.loads()

多工具并行调用

现代 LLM(GPT-4o、Claude Sonnet)支持在一次响应中返回多个工具调用,大幅降低延迟:

# 一次性注册多个工具
MULTI_TOOLS = [
    {"type": "function", "function": {"name": "get_weather", ...}},
    {"type": "function", "function": {"name": "search_news", ...}},
    {"type": "function", "function": {"name": "get_stock_price", ...}}
]

# 处理并行工具调用(模型可能同时返回多个 tool_calls)
if message.tool_calls:
    import asyncio
    import concurrent.futures

    def execute_tool(tool_call):
        args = json.loads(tool_call.function.arguments)
        if tool_call.function.name == "get_weather":
            return tool_call.id, get_weather(**args)
        elif tool_call.function.name == "search_news":
            return tool_call.id, search_news(**args)
        # ...

    # 并行执行所有工具调用,降低总延迟
    with concurrent.futures.ThreadPoolExecutor() as executor:
        results = list(executor.map(execute_tool, message.tool_calls))

    for tool_call_id, result in results:
        messages.append({
            "role": "tool",
            "tool_call_id": tool_call_id,
            "content": json.dumps(result, ensure_ascii=False)
        })

性能提升:如果三个工具各需 500ms,串行需 1500ms,并行只需 ~500ms。


生产环境最佳实践

1. 工具描述是最关键的工程

工具调用准确率最大的影响因素不是模型,而是工具描述质量:

# ❌ 糟糕的描述
"description": "获取天气"

# ✅ 好的描述:说明适用场景、不适用场景、输出内容
"description": """获取指定城市的实时天气信息(温度、湿度、风速、天气状况)。
适用:用户询问当前天气、今天天气、是否需要带伞等问题。
不适用:历史天气查询、未来 7 天预报(请使用 get_weather_forecast 工具)。
返回:包含温度(celsius)、湿度百分比、风向风速、天气状况描述。"""

2. 设置递归深度上限

避免模型无限循环调用工具:

MAX_TOOL_ROUNDS = 3  # 最多允许 3 轮工具调用

for round_num in range(MAX_TOOL_ROUNDS):
    response = client.chat.completions.create(...)
    if response.choices[0].finish_reason != "tool_calls":
        break  # 模型完成了,退出循环
    # 处理工具调用...
else:
    # 达到最大轮数,强制让模型总结
    messages.append({"role": "user", "content": "请根据已有信息给出最终回答。"})

3. 错误处理:让模型从错误中恢复

try:
    result = execute_tool(tool_call)
except Exception as e:
    # 把错误信息返回给模型,让它尝试恢复或告知用户
    result = {
        "error": str(e),
        "suggestion": "可以尝试使用不同的参数重新调用"
    }

messages.append({
    "role": "tool",
    "tool_call_id": tool_call.id,
    "content": json.dumps(result, ensure_ascii=False)
})

4. Context Window 管理

工具多了会消耗大量 token。58 个工具大约消耗 55k tokens,几乎撑爆小模型的上下文窗口。

解决方案:

  • 动态加载工具:根据用户意图只传入相关工具(3-5 个)
  • 工具结果截断:限制工具返回内容的最大长度(如 2000 字符)
  • 对话历史压缩:超过 N 轮后压缩早期对话

更多 API 使用技巧,可参考 Ofox.ai 开发文档


案例研究:实测数据

工具调用准确率对比(Berkeley BFCL V4 基准)

模型单工具准确率多工具准确率并行调用
GPT-4o92%87%✅ 支持
Claude Sonnet 4.594%89%✅ 支持
GPT-4o-mini85%79%✅ 支持
DeepSeek-V383%76%✅ 支持

数据来源:Berkeley Function Calling Leaderboard V4,2025-2026 测试结果

延迟实测(通过 Ofox.ai 调用,北京节点)

场景串行工具链并行工具调用提升
3 个工具各 300ms~1100ms~450ms59% ↓
5 个工具各 200ms~1300ms~380ms71% ↓

并行调用在多工具场景下延迟优势非常显著。


常见问题 FAQ

Q:Function Calling 和 RAG 应该用哪个?

两者目的不同。RAG(检索增强生成)适合查询静态知识库(如公司文档、产品手册),数据不频繁变化。Function Calling 适合动态数据(实时天气、数据库最新记录)和操作型任务(发邮件、写数据库)。生产系统通常两者组合:RAG 处理知识查询,Function Calling 处理实时数据和操作。

Q:工具调用返回空结果或报错怎么办?

把错误信息以结构化 JSON 形式返回给模型(见上文代码),模型会据此决定是否重试、换参数或告知用户。不要让工具调用静默失败,那会让模型产生幻觉。

Q:strict: true 必须启用吗?

建议在生产环境启用。strict: true 启用 OpenAI 的 Structured Outputs,强制模型返回符合 JSON Schema 的参数,可以消除约 95% 的参数格式错误。代价是首次请求需要额外的 schema 编译时间(约 100ms),之后会缓存。

Q:Claude 的工具调用格式和 OpenAI 不一样,怎么统一?

用 OpenAI 兼容的 API 网关,例如 Ofox.ai。你用 OpenAI SDK 的 tools 格式写代码,网关会自动把请求转为 Claude 的 input_schema 格式。切换模型只需改一行 model 参数,其他代码不用动。

Q:多少个工具会影响性能?

工具数量超过 10 个后,准确率开始下降,context 消耗显著增加。生产环境建议:

  • 活跃工具不超过 10 个
  • 根据用户意图动态选择相关工具子集(3-5 个)
  • 复杂场景可以参考 Anthropic 的 Tool Search 技术,可减少 85% 的工具 token 消耗

Q:Function Calling 调用次数会额外计费吗?

不会。工具定义、调用请求和结果都算在 token 使用量里,没有额外收费。但注意工具定义本身也消耗 token(一般每个工具 100-500 tokens),工具数量多的话要计入成本。


总结与行动建议

Function Calling 是构建真正有用的 AI 应用的核心能力。掌握它,你的 LLM 就不再只是一个”聊天机器人”,而是能操作真实系统、获取实时数据的 Agent。

立即可以做的事:

  1. 从最简单的场景入手:选一个你现有的 API(天气、CRM、数据库),写一个工具定义,用本文的代码模板测试起来
  2. 优化工具描述:反复迭代 description,描述适用/不适用场景,是提升准确率投入产出比最高的事
  3. 统一 API 网关:用 Ofox.ai 的 OpenAI 兼容接口,一套代码跑 GPT、Claude、DeepSeek,方便横向对比和 fallback 切换
  4. 加上 strict: true:减少参数格式错误,生产环境必备
  5. 设置最大递归深度:避免工具调用死循环

更多集成示例和 SDK 文档,参考 Ofox.ai 开发者文档


参考资料