通过 Function Calling 连接 GPT 和你的业务系统

GPT 的能力很强,但是很多人不知道如何去使用,如何去落地到自己的业务系统

现在 OpenAI 开发了两个能力 Actions,Function Calling。

GPT 通过这两个能力与外部世界打通。

本文主要介绍 Function Calling

原理

1 .业务系统先拿到用户的输入

2.应用系统构建一个 prompt, 以及一个 function 的定义喂给大模型

3.大模型判断是否需要调用函数,

        3.1 不需要调用函数,大模型直接回答问题了

        3.2 需要调用函数,就返回一个调用说明,里面包含调用的参数给应用系统

4. 应用系统拿到大模型返回的函数调用说明,调用函数获取到结果

5. 应用系统将调用结果构建一个 prompt 给大模型

6. 大模型输出自然语言经过应用系统返回给用户

整体的过程如下图所示:

通过 Function Calling 连接 GPT 和你的业务系统_第1张图片

示例:

先定义几个工具方法

print_json 是为了打印参数用于调试

get_completion:定义了几个函数,供大模型判断是否需要调用

fbnq:是用户自己定义的一个函数

from openai import OpenAI
import json

client = OpenAI(api_key="YOUR KEY")


def print_json(data):
    """
    打印参数。如果参数是有结构的(如字典或列表),则以格式化的 JSON 形式打印;
    否则,直接打印该值。
    """
    if hasattr(data, 'model_dump_json'):
        data = json.loads(data.model_dump_json())

    if isinstance(data, list):
        for item in data:
            print_json(item)
    elif isinstance(data, dict):
        print(json.dumps(
            data,
            indent=4,
            ensure_ascii=False
        ))
    else:
        print(data)


def get_comletion(messages, model="gpt-3.5-turbo"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0.7,
        tools=[  # 用 JSON 描述函数,可以定义多个,由大模型决定调用或者不调用
            {
                "type": "function",
                "function": {
                    "name": "sum",
                    "description": "加法器,计算一组数据的和",
                    "parameters": {
                        "type": "array",
                        "items": {
                            "type": "number"
                        }
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "abs",
                    "description": "绝对值,计算一个数字对应的绝对值",
                    "parameters": {
                        "type": "number"
                    }
                }
            },
            {
                "type": "function",
                "function": {
                    "name": "fbnq",
                    "description": "计算斐波那契数列的值",
                    "parameters": {
                        "type": "number"
                    }
                }
            }
        ]
    )
    return response.choices[0].message


def fbnq(n):
    if n <= 2:
        return 1
    return fbnq(n - 1) + fbnq(n - 2)

开始调用大模型,看看大模型会不会调用我们提供的一些方法

def call_gpt(user_input):
    print("用户输入:", user_input)
    messages = [
        {"role": "system", "content": "你是一个数学家"},
        {"role": "user", "content": user_input}
    ]
    print("prompt:")
    print_json(messages)
    response = get_completion(messages)
    print("大模型:")
    print_json(response)
    # 将大模型的回复加入到对话历史
    messages.append(response)
    if response.tool_calls:
        tool_call = response.tool_calls[0]
        if tool_call.function.name == "sum":
            args = json.loads(tool_call.function.arguments)
            result = sum(args['numbers'])
        if tool_call.function.name == "abs":
            args = json.loads(tool_call.function.arguments)
            result = abs(args['number'])
        if tool_call.function.name == "fbnq":
            args = json.loads(tool_call.function.arguments)
            result = fbnq(args['number'])
        fun_res = {
            "tool_call_id": tool_call.id,  # 用于标识函数调用的 ID
            "role": "tool",
            "name": "sum",
            "content": str(result)  # 数值 result 必须转成字符串
        }
        # 把调用结果加入到对话历史中
        messages.append(fun_res)
        print("函数调用结果:")
        print_json(fun_res)
        # 再次调用大模型
        print("=====最终 GPT 回复=====")
        print(get_completion(messages).content)


if __name__ == "__main__":
    call_gpt("计算一下斐波那契数列第10位是几?")

输出结果:

用户输入: 计算一下斐波那契数列第10位是几?
prompt:
{
    "role": "system",
    "content": "你是一个数学家"
}
{
    "role": "user",
    "content": "计算一下斐波那契数列第10位是几?"
}
大模型:
{
    "content": null,
    "role": "assistant",
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_naVIx2li3mJuOAtzXag9mUcZ",
            "function": {
                "arguments": "{\"number\":10}",
                "name": "fbnq"
            },
            "type": "function"
        }
    ]
}
函数调用结果:
{
    "tool_call_id": "call_naVIx2li3mJuOAtzXag9mUcZ",
    "role": "tool",
    "name": "sum",
    "content": "55"
}
=====最终 GPT 回复=====
斐波那契数列的第10位是55。

从结果可以看出,GPT 是调用了前面定义的 fbnq 方法。

多任务拆解

上面的代码无法处理下面这样的任务

计算一下斐波那契数列第10位和第11位,并计算这两个数的和

这个任务应该是分解成两步,第一步计算第10位和11位的数值,第二步再将上一步的结果加和

将代码稍加修改

def call_gpt(user_input):
    print("用户输入:", user_input)
    messages = [
        {"role": "system", "content": "你是一个数学家"},
        {"role": "user", "content": user_input}
    ]
    print("prompt:")
    print_json(messages)
    response = get_completion(messages)
    print("大模型:")
    print_json(response)
    # 将大模型的回复加入到对话历史
    messages.append(response)
    if response.tool_calls:
        for tool_call in response.tool_calls:#  改了这里 gpt 会将任务拆解成多个任务

            if tool_call.function.name == "sum":
                args = json.loads(tool_call.function.arguments)
                result = sum(args['numbers'])
            if tool_call.function.name == "abs":
                args = json.loads(tool_call.function.arguments)
                result = abs(args['number'])
            if tool_call.function.name == "fbnq":
                args = json.loads(tool_call.function.arguments)
                result = fbnq(args['number'])
            fun_res = {
                "tool_call_id": tool_call.id,  # 用于标识函数调用的 ID
                "role": "tool",
                "name": "sum",
                "content": str(result)  # 数值 result 必须转成字符串
            }
            # 把调用结果加入到对话历史中
            messages.append(fun_res)
            print("函数调用结果:")
            print_json(fun_res)
            # 再次调用大模型
        print("=====最终 GPT 回复=====")
        print(get_completion(messages).content)

输出:

用户输入: 计算一下斐波那契数列第10位和第11位,并计算这两个数的和
prompt:
{
    "role": "system",
    "content": "你是一个数学家"
}
{
    "role": "user",
    "content": "计算一下斐波那契数列第10位和第11位,并计算这两个数的和"
}
大模型:
{
    "content": null,
    "role": "assistant",
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_GbbKxqjBn6lv0JSYY7UBeNLg",
            "function": {
                "arguments": "{\"number\": 10}",
                "name": "fbnq"
            },
            "type": "function"
        },
        {
            "id": "call_zO1khQ64ERobcHqqkAf27afv",
            "function": {
                "arguments": "{\"number\": 11}",
                "name": "fbnq"
            },
            "type": "function"
        }
    ]
}
函数调用结果:
{
    "tool_call_id": "call_GbbKxqjBn6lv0JSYY7UBeNLg",
    "role": "tool",
    "name": "sum",
    "content": "55"
}
函数调用结果:
{
    "tool_call_id": "call_zO1khQ64ERobcHqqkAf27afv",
    "role": "tool",
    "name": "sum",
    "content": "89"
}
=====最终 GPT 回复=====
None

Process finished with exit code 0

从结果看,gpt 确实将任务做到了拆解,但是最后一步的任务却拆不出来,其实大模型已经给出步骤了,还需要进一步处理才能得到最后结果

继续调整代码

def call_gpt(user_input):
    print("用户输入:", user_input)
    messages = [
        {"role": "system", "content": "你是一个数学家"},
        {"role": "user", "content": user_input}
    ]
    print("prompt:")
    print_json(messages)
    response = get_completion(messages)
    print("大模型:")
    print_json(response)
    # 将大模型的回复加入到对话历史
    messages.append(response)
    while response.tool_calls:
        for tool_call in response.tool_calls:

            if tool_call.function.name == "sum":
                args = json.loads(tool_call.function.arguments)
                result = sum(args['numbers'])
            if tool_call.function.name == "abs":
                args = json.loads(tool_call.function.arguments)
                result = abs(args['number'])
            if tool_call.function.name == "fbnq":
                args = json.loads(tool_call.function.arguments)
                result = fbnq(args['number'])
            fun_res = {
                "tool_call_id": tool_call.id,  # 用于标识函数调用的 ID
                "role": "tool",
                "name": "sum",
                "content": str(result)  # 数值 result 必须转成字符串
            }
            # 把调用结果加入到对话历史中
            messages.append(fun_res)
            print("函数调用结果:")
            print_json(fun_res)
            # 再次调用大模型

        response = get_completion(messages)
        messages.append(response)  # 把大模型的回复加入到对话中

    print("=====最终回复=====")
    print(response.content)
用户输入: 计算一下斐波那契数列第10位和第11位,并计算这两个数的和
prompt:
{
    "role": "system",
    "content": "你是一个数学家"
}
{
    "role": "user",
    "content": "计算一下斐波那契数列第10位和第11位,并计算这两个数的和"
}
大模型:
{
    "content": null,
    "role": "assistant",
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_hAVFeLjeegHSE4ZlCrMVCNpc",
            "function": {
                "arguments": "{\"number\": 10}",
                "name": "fbnq"
            },
            "type": "function"
        },
        {
            "id": "call_R5jEoh0cdTO2j1oo1z7wDq1j",
            "function": {
                "arguments": "{\"number\": 11}",
                "name": "fbnq"
            },
            "type": "function"
        }
    ]
}
函数调用结果:
{
    "tool_call_id": "call_hAVFeLjeegHSE4ZlCrMVCNpc",
    "role": "tool",
    "name": "sum",
    "content": "55"
}
函数调用结果:
{
    "tool_call_id": "call_R5jEoh0cdTO2j1oo1z7wDq1j",
    "role": "tool",
    "name": "sum",
    "content": "89"
}
函数调用结果:
{
    "tool_call_id": "call_l7zDPFvIwYEzZIB8XQ6B9vjk",
    "role": "tool",
    "name": "sum",
    "content": "144"
}
=====最终回复=====
斐波那契数列的第10位是55,第11位是89,它们的和是144。

至此,大模型的任务拆解就完成了

你可能感兴趣的:(gpt)