OpenAI ChatGPT 函数调用(function calling)流程

什么是函数调用(function calling)

大家都知道chatGpt、Deepseek、Claude等大语言模型(LLM)很厉害,但他们是基于某个时间点以前的数据训练的,意思就是他们本质是离线的。那天然导致有很大的局限性,如你问实时广州的天气是怎样时,你问下现在广州南站的人流大不大等,这种要联网的数据他们就一脸懵逼了。

更何况我还要进一步的与外部系统互动呢?比如要跟我们自己的业务系统互动,可以做到对内部系统数据的查询,修改,直接插入数据,甚至是执行代码呢?这就引出了我们今天要讲的函数调用了。

大语言模型的函数调用是指:大语言模型可以直接调用我们自己定义的函数,从而实现大语言模型与我们代码的互动实现拓展的能力;

基本流程

背景

我这里的背景就以获取某个城市的实时天气为例,比如获取广州市当天的天气。本文以openai为例。

实现流程

Function Calling Diagram Steps
Function Calling Diagram Steps

发送function call请求:开发者先给openai(服务器)发送一个function_call定义请求;

openai返回function的参数并调用本地函数:openai返回function_call请求的function所需要用到的参数,并调用本地函数;

openai返回最终的响应:  也就是结合本地函数返回值和最初的提示词发送的二次请求,openai给出最终对于初始提示词的答复。

我们接下来按流程图步骤逐步实现。

python实现

环境准备

先安装OpenAI包

pip install OpenAI

设置环境变量

#windows
setx OPENAI_API_KEY <your_openai_key>

1、发送function call请求

from openai import OpenAI
import os

client = OpenAI()

tools = [{
    "type": "function",
    "name": "get_weather", #函数名
    "description": "Get current temperature for provided coordinates in celsius.", #函数描述要写好
    "parameters": {
        "type": "object",
        "properties": {
            "latitude": {"type": "number"}, #纬度
            "longitude": {"type": "number"} #经度
        },
        "required": ["latitude", "longitude"], #必填参数
        "additionalProperties": False
    },
    "strict": True#严格模式
}]

#提示词这里:问广州今天的天气怎样()
input_messages = [{"role": "user", "content": "What's the weather like in Guangzhou today?"}]

response = client.responses.create(
    model="gpt-4o",
    input=input_messages,
    tools=tools,
)
print(response.output)

输出:

[ResponseFunctionToolCall(arguments='{"latitude":23.1291,"longitude":113.2644}', call_id='call_P6ZbwrZNWaooKkdKy1QYrpdX', name='get_weather', type='function_call', id='fc_67f7b805b2c48191a2996e870cbf55650e0e19e40e69d7e3', status='completed')]

看到这里打出了调用函数需要用到的参数,实参。注意这里还返回了一个call_id,后面要用到。

当然,你本地也要有这个函数:

def get_weather(latitude, longitude):
    print("get_weather called................................")
    response = requests.get(f"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m")
    data = response.json()
    return data['current']['temperature_2m']

这里我也发一个非function call的响应对比

client = OpenAI()
response = client.responses.create(
    model="gpt-4o",
    input=[{"role": "user", "content": "Write a one-sentence bedtime story about a unicorn."}],
)
print(response.output[0])

输出:

ResponseOutputMessage(id='msg_67f7c096a794819197a9ac7e49b3db53060290b7f1639d43', content=[ResponseOutputText(annotations=[], text='Under the silver glow of the moon, the gentle unicorn danced through the whispering meadows, leaving a trail of stardust dreams for all the sleeping children.', type='output_text')], role='assistant', status='completed', type='message')

2、openai返回function的参数并调用本地函数

解析参数调用本地函数

tool_call = response.output[0]
args = json.loads(tool_call.arguments)

result = get_weather(args["latitude"], args["longitude"])
print(result)

输出:

get_weather called................................
26.4

3、openai返回最终响应

# 把整个function_call的响应添加到第二次的请求体里
input_messages.append(tool_call)  

# input_messages 还要再加一个function_call_output类型的参数,注意tool_call.call_id
input_messages.append({
    "type": "function_call_output",
    "call_id": tool_call.call_id,
    "output": str(result)
})
response= client.responses.create(
    model="gpt-4o",
    input=input_messages,
    tools=tools,
)
#现在回顾下提示词:What's the weather like in Guangzhou today?
print(response.output_text)

输出:

The current temperature in Guangzhou is 26.4°C.

Dotnet实现

先安装包:

<PackageReference Include="OpenAI" Version="2.1.0" />

//这个是做http请求用的
<PackageReference Include="Flurl.Http" Version="4.0.2" />

1、发送function call请求

//准备function_call tool:
privatestaticreadonly ChatTool getWeatherTool = ChatTool.CreateFunctionTool(
    functionName: nameof(GetWeather),
    functionDescription: "Get current temperature for provided coordinates in celsius.",
    functionParameters: BinaryData.FromBytes("""
            {
                "type": "object",
                "properties": {
                    "latitude": {"type": "number"},
                    "longitude": {"type": "number"}
                },
                "required": ["latitude", "longitude"],
                "additionalProperties": false
            }
            """u8.ToArray())
);

//1、注意这里设置好环境变量
ChatClient client = new(model: "gpt-4o", apiKey: Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
ChatCompletionOptions options = new()
{
    //这里支持多个函数的
    Tools = { getWeatherTool },
};
List<ChatMessage> messages =
[
    new UserChatMessage("What's the weather like in Guangzhou today?"),
];
//发送functioncall请求
ChatCompletion completion = client.CompleteChat(messages, options);
var toolCall = completion.ToolCalls.FirstOrDefault();
using JsonDocument argumentsJson = JsonDocument.Parse(toolCall?.FunctionArguments);
Console.WriteLine(JsonSerializer.Serialize(argumentsJson.RootElement));

注意看tools的json,就是函数定义

当然,你本地也要有这个函数:

private static async Task<string> GetWeather(decimal latitude, decimal longitude)
{
    Console.WriteLine("get_weather called................................");
    var url = $"https://api.open-meteo.com/v1/forecast?latitude={latitude}&longitude={longitude}&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m,wind_speed_10m";
    var result = await url.GetAsync().ReceiveJson<JsonElement>();
    var temperature = result.GetProperty("current").GetProperty("temperature_2m").GetDecimal();

    return temperature.ToString();
}

2、openai返回function的参数并调用本地函数

messages.Add(new AssistantChatMessage(completion));
//解析参数
bool hasLatitude = argumentsJson.RootElement.TryGetProperty("latitude", out JsonElement latitude);
bool hasLongitude = argumentsJson.RootElement.TryGetProperty("longitude", out JsonElement longitude);

if (!hasLatitude || !hasLongitude)
{
    throw new ArgumentNullException(nameof(latitude), "The hasLatitude or hasLongitude  argument is required.");
}
//用解析的参数调用本地函数
string toolResult = await GetWeather(decimal.Parse(latitude.ToString()), decimal.Parse(longitude.ToString()));
Console.WriteLine(toolResult);

输出:

get_weather called................................
26.4

看到这里打出了调用函数需要用到的参数,实参。注意这里还返回了一个call_id,后面要用到。

3、openai返回最终响应

//结合本地函数返回值二次调用
messages.Add(new ToolChatMessage(toolCall.Id, toolResult));
completion = client.CompleteChat(messages, options);
Console.WriteLine($"[ASSISTANT]: {completion.Content[0].Text}");

//如果还要继续聊的要把以上记录追加到history
messages.Add(new AssistantChatMessage(completion));

输出:

[ASSISTANT]: The current temperature in Guangzhou is 26.4°C.

总结

整理了下以前的笔记水一篇,大家将就看看能不能查漏补缺[拜托]


源码

https://github.com/gebiWangshushu/Hei.LLM.Test/tree/master/Hei.LLM.Test/OpenAi

[参考]

https://platform.openai.com/docs/guides/function-calling

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值