一架梯子,一头程序猿,仰望星空!
OpenAI开发教程 > 内容正文

Function calling(函数调用)


1. Function calling介绍

在GPT模型中,Function calling(函数调用)是指通过API向模型提供描述性的函数,使其能够智能地选择输出一个包含参数的JSON对象,以便调用一个或多个函数。值得注意的是,Chat Completions API并不直接执行函数调用,而是生成可以用于在代码中执行函数的JSON。

通俗的讲Function calling功能就是我们提供一组函数的定义(包括函数的功能描述、参数描述),把函数的定义传递给GPT模型,由模型根据用户的问题决定要调用那个函数,由于模型无法执行我们定义的外部函数,模型只能以请求响应的方式返回希望调用那个函数(包括函数调用参数),我们程序收到模型请求结果后,在本地执行函数调用,然后把函数调用的结果拼接到提示词中再次传给模型,让模型进一步处理,最后在把结果返回给用户。

2. Function calling的应用场景

以下是其在实际应用中的一些例子:

  • 创建调用外部API回答问题的助手,例如定义函数send_email(to: string, body: string)get_current_weather(location: string, unit: 'celsius' | 'fahrenheit')
  • 将自然语言转换为API调用,例如将“Who are my top customers?”转换为get_customers(min_revenue: int, created_before: string, limit: int)然后调用内部API。
  • 从文本中提取结构化数据,例如定义函数extract_data(name: string, birthday: string)sql_query(query: string)

利用函数调用功能,我们可以实现智能体(AI Agent),让AI可以跟我们本地的系统、数据库互动,例如让AI去查询最新的天气、查询股票价格、让AI去点外卖、订机票等等。

3. 支持Function calling的模型

并非所有版本的模型都经过了函数调用数据的训练。目前支持函数调用的模型有:gpt-4gpt-4-turbo-previewgpt-4-0125-previewgpt-4-1106-previewgpt-4-0613gpt-3.5-turbogpt-3.5-turbo-1106gpt-3.5-turbo-0613

此外,模型gpt-4-turbo-previewgpt-4-0125-previewgpt-4-1106-previewgpt-3.5-turbo-1106支持并行函数调用功能,这允许它们一次性执行多个函数调用,并且能够并行解决这些函数调用效果和结果。

提示:Function calling功能带来的潜在风险在于,它可能导致模型产生错误参数(即幻觉参数)。因此,在代表用户对外界产生实际影响之前(例如发送电子邮件、在线发布、进行购买等),建议在产品层面增加用户确认流程,用户确认之后再执行函数。

4. Function calling例子

4.1 python例子

在OpenAI平台上实现Function calling通常遵循下面的基本步骤,以下将通过查询天气的案例,使用Python代码详细讲解整个过程。

步骤1: 准备可被模型调用的函数和函数定义

首先,我们需要定义一个可以被GPT模型调用的函数。这通常意味着我们需要准备一个我们自己的函数,这个函数能够根据传入的参数执行某些操作,比如与第三方API通信。

import json

# 这是一个假设的函数,用于获取当前的天气情况
def get_current_weather(location, unit="fahrenheit"):
    # 在实际应用中,这里会与某个天气API接口进行交互来获取数据
    # 这里为了简化示例返回了固定的数据
    return json.dumps({
        "location": location, 
        "temperature": "18", 
        "unit": unit
    })

步骤2: 根据问题和tools参数调用模型

接下来,我们需要通过Chat Completion API调用GPT模型,并传入用户的查询(如“当前天气如何”),以及tools参数,其中包含了我们刚刚定义的get_current_weather函数的描述。

from openai import OpenAI

client = OpenAI()

# 定义tools参数,其中包含了get_current_weather函数的描述
tools = [{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "获取给定地点的当前天气",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "城市名称,例如:“旧金山,CA”"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"]
                }
            },
            "required": ["location"]
        }
    }
}]

# 发起调用,传入用户的问题
response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[{"role": "user", "content": "旧金山当前的天气如何?"}],
    tools=tools
)

步骤3: 本地执行函数

模型返回的结果会包含模型希望调用的函数,函数信息通常包含在tool_calls响应参数中,我们就可以根据tool_calls参数描述的函数调用信息,在本地执行相应的函数调用。

# 解析模型响应中的tool_calls
tool_calls = response.choices[0].message.tool_calls

# 如果模型确定要调用一个函数
if tool_calls:
    # 获取模型生成的参数
    # ps: 这里只是假设模型仅调用一个函数,实际上有可能调用多个函数
    arguments = json.loads(tool_calls[0].function.arguments)
    # 执行我们之前定义的函数
    # ps: 这里为了演示以硬编码的方式调用本地函数,实际业务你可以根据tool_calls参数动态的选择调用本地函数。
    weather_info = get_current_weather(**arguments)
    print(weather_info) # 我们可以在这里看到函数调用查询到的天气信息

步骤4: 通过函数返回结果再次调用模型

现在,我们可以将函数返回的结果作为一个新的消息发送给模型,这样模型就可以对这些结果进行处理并生成一个面向用户的响应。

# 使用刚才函数返回的结果继续与模型对话
follow_up_response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    messages=[
        {"role": "user", "content": "旧金山当前的天气如何?"},
        {"role": "function", "name": "get_current_weather", "content": weather_info}
    ],
    tools=tools
)

说明:
上面例子通过function消息的方式,告诉gpt函数返回内容

{"role": "function", "name": "get_current_weather", "content": weather_info}

实际上,你也可以简单的把函数返回的内容作为参考内容放到sytem消息prompt里面,让AI参考这些信息回答问题。

步骤5: 获取模型最终响应结果

最后,我们可以获取模型的最终响应并将其提供给用户。在此步骤中,模型会根据我们给予的天气信息输出一个面向用户友好的回答。

# 处理得到最终的模型响应
final_output = follow_up_response.choices[0].message.content
print(final_output) # 这个输出就是我们要展示给用户的天气信息

通过以上步骤,我们就完成了一个使用GPT模型以及函数调用的完整的查询天气的例子。

4.2. Function call函数定义说明

4.2.1 Tools参数的字段含义

函数调用(Function Call)在使用时,需要在tools参数中定义函数的相关信息。tools参数是一个包含多个函数定义的数组,每个函数定义包含以下字段:

  1. type: 此字段表示工具的类型,在函数调用中该字段应设为"function"
  2. function: 此字段包含函数的详细信息,是一个对象,其具体字段如下:
    • name: 函数的名称,是一个字符串。
    • description: 对函数作用的描述,可以帮助模型生成符合用户期望的参数。
    • parameters: 描述函数的参数定义,是一个对象,该对象包含下列子字段:
      • type: 定义参数的类型,在大多数情况下应设为"object"
      • properties: 函数的每个参数的具体定义,每个参数定义都是一个对象,通常包含下列子字段:
        • type: 参数的数据类型(如"string""integer""boolean"等)。
        • description: 此参数的描述,以帮助模型理解其用途。
        • enum (可选): 当type"string"时,enum字段可以指定一个字符串的数组,表示有效值的集合。
      • required: 一个包含必填参数名称的字符串数组。

4.2.2 Tools定义示例

现在我们将提供几个tools定义的例子,以便于理解每个字段如何使用。

示例 1:获取当前天气信息

{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "获取指定地点的当前天气信息",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "需要查询天气的城市,如'San Francisco, CA'"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "温度单位,'celsius'表示摄氏度,'fahrenheit'表示华氏度"
                }
            },
            "required": ["location", "unit"]
        }
    }
}

在上述示例中,我们定义了一个名为get_current_weather的函数,用于获取指定地点的当前天气。参数包括location(位置)和unit(单位),其中unit有两个有效值:”celsius”(摄氏度)和”fahrenheit”(华氏度)。

示例 2:查找特定艺术家的专辑

{
    "type": "function",
    "function": {
        "name": "find_artist_albums",
        "description": "查找特定艺术家的所有专辑",
        "parameters": {
            "type": "object",
            "properties": {
                "artist_name": {
                    "type": "string",
                    "description": "艺术家的名称"
                }
            },
            "required": ["artist_name"]
        }
    }
}

在这个示例中,我们创建了一个名为find_artist_albums的函数,它的作用是查找特定艺术家的所有专辑。此函数只需要一个参数:artist_name(艺术家名称)。

4.3. http请求function call例子

OpenAI是通过http协议的方式开放API,下面讲解如何通过http api使用function call功能,其他编程语言的开发者可以参考这个例子。

4.3.1. 步骤1:通过用户问题和函数声明调用模型

首先我们需要把用户的问题和我们支持的函数清单一起发给GPT模型,让模型根据用户的问题自动分析需要调用那个函数来回答用户问题。

下面例子,告诉GPT我们有一个get_current_weather函数可以用来查询指定城市的天气信息。

curl --location 'https://api.aiproxy.io/v1/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer {OPENAI_KEY}' \
--data '{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "user",
            "content": "今天上海天气怎么样?"
        }
    ],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "获取指定地点的当前天气信息",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "需要查询天气的城市,如'\''San Francisco, CA'\''"
                        },
                        "unit": {
                            "type": "string",
                            "enum": [
                                "celsius",
                                "fahrenheit"
                            ],
                            "description": "温度单位,'\''celsius'\''表示摄氏度,'\''fahrenheit'\''表示华氏度"
                        }
                    },
                    "required": [
                        "location",
                        "unit"
                    ]
                }
            }
        }
    ]
}'

请求参数说明:

{
    "model": "gpt-3.5-turbo", // 需要调用的gpt模型
    "messages": [ // 这里是gpt的消息列表,包括用户的问题
        {
            "role": "user",
            "content": "今天上海天气怎么样?"
        }
    ],
    "tools": [
      // 这里是你的函数定义,告诉gpt有什么函数可以调用
    ]
}

tools参数定义参考4.2.2章节。

GPT模型正常处理的情况下,你会得到类似下面的API响应结果:

{
    "model": "gpt-3.5-turbo-0613",
    "object": "chat.completion",
    "usage": {
        "prompt_tokens": 122,
        "completion_tokens": 27,
        "total_tokens": 149
    },
    "id": "chatcmpl-8mL4hS4zNMocyR2ajKyAvSTcbNaao",
    "created": 1706531447,
    "choices": [
        {
            "index": 0,
            "delta": null,
            "message": {
                "role": "assistant",
                "tool_calls": [ // tool_calls参数代表GPT希望调用的函数列表
                    {
                        "id": "call_1iF09ttX1R9ESR18Ul2nLe1R",
                        "type": "function",
                        "function": {
                            "name": "get_current_weather",  // 代表GPT希望通过调用get_current_weather函数,回答用户问题。
                            "arguments": "{\n  \"location\": \"Shanghai, China\",\n  \"unit\": \"celsius\"\n}" // 这里是调用get_current_weather函数的输入参数
                        }
                    }
                ]
            },
            "finish_reason": "tool_calls"
        }
    ]
}

4.3.2. 步骤2:本地执行函数调用

因为GPT模型本身是没法执行具体的函数调用,只是告诉我们希望调用那个函数,因此我们本地程序需要根据模型请求返回的tool_calls参数,执行具体函数调用,不同编程语言执行本地函数的方式不一样,python的例子,可以参考前面章节。

4.3.3. 步骤3:通过函数返回结果再次调用模型

因为函数的调用是在本地执行的,所以我们需要把函数执行的结果和用户的问题再次送入GPT模型,让GPT做出最终的回答。

curl --location 'https://api.aiproxy.io/v1/chat/completions' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer sk-Roc5MX1zEuVxiuaMaETV6wZ2jXcCehjUCzwP9AcNErUiwppQ' \
--data '{
    "model": "gpt-3.5-turbo",
    "messages": [
        {
            "role": "user",
            "content": "今天上海天气怎么样?"
        },
        {
            "role": "function",
            "name": "get_current_weather",
            "content": "{\"city\":\"上海\", \"temperature\":\"25摄氏度\"}"
        }
    ],
    "tools": [
        {
            "type": "function",
            "function": {
                "name": "get_current_weather",
                "description": "获取指定地点的当前天气信息",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "需要查询天气的城市,如'\''San Francisco, CA'\''"
                        },
                        "unit": {
                            "type": "string",
                            "enum": [
                                "celsius",
                                "fahrenheit"
                            ],
                            "description": "温度单位,'\''celsius'\''表示摄氏度,'\''fahrenheit'\''表示华氏度"
                        }
                    },
                    "required": [
                        "location",
                        "unit"
                    ]
                }
            }
        }
    ]
}'

说明,上面请求多了一条function消息告诉GPT模型函数的返回值,GPT会根据函数返回信息直接回答用户问题,不会再调用函数。

function消息,代表函数的返回值,格式如下:

 {
            "role": "function", // 消息类型是function,代表函数返回值
            "name": "get_current_weather", // 告诉gpt当前消息是get_current_weather函数的返回值
            "content": "{\"city\":\"上海\", \"temperature\":\"25摄氏度\"}"  // 函数返回内容,可以是json格式,也可以是其他文本内容。
}

下面是GPT做出的最终回答:

{
    "model": "gpt-3.5-turbo-0613",
    "object": "chat.completion",
    "usage": {
        "prompt_tokens": 144,
        "completion_tokens": 17,
        "total_tokens": 161
    },
    "id": "chatcmpl-8mLmvvKAjSql7rGF8fvQeddKhWYvr",
    "created": 1706534189,
    "choices": [
        {
            "index": 0,
            "delta": null,
            "message": {
                "role": "assistant",
                "content": "今天上海的天气是25摄氏度。"
            },
            "finish_reason": "stop"
        }
    ]
}

关联主题