理解OpenAI刚刚提供的核心功能Function calling

前言

作为一名非科班出身的AI工程师爱好者,很多跟我类似的朋友,都是通过半年前OpenAI提供的ChatGPT来入门AI的。
因为在AI面前,你我都还是孩子;所以在面对OpenAI新鲜出炉的核心功能function calling时,大多数人都是一知半解,懵懵懂懂。
很多营销号、自媒体都会表示,function calling牛逼!但它是干嘛的呢?很多人点到为止,很多人讲不明白。
我斗胆描述一下场景,带你过一个例子,你就懂了。

ChatGPT v.s. OpenAI API

开始之前,我简单介绍一下ChatGPT和OpenAI API(以下简称API)的区别。
ChatGPT是一款产品,面向大众用户(即不会写代码的人);而API是一个接口/函数,面向开发者。
ChatGPT是由OpenAI通过调用公有(public)API + 尚未提供的私有(private)API + 后端、前端、客户端API来开发实现的。
举一个不恰当的比方,微信是一款产品,它不提供公有API,只能通过逆向工程的方式来获取私有API,例如[CMessageMgr AsyncOnAddMsg:MsgWrap:]

痛点

毋庸置疑,ChatGPT/API的功能非常强大。但它当前的核心痛点之一是信息不能实时更新,也不能联网,所以一些相对“小众”的信息,ChatGPT/API回答不了,如:

  • 我问:Who is snakeninny?
  • AI答:I’m sorry, but I don’t have any information about “snakeninny”. It’s possible that it might be a username or nickname for someone on a particular platform or community, but without further context, I cannot provide a more accurate answer.

为了解决这个痛点,OpenAI提供了两种解决方案。

方案一:ChatGPT Plugins

它能让ChatGPT“联网”,通过第三方插件(plugin)的方式供ChatGPT调用;第三方插件需要提前注册,并提供足够多的信息,以便让ChatGPT了解这个插件是干嘛的。当ChatGPT认为有必要让“外援”出场时,就会调用第三方插件,获取到第三方插件提供的信息,然后回复给用户。

方案二:Function Calling

它能让API“联网”,通过第三方函数(function)的方式供新模型gpt-4-0613gpt-3.5-turbo-0613调用;第三方函数不需要提前注册,但需要提供足够多的信息,以便让gpt-4-0613gpt-3.5-turbo-0613了解这个函数是干嘛的。当gpt-4-0613gpt-3.5-turbo-0613认为有必要让“外援”出场时,就会调用第三方函数,获取到第三方函数提供的信息,然后回复给用户。

简而言之,ChatGPT Plugins和Function calling的出发点类似,都是“联网”。最大的区别是场景,前者服务于ChatGPT,而后者服务于API。

大致流程

以API为例,当没有function calling时,API的大致流程是这样的:

  • 用户提问 → API搞不搞得定,都擅自回答 → 用户收到

而有了function calling后,API的流程变成了这样:

  • 用户提问 → API如果搞的定,则直接回答 → 用户收到
  • 用户提问 → API如果搞不定,则向“外援”求助 → Function Calling回答 → OpenAI API收到然后润色 → 用户收到

注意,API搞不搞得定,是由gpt-4-0613gpt-3.5-turbo-0613自行决定的,而不是由开发者决定的。

上demo

还是一知半解?没关系。我写个demo给你看,你就懂了。

使用Function Calling的流程

上demo前,我先把function calling的流程梳理一下。

  1. 开发者定义一个第三方函数。
  2. 在调用API时,开发者把第三方函数作为参数传给模型。
  3. 模型来判断何时需要调用第三方函数(注意:模型只判断,然后由开发者来执行;就像ChatGPT/API只是大脑,四肢和躯干需要第三方来执行)。具体而言,开发者需要关注模型的返回值。
  4. 开发者调用第三方函数后,要把返回值传给模型。具体而言,开发者要再次调用API。
  5. 此时模型的返回值则是最终的回答,可以发给用户了。

demo

参考官方文档

import openai
import json

openai.api_key = "改成你自己的API_KEY"

# 用户问:“这TM谁啊??”
messages = [
    {"role": "user", "content": "Who is snakeninny?"}
]

# 第三方函数,这里我是本地调用,不联网;当然你可以改代码,联网
def who_is_snakeninny(value):
    if value == "snakeninny":
        result = {"result" : "He's an AI n00b."}
    elif value == "大名狗剩":
        result = {"result" : "He's an AI h0bby1st."}
    elif value == "沙梓社":
        result = {"result" : "He's an AI enthus1ast."}
    else:
        result = {"result" : "I don't know him."}
    return json.dumps(result) # 官方文档里的返回值格式是JSON

# 第三方函数的介绍,供AI理解和使用
who_is_snakeninny_description = {
    "name": "who_is_snakeninny",
    "description": "Describe snakeninny", # 这个函数是干嘛的
    "parameters": {
        "type": "object",
        "properties": {
            "value": {
                "type": "string",
                "description": "The name of a person", # 这个参数是干嘛的
            }
        },
        "required": ["value"],
    },
}

# 调用API,把第三方函数作为参数传给模型
def get_completion(messages):
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613", # 模型可选,目前有gpt-4-0613和gpt-3.5-turbo-0613
        messages=messages, # 其实就是prompt
        functions=[who_is_snakeninny_description], # 如果需要“外援”,就需要这个参数。如果没有这个参数,就不会调用“外援”
    )
    return completion

first_response = get_completion(messages)

# 关注返回值
finish_reason = first_response["choices"][0]["finish_reason"]
message = first_response["choices"][0]["message"] # 其实就是completion

if finish_reason == "stop": # 这时模型可以自己hold住,不需要“外援”
    content = message["content"] # 可以直接返回给用户了
elif finish_reason == "function_call": # 模型感觉自己hold不住,需要“外援”上场
    function_call = message["function_call"]

    function_name = function_call["name"] # 第三方函数名
    function = locals()[function_name] # 第三方函数

    arguments = function_call["arguments"] # 第三方函数的参数名
    name = json.loads(arguments)["value"] # 第三方函数的参数

    function_response = function(name) # 调用第三方函数拿到的返回值

    messages.append(message) # 上下文的固定用法
    messages.append( # 上下文固定用法
            {
                "role": "function",
                "name": function_name,
                "content": function_response,
            }
    )
    
    second_response = get_completion(messages) # 再次调用API,模型根据我们收集的messages再次组织语言
    message = second_response["choices"][0]["message"]
    content = message["content"] # 可以直接返回给用户了

print(content)

效果

  • 问:Who is snakeninny?

  • 答:Snakeninny is an AI n00b.

  • 问:Who is 大名狗剩?

  • 答:大名狗剩 is an AI hobbyist.

  • 问:Who is 沙梓社?

  • 答:沙梓社 is an AI enthusiast.

  • 问:Who is 大名snake?

  • 答:I’m sorry, but I don’t have any information about 大名snake.

  • 问:Who is Jack Ma?

  • 答:I’m sorry, but I don’t have any information about Jack Ma.

结尾

可以看到,gpt-3.5-turbo-0613认识了snakeninny,但不认识Jack Ma了。但是如果我注释掉这一行:

# 调用API,把第三方函数作为参数传给模型
def get_completion(messages):
    completion = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613", # 模型可选,目前有gpt-4-0613和gpt-3.5-turbo-0613
        messages=messages, # 其实就是prompt
        # functions=[who_is_snakeninny_description], # 如果需要“外援”,就需要这个参数。如果没有这个参数,就不会调用“外援”
    )
    return completion

gpt-3.5-turbo-0613就认识Jack Ma了:

Jack Ma, whose full name is Ma Yun, is a Chinese entrepreneur and philanthropist. He is the co-founder and former executive chairman of Alibaba Group, one of the world’s largest e-commerce companies. Born on September 10, 1964, in Hangzhou, China, Ma grew up in a middle-class family and faced several rejections before finding success. He launched Alibaba in 1999, initially as an online marketplace connecting Chinese exporters with overseas buyers. Under his leadership, the company expanded its services to include various platforms such as AliExpress, Taobao, and Tmall. Jack Ma is known for his inspirational leadership style, innovative approaches to business, and his vision of making it easy to do business anywhere in the world. He stepped down from his role as Alibaba’s executive chairman in 2019 but continues to be involved in philanthropic initiatives and investments in various industries.

我还没有深究,猜测是因为我的第三方函数造成了“污染”;模型还区分不出“describe snakeninny”和“describe a person”的区别,在“外援”和自己间“左右为难”。
或许进一步prompt engineering,能让模型更清晰地理解我的描述,选择更合适的解决方案。
留待有空再测吧。

谢谢!

1 个赞

这个环境咋配置啊

老板多发点这种。之前还见过一种用法,把gpt4催眠成gpt5,听说可以提升效果

有链接吗?我看看是啥情况

1 个赞

https://github.com/zhangferry/SummarAI 提示工程

1 个赞

通过这个方式,跟内部数据库做个结合(比如客服问答语料),感觉就能做个类似微调的效果

没看到哪里提到了“把gpt4催眠成gpt5,听说可以提升效果”啊?

是的,感觉结合embeddings,非常合适~
客服问答语料embeddings化,能解决90%的问题;还有10%疑难杂症的问题,对于embeddings来说“超纲”了,那就直接function calling,用第三方API来解决,那头是人工服务都行

1 个赞

这东西很难对比,因为gpt5还没出,倒是可以对比下gpt3.5和gpt4。具体增幅效果也是听别人谈的,只是这个脑洞很秀 :smile:

因为我的场景有限,所以对我来说,ChatGPT使用的GPT-3.5-turbo和GPT-4效果差异不明显