Function Calling完整调用教程

Function Calling 完整调用教程:让模型自己调你的函数

Function Calling(函数调用)解决的是同一个核心问题:让模型输出的不再是自由文本,而是一个结构化的调用请求。

传统的 AI 对话,模型给你一段文字,你再自己解析这段文字去执行操作。Function Calling 改变了这个范式------模型直接告诉你「我要调用 get_weather,参数是 location=杭州」,你负责执行函数、把结果传回去,模型再基于真实数据给出最终回答。整个过程中,模型只负责决策,不负责执行。

这套机制是 OpenAI 在 2023 年 6 月引入的(gpt-3.5-turbo-0613 / gpt-4-0613),随后被 Claude、DeepSeek 等各大平台兼容。现在主流的 Function Calling 实现已经相当成熟,支持并行调用(一次回复触发多个函数)、严格模式(Structured Outputs 保证参数格式分毫不差)。

本文聚焦「怎么写对」,完整覆盖:tool 定义 → 调用 → 解析返回 → 执行函数 → 回传结果的闭环。关于报错的具体原因和解决方案,参见另一篇《JSON 模式/结构化输出报错怎么办?》。


1. 完整调用流程:一次对话,两次请求

Function Calling 通常需要两次模型调用才能完成一轮完整交互:

复制代码
用户输入
    ↓
第一次请求 → 模型判断要调哪个函数 → 返回 tool_calls
    ↓
你执行函数,获取结果
    ↓
第二次请求 → 把函数结果传回去 → 模型生成最终回复

下面用 Python 演示这个闭环。

1.1 定义工具(tools)

python 复制代码
from openai import OpenAI
import json

client = OpenAI(
    api_key="YOUR_API_KEY",
    base_url="YOUR_BASE_URL",  # OpenAI 用 https://api.openai.com/v1
                                # DeepSeek 用 https://api.deepseek.com
)

tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的当前天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名称,如杭州、东京"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"],
                        "description": "温度单位,默认 celsius"
                    }
                },
                "required": ["location"],
                "additionalProperties": False  # 严格模式建议加,禁止返回未定义字段
            }
        }
    }
]

参数定义要点:

  • type 必须是 "object"
  • required 列出所有必填字段
  • description 字段非常重要,模型靠它理解什么时候该调这个工具
  • additionalProperties: false 防止模型返回未定义的额外字段
  • 工具名称只允许 a-z、A-Z、0-9、_、-,最大 64 字符

1.2 第一次请求:让模型决定调用哪个函数

python 复制代码
messages = [
    {"role": "user", "content": "杭州今天热不热?"}
]

response = client.chat.completions.create(
    model="YOUR_MODEL",  # 如 gpt-4o、claude-sonnet-4-5、deepseek-v4-flash
    messages=messages,
    tools=tools,
    tool_choice="auto"  # auto: 模型自己决定是否调用;required: 强制调用
)

message = response.choices[0].message
print(message.tool_calls)

正常情况下,返回值中 tool_calls 不为空:

python 复制代码
# message.tool_calls 示例
[
    ToolCall(
        id="call_abc123",
        type="function",
        function=Function(
            name="get_weather",
            arguments='{"location": "杭州", "unit": "celsius"}'
        )
    )
]

1.3 解析并执行函数

python 复制代码
def execute_tool(tool_call):
    """根据 tool_call 的 name 和 arguments 执行对应函数"""
    function_name = tool_call.function.name
    arguments = json.loads(tool_call.function.arguments)

    if function_name == "get_weather":
        return get_weather(**arguments)
    else:
        raise ValueError(f"未知函数: {function_name}")


def get_weather(location: str, unit: str = "celsius") -> dict:
    """这里是你的业务逻辑,比如调用真实天气 API"""
    # 模拟返回
    return {
        "location": location,
        "temperature": 28 if unit == "celsius" else 82,
        "unit": unit,
        "condition": "晴转多云"
    }


# 执行所有调用的函数
if message.tool_calls:
    for tool_call in message.tool_calls:
        result = execute_tool(tool_call)
        print(f"函数 {tool_call.function.name} 返回: {result}")

1.4 第二次请求:把结果传回模型

python 复制代码
# 把模型的 tool_calls 和函数执行结果都加到对话历史里
messages.append(message)  # 模型返回的 tool_calls 消息

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

# 第二次请求
final_response = client.chat.completions.create(
    model="YOUR_MODEL",
    messages=messages,
    tools=tools
)

print(final_response.choices[0].message.content)
# 模型输出:「杭州今天天气晴转多云,气温 28°C。」

这才是用户最终看到的回复。


2. 多工具选择:让模型智能调度

当你有多个工具时,模型会根据用户意图自动选择最合适的一个,或者同时调用多个。

2.1 典型多工具场景

python 复制代码
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "description": "查询指定城市的当前天气和空气质量",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {"type": "string", "description": "城市名称"}
                },
                "required": ["location"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_flights",
            "description": "搜索两个城市间的航班信息",
            "parameters": {
                "type": "object",
                "properties": {
                    "origin": {"type": "string", "description": "出发城市"},
                    "destination": {"type": "string", "description": "目的城市"},
                    "date": {"type": "string", "description": "出发日期,格式 YYYY-MM-DD"}
                },
                "required": ["origin", "destination", "date"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "send_email",
            "description": "发送邮件",
            "parameters": {
                "type": "object",
                "properties": {
                    "to": {"type": "string", "format": "email", "description": "收件人邮箱"},
                    "subject": {"type": "string", "description": "邮件主题"},
                    "body": {"type": "string", "description": "邮件正文"}
                },
                "required": ["to", "subject", "body"]
            }
        }
    }
]

2.2 并行调用处理

支持并行的模型(GPT-4o、o3、Claude 4.x、DeepSeek-v4 等)可以一次返回多个 tool_calls:

python 复制代码
# 处理并行调用
tool_results = []

if message.tool_calls:
    for tool_call in message.tool_calls:
        try:
            result = execute_tool(tool_call)
            tool_results.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps(result, ensure_ascii=False)
            })
        except Exception as e:
            tool_results.append({
                "role": "tool",
                "tool_call_id": tool_call.id,
                "content": json.dumps({"error": str(e)}, ensure_ascii=False)
            })

    messages.append(message)
    messages.extend(tool_results)

并行调用的注意事项:

  • 某些严格模式(strict: true)不支持并行调用,并行时可能不保证参数格式
  • 收到多个 tool_calls 时,所有结果必须全部传回模型才能得到最终回复
  • 如果某个函数执行失败,建议返回错误 JSON 而不是空内容,这样模型可以决定如何处理

2.3 强制调用特定工具

如果你希望模型必须调用某个工具(而不是自己选择是否调用):

python 复制代码
# 强制调用单个工具
response = client.chat.completions.create(
    model="YOUR_MODEL",
    messages=messages,
    tools=tools,
    tool_choice={
        "type": "function",
        "function": {"name": "get_weather"}
    }
)

3. 严格模式(Structured Outputs)

2024 年 6 月,OpenAI 推出 Structured Outputs,DeepSeek 在 Beta API 中也支持了 strict: true。开启后,模型生成的参数会数学级别保证符合你定义的 JSON Schema,不再依赖模型"努力写对"。

3.1 启用严格模式

python 复制代码
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_weather",
            "strict": True,  # 开启严格模式
            "description": "查询天气",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "城市名"
                    },
                    "unit": {
                        "type": "string",
                        "enum": ["celsius", "fahrenheit"]
                    }
                },
                "required": ["location"],
                "additionalProperties": False
            }
        }
    }
]

严格模式对 Schema 的强制要求(OpenAI 规范):

  • 所有对象必须设置 additionalProperties: false
  • required 数组必须包含对象的所有属性
  • 不支持 prefixItems(用 items 代替)
  • 不支持 minItems / maxItems / minLength / maxLength
  • 支持 pattern、enum、format(email、hostname、ipv4、ipv6、uuid)

3.2 严格模式 vs 普通模式

特性 普通模式 严格模式(strict: true)
参数格式保证 模型 best-effort 100% 格式保证
Schema 约束 较宽松 严格(必须满足上述要求)
并行调用 支持 不支持
适用场景 快速迭代 生产环境

4. 与报错排查文章的区别

本文解决的是「代码怎么写、功能怎么实现」的问题。

那篇《JSON 模式/结构化输出报错怎么办?》解决的是「代码跑起来了但报错了」的问题。两篇文章分工如下:

维度 本文(调用教程) 那篇(报错排查)
核心问题 怎么写对 Function Calling 写错了怎么排查
关注点 流程、Schema 设计、多工具 错误码、模型兼容性、截断
典型场景 第一次接入,不会写 接入后遇到具体报错

5. 常见坑

坑 1:Schema 漏写 type: "object"

DeepSeek、部分第三方兼容端点会严格校验,缺少 "type": "object" 直接返回 400:

python 复制代码
# ❌ 错误:漏了 type
"parameters": {
    "properties": {"location": {"type": "string"}},
    "required": ["location"]
}

# ✅ 正确
"parameters": {
    "type": "object",
    "properties": {"location": {"type": "string"}},
    "required": ["location"]
}

坑 2:单属性 Schema 在某些端点报错

OpenAI 社区发现,只有单个属性 的 Schema 在部分模型/端点组合下会报 Invalid schema 错误。解决方法是加一个无意义的辅助字段:

python 复制代码
# ❌ 可能报错
"parameters": {
    "type": "object",
    "properties": {"items": {"type": "array", "items": {"type": "string"}}},
    "required": ["items"]
}

# ✅ 加一个 count 字段
"parameters": {
    "type": "object",
    "properties": {
        "items": {"type": "array", "items": {"type": "string"}},
        "count": {"type": "integer"}
    },
    "required": ["items", "count"]
}

坑 3:并行调用 + 严格模式混用

严格模式下开启并行调用,参数格式不再保证。生产环境如需两者兼得,需要自己在解析后做 JSON Schema 校验。

坑 4:tool_call_id 不匹配

每次请求中 tool_call.id 都是新生成的,传回时必须使用上一次返回的 id,不能自己造一个。

坑 5:messages 历史里塞了 reasoning_content

DeepSeek-v4-pro 等模型会在响应中包含 reasoning_content(内部推理链),多轮对话时只应把 content 加入历史,reasoning_content 不应加入,否则会干扰后续调用。


快速排错表

错误信息 原因 解决方式
Invalid schema for function Schema 缺少 type: object 或使用了不支持的 JSON Schema 特性 检查 Schema,移除 prefixItems、minItems 等不支持的关键字
required is required to be an array including every key strict 模式下所有属性必须出现在 required 数组中 把所有属性加入 required,或把 optional 字段设为 null 联合类型
array schema missing items 数组参数用了 prefixItems 但缺少 items 改用 items 关键字
模型不调用任何函数 system prompt 或 tool description 写得不清楚 优化 description,确保 prompt 中明确要求调用工具
多个 tool_calls 但只有部分执行了 并行调用处理逻辑有遗漏 遍历所有 tool_calls,全部执行并传回结果
二次请求后模型回复很奇怪 messages 历史构建顺序错误 按 [user, assistant, tool_result, assistant, tool_result...] 顺序排列
finish_reason="length" max_tokens 太小导致输出截断 增大 max_tokens

配置检查清单

接入 Function Calling 前,逐项确认以下配置:

工具定义

  • type 字段设为 "function"(外层)和 "object"(parameters)
  • name 只含 a-zA-Z0-9_-,不超过 64 字符
  • 每个 property 有 description,模型靠它理解语义
  • required 数组列出了所有必填字段
  • 严格模式下所有对象加了 additionalProperties: false
  • 没有使用 prefixItems、minItems、maxItems 等不支持的特性

API 调用

  • 使用支持 Function Calling 的模型(GPT-4o、Claude 4.x、DeepSeek-v4 等)
  • max_tokens 设置足够大,避免截断
  • 第一次请求正确传递 tools 和 tool_choice 参数
  • 收到 tool_calls 后正确解析 tool_call.id 和 function.arguments
  • 第二次请求把所有 tool 消息和原始 assistant 消息都加入 messages
  • tool_call_id 使用上一次返回的真实 id,不自行生成

结果处理

  • 函数执行异常时返回错误 JSON 而非抛异常
  • 多 tool_calls 场景下全部执行完毕再发起第二次请求
  • 不把 reasoning_content 加入多轮对话历史(DeepSeek 等模型)
  • 解析 arguments 用 json.loads() 而非直接当 dict 用
相关推荐
IvorySQL13 分钟前
PostgreSQL 日报|恢复期 COPY TO 未日志表修复(10 月 21 日)
数据库·postgresql·区块链
程序员海军34 分钟前
用了两年多 GPT,我最近开始换模型了,还折腾了个求职避坑 Agent
前端·人工智能·后端
AIsoft_868941 分钟前
人才评估中的关键信息如何沉淀?
人工智能·iphone
紧跟先前的步伐1 小时前
Go AI Agent 实战 06|JSON 结构化输出与 Go 后端校验
人工智能·ai·golang
贝猫说python1 小时前
大模型比赛2.3智能体开发(工具调用智能问答助手)
数据库·oracle
vibecoding772 小时前
Codex 分层用模型完整教程:让 GPT-6.1 Sol 拍板、GPT-6 Luna 跑侦察
人工智能·gpt
新知图书2 小时前
1.3 启动第一个机器人
人工智能·机器人
明志数科3 小时前
自主探索范式下机器人示教数据的工程定位:少量示范为何对质量更敏感
人工智能·机器学习·机器人
许彰午3 小时前
28-ORA-01110数据文件读取错误
数据库·oracle
凯凯姜3 小时前
MySQL 联合索引设计与索引失效
数据库·mysql