背景

最近 OpenAI 发布了一些模型及 API 的更新,主要有以下内容:

  1. Chat Completions 接口中新增对 function calling函数调用)的支撑;
  2. gpt-3.5 发布的新的支撑 16k 上下文的模型 gpt-3.5-turbo-16k ,直接变成了原来的 4 倍;
  3. 发布了新的支撑函数调用的模型 gpt-3.5-turbo-0613gpt-4-0613

其间对开发者而言,最兴奋的便是 function calling 的功用了,这个根本便是 ChatGPT Plugin 底层完结的根底了,有了这个才能咱们也能够打造自己的“插件商城”了。下面咱们就一起看一下 function calling 的具体功用。

概述

GPT 自身的约束

OpenAI 的 GPT 自身具有很强的智能,但也有知识陈旧以及短少“四肢”问题。比如,gpt-4 模型的练习数据都是 2021 年 9 月之前的数据,所以他并不知道 2023 年苹果发布了 visionOS 体系;另外,现在的 ChatGPT 并不能履行一些动作,比如帮助发送邮件,现在能做的仅仅是编写邮件。

具体表现为:

  1. LLM 的知识是依据模型练习时运用的数据及知识,关于后续的新的事实其并不知道;
  2. LLM 相似于“缸中之脑”,其有很高的智能但是短少一些举动的才能;

现在的解决计划

上述问题社区中也有一些解决计划,如 LangChain 之类的东西。除此之外,OpenAI 官方也推出了 Plugin 的功用,答应开发者能够给 LLM 供给一些才能来解决上述问题,甚至是增强其才能供给了更多模态的信息输出。

上述的解决计划中也都有各自的问题:

  • LangChain 的解决计划究竟不是官方计划,不可避免的存在一些安稳性的危险,除此之外,LangChain 现在仅支撑 Python 以及 JS 代码,其他的言语没有现成的解决计划;
  • OpenAI Plugin 的方式是正解,但是只能在 ChatGPT 这个封闭的生态中运用,关于一些笔直范畴的 LLM 运用开发而言,并不能直接运用;

Function Calling

简略讲 Function Calling 功用其实便是在 Chat Completions 接口(下文一致称 Chat 接口)中新增了一个 functions 的可选入参,该参数是一个列表能够支撑传入多个函数的声明,在声明中告诉 GPT 有这些函数能够被调用,他能够凭借这些函数完结自己不能完结的的一些事情,或者说把自己的使命完结的愈加超卓。

从这视点来理解 Function Calling 和 ChatGPT 中的 Plugin 并没有本质差异。前者是面临开发者的根底才能,后者是面临用户的根底交互体会。

Function Calling 的呈现根本上能够很好的解决之前留传的一些问题:

  1. 突破编程言语的约束,现在发布的功用是云端接口,只需能够进行网络恳求的言语都能够完结此功用;
  2. 提升函数调用的安稳性,官方出品安稳性有确保,但并不是没有安稳性的问题仍是要增加对应容错逻辑;
  3. 提示 API 易用性,笔直范畴的 LLM 运用开发少不了 API 调用,这部分的运用越简略其上层的 LLM 运用月昌盛;

下面咱们就来具体看一下 Function Calling 到底是什么,怎么用,以及一些常见的运用案例。

Chat Completions

Function Calling 是 Chat 接口中一个参数,想要了解 Function Calling 咱们需求先了解一下 Chat 接口的一些根本概念。

接口简介

Chat 接口ChatGPT 谈天运用中运用的接口,差异于之前的 Completions 接口,它将用户和 GPT 的对话以列表的方式进行进行办理,并不是将一切的对话的一致拼接在一个字符串中。除此之外,Chat 接口还明确了 role 的概念,体系的上下文信息经过 system 来标明。

现在接口仅支撑 gpt-3.5 和 gpt-4 的系列模型。其方式大致如下:

import openai
openai.ChatCompletion.create(
  model="gpt-3.5-turbo",
  messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "Who won the world series in 2020?"},
        {"role": "assistant", "content": "The Los Angeles Dodgers won the World Series in 2020."},
        {"role": "user", "content": "Where was it played?"}
    ]
)

messages 标明的是对话列表,包括对话的角色以及对应的内容,此次的更新中新增了 function 这个角色,现在共有以下内容:

  1. system:标明体系上下文信息,确认当时对话的根本风格,一般接口只包括一条该内容;
  2. user:用户的发问的问题;
  3. assistant:GPT 回来的答复内容;
  4. function:函数履行的成果,需求将此信息传递给 GPT,以便 GPT 愈加次内容做出进一步的答复;

Function Calling 根本界说

这次的 Function Calling 功用并不是新接口,而是在 Chat 接口上扩展而来的。在恳求接口以及接口回来上都有调整。主要内容如下:

  1. 接口恳求:新增 functions 参数和messages平级,role 中新增 function 枚举标识是函数履行的成果;
  2. 接口回来:choices 的 message 中新增 function_call 字段,包括函数履行有必要的参数信息,与文本内容 content 平级,但根本不会一起呈现;

Curl 恳求方式大致如下(下文会具体解说字段意义):

curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "What is the weather like in Boston?"}
  ],
  "functions": [
    {
      "name": "get_current_weather",
      "description": "Get the current weather in a given location",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"]
          }
        },
        "required": ["location"]
      }
    }
  ]
}'

回来的 JONS 方式大致如下(下文会具体解说字段意义):

{
  "id": "chatcmpl-123",
  ...
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": null,
      "function_call": {
        "name": "get_current_weather",
        "arguments": "{ \"location\": \"Boston, MA\"}"
      }
    },
    "finish_reason": "function_call"
  }]
}

Function Calling 调用流程

Function Calling 整个功用的调用次序大致如下:

  1. 声明函数并恳求 API:界说当时函数的称号,描绘,以及对应的参数信息,并履行 OpenAI 的 Chat 接口;
  2. 履行本地函数:承受接口回来,并解析对应的函数参数信息,依据对应的参数信息调用本地函数;
  3. 上报履行成果:将本地函数履行的成果上报给 OpenAI Chat 接口进行汇总、总结;

具体的时序图如下:

OpenAI 函数调用(1) - ChatGPT Plugin 是如何实现的?现已开放

下面就以用户询问当时的气候的场景来具体解说下 Function Calling 是如何运行的。

根本运用

第 1 步:声明函数并恳求 API

其间 functions 是一个列表包括多个函数目标,每个目标中包括:

  1. name:函数的名字,但函数被射中时接口会回来此处界说的称号;
  2. description:函数的描绘,OpenAI 会依据此描绘依据场景决议是否需求调用此函数,所以这里一定要描绘清楚;
  3. parameters:函数参数,当时界说的函数需求哪些传参,这些参数的类型以及可空性都需求在这里声明;

首要咱们先界说一个获取当时气候的函数声明,而且这个函数需求两个参数:

  1. location:方位信息,即想要查询那个地方的气候信息,类型是一个字符串,非空;
  2. unit:气候的单位,类型是一个字符串,是两个枚举摄氏度与华氏度,可空;

其声明大致如下:

{
      "name": "get_current_weather",
      "description": "Get the current weather in a given location",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"]
          }
        },
        "required": ["location"]
      }
    }

运用上述函数声明恳求 OpenAI 接口的 Python 代码大致如下:

def chat():
    # Step 1: send the conversation and available functions to GPT
    messages = [{"role": "user", "content": "What's the weather like in Boston?"}]
    functions = [
        {
            "name": "get_current_weather",
            "description": "Get the current weather in a given location",
            "parameters": {
                "type": "object",
                "properties": {
                    "location": {
                        "type": "string",
                        "description": "The city and state, e.g. San Francisco, CA",
                    },
                    "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                },
                "required": ["location"],
            },
        }
    ]
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=messages,
        functions=functions,
        function_call="auto",  # auto is default, but we'll be explicit
    )

留意:

  1. 这里运用的模型是 gpt-3.5-turbo-0613 ,现在支撑此功用的模型还有 gpt-4-0613 其他的模型可能并不支撑函数调用。
  2. function_call 传参数标明 OpenAI 如何运用函数,如果 functions 参数不为空时默认值为 auto,如果为空时默认值为 none;

第 2 步:履行本地函数

若声明的函数被射中之后,GPT 会回来本地函数履行所需的必要参数(第一步中界说的),在履行本地函数之前需求先将其解析处理。GPT 的 Response 和之前相比会有以下不同之处:

  1. content:content 内容为空,不仔包括对应的文本信息;
  2. function_call:在 choices 列表中新增 function_call 字段,该字段中包括需求调用的函数信息;
  3. finish_reason:finish_reason 对应的值为 function_call ,标明是因为函数调用而停止的;

其回来的内容方式大致如下:

{
  "id": "chatcmpl-123",
  ...
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": null,
      "function_call": {
        "name": "get_current_weather",
        "arguments": "{ \"location\": \"Boston, MA\"}"
      }
    },
    "finish_reason": "function_call"
  }]
}

其解析的代码逻辑大致如下:

def parse_response(response):
    response_message = response["choices"][0]["message"]
    # 检测是否需求调用函数
    if response_message.get("function_call"):
        # 调用函数
        available_functions = {
            "get_current_weather": get_current_weather,
        }  # only one function in this example, but you can have multiple
        function_name = response_message["function_call"]["name"]
        fuction_to_call = available_functions[function_name]
        function_args = json.loads(response_message["function_call"]["arguments"])
        function_response = fuction_to_call(
            location=function_args.get("location"),
            unit=function_args.get("unit"),
        )

代码的大致逻辑如下:

  1. 首要判别 function_call 是否存在,若存在则标明需求调用函数;
  2. 运用 JSON 解析对应的函数名以及对应的参数信息;

留意,OpenAI 回来的 JSON 信息并不能始终确保其有效性,需求自己做容错处理。

其间 get_current_weather 这个函数的具体完结如下:

def get_current_weather(location, unit="fahrenheit"):
    """Get the current weather in a given location"""
    weather_info = {
        "location": location,
        "temperature": "72",
        "unit": unit,
        "forecast": ["sunny", "windy"],
    }
    return json.dumps(weather_info)

首要函数在调用时 location 时必传的,unit 有默认值能够不必传递。这里为了便利起见并没有真实的去恳求气候的接口,而是写死了一个 mock 的数据。在自己的业务中,需求将其替换成自己的真实业务逻辑。

下一步便是需求将这里的履行数据上报给 OpenAI 进行进一步的整合了。

第 3 步:将成果上报给 OpenAI

这一步咱们会将函数的履行成果上传给 OpenAI,需求留意的有:

  1. 对话的 messages 中需求包括上一步 OpenAI 回来的 function_call 信息;
  2. 上报成果的 role 为 function,而且还需求经过 name 特点奉告对应的函数名;

具体的代码如下:

def chat_function_call():
    # 第 3 步: 将函数履行的成果上报给 GPT
    messages.append(response_message)  # extend conversation with assistant's reply
    messages.append(
        {
            "role": "function",
            "name": function_name,
            "content": function_response,
        }
    )  # extend conversation with function response
    second_response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=messages,
    )  # get a new response from GPT where it can see the function response
    return second_response

完整的的 curl 代码大致如下:

curl https://api.openai.com/v1/chat/completions -u :$OPENAI_API_KEY -H 'Content-Type: application/json' -d '{
  "model": "gpt-3.5-turbo-0613",
  "messages": [
    {"role": "user", "content": "What is the weather like in Boston?"},
    {"role": "assistant", "content": null, "function_call": {"name": "get_current_weather", "arguments": "{ \"location\": \"Boston, MA\"}"}},
    {"role": "function", "name": "get_current_weather", "content": "{\"temperature\": "22", \"unit\": \"celsius\", \"description\": \"Sunny\"}"}
  ],
  "functions": [
    {
      "name": "get_current_weather",
      "description": "Get the current weather in a given location",
      "parameters": {
        "type": "object",
        "properties": {
          "location": {
            "type": "string",
            "description": "The city and state, e.g. San Francisco, CA"
          },
          "unit": {
            "type": "string",
            "enum": ["celsius", "fahrenheit"]
          }
        },
        "required": ["location"]
      }
    }
  ]
}'

对应的回来成果如下:

{
  "id": "chatcmpl-123",
  ...
  "choices": [{
    "index": 0,
    "message": {
      "role": "assistant",
      "content": "The weather in Boston is currently sunny with a temperature of 22 degrees Celsius.",
    },
    "finish_reason": "stop"
  }]
}

解析上面的 message 中的 content 内容,就能够展示呼应的信息给到用户了。

总结

Chat Completions 接口中新增的 Function Calling 功用能够很好解决 GPT 知识陈旧以及短少四肢的问题,是对 GPT 才能的一次大升级。其主要流程为:

  1. 声明函数并恳求 API:界说当时函数的称号,描绘,以及对应的参数信息,并履行 OpenAI 的 Chat 接口;
  2. 履行本地函数:承受接口回来,并解析对应的函数参数信息,依据对应的参数信息调用本地函数;
  3. 上报履行成果:将本地函数履行的成果上报给 OpenAI Chat 接口进行汇总、总结;

个人估测 Function Calling 是 ChatGPT Plugin 功用的底层完结技能支撑,有了这个功用 LLM 运用开发变得愈加安稳可靠,一起也会有人依据这个这个技能开发出一套第三方的 GPT Plugin 商店;

除此之外,想必在 LLM 开发层也会有对应的动作:

  1. LangChain 相似的东西会快速跟进支撑(现已支撑了);
  2. 像 Google Bart 之类自研 LLM 的厂家会及时跟进开发相似的功用;
  3. 开源的 LLM 应该也会很快的推出依据 Function Calling 相似的微调计划,使开源的 LLM 也能装上“四肢”;

后面会依据 Function Calling 功用解说更多的实际案例,概况见:OpenAI 函数调用(2) – 打造自己 LLM 运用,欢迎重视。

参考资料

  1. OpenAI新功用 blog:Function calling and other API updates
  2. OpenAI 开发文档
  3. How_to_call_functions_with_chat_models