] }

OpenAI API Function Calling 教程

什么是 OpenAI API Function Calling

OpenAI API Function Calling(函数调用)是 OpenAI 在 2023 年推出的一项强大功能,它允许开发者将自定义函数描述传递给 GPT 模型,让模型能够智能地决定何时调用这些函数,并生成符合函数参数要求的 JSON 数据。这项功能极大地扩展了 AI 应用的能力边界,使得 GPT 模型可以与外部系统、数据库、API 服务无缝集成。

通过 OpenAI API Function Calling 教程,你可以构建出能够查询天气、预订酒店、操作数据库、控制智能家居等复杂功能的 AI 应用。本文将从基础概念到实战代码,全面讲解如何使用这一功能。

Function Calling 的工作原理

OpenAI 函数调用的核心流程分为三个步骤:

  1. 函数定义:开发者向 API 提供函数的名称、描述和参数结构(JSON Schema 格式)
  2. 模型判断:GPT 模型根据用户输入和函数描述,决定是否需要调用函数,并生成符合参数要求的 JSON
  3. 执行返回:开发者在本地执行函数,将结果返回给模型,模型基于结果生成最终回复

这种设计让模型保持无状态,所有函数执行都在开发者的控制之下,既保证了安全性,又提供了极大的灵活性。

快速开始:第一个 Function Calling 示例

Python 实现

以下是一个完整的 Python 示例,演示如何使用 OpenAI API Function Calling 实现天气查询功能:

import openai
import json

# 初始化 OpenAI 客户端
client = openai.OpenAI(api_key="your-api-key")

# 定义函数描述
functions = [
    {
        "name": "get_weather",
        "description": "获取指定城市的天气信息",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "城市名称,例如:北京、上海"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "温度单位"
                }
            },
            "required": ["location"]
        }
    }
]

# 模拟天气查询函数
def get_weather(location, unit="celsius"):
    # 实际应用中这里应该调用真实的天气 API
    weather_data = {
        "北京": {"temp": 15, "condition": "晴朗"},
        "上海": {"temp": 20, "condition": "多云"}
    }
    return json.dumps(weather_data.get(location, {"error": "城市未找到"}))

# 第一次调用:让模型决定是否需要调用函数
response = client.chat.completions.create(
    model="gpt-4-turbo",
    messages=[{"role": "user", "content": "北京今天天气怎么样?"}],
    functions=functions,
    function_call="auto"
)

message = response.choices[0].message

# 检查模型是否要求调用函数
if message.function_call:
    function_name = message.function_call.name
    function_args = json.loads(message.function_call.arguments)
    
    # 执行函数
    if function_name == "get_weather":
        function_response = get_weather(**function_args)
        
        # 第二次调用:将函数结果返回给模型
        second_response = client.chat.completions.create(
            model="gpt-4-turbo",
            messages=[
                {"role": "user", "content": "北京今天天气怎么样?"},
                message,
                {
                    "role": "function",
                    "name": function_name,
                    "content": function_response
                }
            ]
        )
        
        print(second_response.choices[0].message.content)

Node.js 实现

对于 Node.js 开发者,以下是等效的实现:

const OpenAI = require('openai');

const client = new OpenAI({
  apiKey: 'your-api-key'
});

const functions = [
  {
    name: 'get_weather',
    description: '获取指定城市的天气信息',
    parameters: {
      type: 'object',
      properties: {
        location: {
          type: 'string',
          description: '城市名称,例如:北京、上海'
        },
        unit: {
          type: 'string',
          enum: ['celsius', 'fahrenheit']
        }
      },
      required: ['location']
    }
  }
];

function getWeather(location, unit = 'celsius') {
  const weatherData = {
    '北京': { temp: 15, condition: '晴朗' },
    '上海': { temp: 20, condition: '多云' }
  };
  return JSON.stringify(weatherData[location] || { error: '城市未找到' });
}

async function main() {
  const response = await client.chat.completions.create({
    model: 'gpt-4-turbo',
    messages: [{ role: 'user', content: '北京今天天气怎么样?' }],
    functions: functions,
    function_call: 'auto'
  });

  const message = response.choices[0].message;

  if (message.function_call) {
    const functionName = message.function_call.name;
    const functionArgs = JSON.parse(message.function_call.arguments);
    
    const functionResponse = getWeather(functionArgs.location, functionArgs.unit);
    
    const secondResponse = await client.chat.completions.create({
      model: 'gpt-4-turbo',
      messages: [
        { role: 'user', content: '北京今天天气怎么样?' },
        message,
        {
          role: 'function',
          name: functionName,
          content: functionResponse
        }
      ]
    });
    
    console.log(secondResponse.choices[0].message.content);
  }
}

main();

进阶技巧:多函数调用与并行处理

在实际应用中,你可能需要定义多个函数供模型选择。OpenAI API Function Calling 教程的进阶部分包括如何处理多函数场景:

定义多个函数

functions = [
    {
        "name": "get_weather",
        "description": "获取天气信息",
        "parameters": {...}
    },
    {
        "name": "book_hotel",
        "description": "预订酒店",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string"},
                "check_in": {"type": "string", "format": "date"},
                "check_out": {"type": "string", "format": "date"}
            },
            "required": ["city", "check_in", "check_out"]
        }
    },
    {
        "name": "search_flights",
        "description": "搜索航班",
        "parameters": {...}
    }
]

并行函数调用(GPT-4 Turbo 支持)

从 GPT-4 Turbo 开始,模型支持在一次响应中调用多个函数:

response = client.chat.completions.create(
    model="gpt-4-turbo",
    messages=[{
        "role": "user", 
        "content": "帮我查一下北京的天气,并预订明天到后天的酒店"
    }],
    functions=functions,
    function_call="auto"
)

# 处理多个函数调用
for tool_call in response.choices[0].message.tool_calls:
    function_name = tool_call.function.name
    function_args = json.loads(tool_call.function.arguments)
    # 执行对应函数...

最佳实践与常见陷阱

1. 函数描述要清晰准确

模型完全依赖你提供的 description 字段来理解函数用途。描述越详细,模型的判断越准确。

2. 参数验证不可省略

虽然模型会尽力生成符合 JSON Schema 的参数,但仍需在代码中进行验证:

def get_weather(location, unit="celsius"):
    if not location:
        return json.dumps({"error": "location 参数不能为空"})
    if unit not in ["celsius", "fahrenheit"]:
        return json.dumps({"error": "unit 参数无效"})
    # 正常处理...

3. 处理函数调用失败

当函数执行出错时,应该将错误信息返回给模型,让它生成友好的用户提示:

try:
    result = call_external_api(params)
    function_response = json.dumps(result)
except Exception as e:
    function_response = json.dumps({"error": str(e)})

4. 控制函数调用行为

function_call 参数提供三种模式:

实战案例:构建智能客服系统

以下是一个完整的智能客服示例,整合了订单查询、退款申请和常见问题解答:

functions = [
    {
        "name": "query_order",
        "description": "查询订单状态",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string", "description": "订单号"}
            },
            "required": ["order_id"]
        }
    },
    {
        "name": "request_refund",
        "description": "申请退款",
        "parameters": {
            "type": "object",
            "properties": {
                "order_id": {"type": "string"},
                "reason": {"type": "string", "description": "退款原因"}
            },
            "required": ["order_id", "reason"]
        }
    }
]

def handle_customer_service(user_message):
    messages = [{"role": "user", "content": user_message}]
    
    while True:
        response = client.chat.completions.create(
            model="gpt-4-turbo",
            messages=messages,
            functions=functions
        )
        
        message = response.choices[0].message
        
        if not message.function_call:
            return message.content
        
        # 执行函数
        function_name = message.function_call.name
        function_args = json.loads(message.function_call.arguments)
        
        if function_name == "query_order":
            result = query_order_from_db(function_args["order_id"])
        elif function_name == "request_refund":
            result = process_refund(function_args["order_id"], function_args["reason"])
        
        messages.append(message)
        messages.append({
            "role": "function",
            "name": function_name,
            "content": json.dumps(result)
        })

性能优化与成本控制

使用 OpenAI API Function Calling 时需要注意成本控制:

对于需要频繁调用 API 的应用,可以考虑使用 API 中转服务来优化请求路由和成本管理,这些服务通常提供负载均衡、请求缓存和更灵活的计费方式。

常见问题解答

Function Calling 支持哪些 OpenAI 模型?

目前支持函数调用的模型包括:gpt-4、gpt-4-turbo、gpt-4o、gpt-3.5-turbo(0613 版本及以后)。推荐使用 gpt-4-turbo 或 gpt-4o,它们支持并行函数调用和更复杂的参数推理。

函数调用会增加多少 Token 消耗?

函数定义会计入输入 Token,每个函数的描述和参数结构大约消耗 50-200 个 Token。函数调用的响应(包含 function_call 字段)也会计入输出 Token。建议在开发时使用 response.usage 监控实际消耗。

如何让模型更准确地选择函数?

关键在于函数的 description 字段。应该清晰描述函数的用途、适用场景和限制条件。例如:"查询指定日期范围内的订单,日期格式为 YYYY-MM-DD,最多支持查询 90 天内的数据"。

Function Calling 和 Plugins 有什么区别?

Function Calling 是开发者在代码中定义和执行函数,完全由开发者控制;Plugins(已废弃)是 OpenAI 托管的第三方服务。Function Calling 提供了更好的安全性、灵活性和性能。

可以在函数中调用外部 API 吗?

完全可以。Function Calling 的设计初衷就是让 GPT 模型能够与外部系统交互。你可以在函数中调用任何 API、查询数据库、操作文件系统等。只需确保将结果以 JSON 字符串形式返回给模型即可。

总结

通过本篇 OpenAI API Function Calling 教程,你已经掌握了从基础概念到实战应用的完整知识体系。函数调用功能让 GPT 模型从单纯的对话工具进化为能够执行实际任务的智能代理,极大地拓展了 AI 应用的可能性。

在实际开发中,建议从简单的单函数场景开始,逐步过渡到多函数并行调用的复杂应用。记住始终验证函数参数、处理异常情况,并通过合理的架构设计控制成本。随着对这项技术的深入理解,你将能够构建出更加智能、实用的 AI 应用。

通过 XiaoMu AI 使用所有主流 AI API

一个 API Key 访问 GPT-4o、Claude、Gemini 等全部模型。国内直连,无需翻墙,按量计费更省钱。

立即领取

新用户赠送免费额度,无需绑定信用卡

常见问题

Function Calling 支持哪些 OpenAI 模型?

目前支持函数调用的模型包括:gpt-4、gpt-4-turbo、gpt-4o、gpt-3.5-turbo(0613 版本及以后)。推荐使用 gpt-4-turbo 或 gpt-4o,它们支持并行函数调用和更复杂的参数推理。

函数调用会增加多少 Token 消耗?

函数定义会计入输入 Token,每个函数的描述和参数结构大约消耗 50-200 个 Token。函数调用的响应(包含 function_call 字段)也会计入输出 Token。建议在开发时使用 response.usage 监控实际消耗。

如何让模型更准确地选择函数?

关键在于函数的 description 字段。应该清晰描述函数的用途、适用场景和限制条件。例如:"查询指定日期范围内的订单,日期格式为 YYYY-MM-DD,最多支持查询 90 天内的数据"。

Function Calling 和 Plugins 有什么区别?

Function Calling 是开发者在代码中定义和执行函数,完全由开发者控制;Plugins(已废弃)是 OpenAI 托管的第三方服务。Function Calling 提供了更好的安全性、灵活性和性能。

可以在函数中调用外部 API 吗?

完全可以。Function Calling 的设计初衷就是让 GPT 模型能够与外部系统交互。你可以在函数中调用任何 API、查询数据库、操作文件系统等。只需确保将结果以 JSON 字符串形式返回给模型即可。