闽公网安备 35020302035485号
Function calling是ChatGPT的新功能,它允许我们在API调用中描述特定函数的特性,模型会根据我们的描述,智能地决定是否生成一个包含函数参数的JSON对象作为输出。这样我们就可以用ChatGPT和其他的工具或API进行交互,实现更多的功能。
例如:
1.将自然语言转换为数据库查询和API调用。我们可以使用这个特性来将普通语言转换为内部API调用,来回答问题或提供更好的选项。//文档地址 https://platform.openai.com/docs/guides/gpt/function-calling //官网示例 https://github.com/openai/openai-cookbook/blob/main/examples/How_to_call_functions_with_chat_models.ipynb例如我们想要模型调用一个名为get_current_weather的函数,来获取某个地点的当前天气情况,可以这样设置functions参数:
{
"get_current_weather": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "地点,比如:北京、上海"
},
"unit": {
"type": "string",
"enum": ["摄氏度", "华氏度"]
}
},
"required": ["location", "unit"]
}
}
这段JSON Schema描述的意思是:{
"name": "get_current_weather"
}
这样,当向模型发送一个类似于“今天北京的天气如何?”的输入时,模型就会尝试生成一个符合get_current_weather函数签名的JSON对象作为输出,比如:{
"location": "北京",
"unit": "摄氏度"
}
当然,如果我们不添加function_call参数也可以,模型会根据我们的输入和函数描述,自动判断是否需要调用函数,以及调用哪个函数。但是这样可能会降低准确性和可靠性,所以建议尽量明确地告诉模型你想要它做什么。public class RegistrationStatus
{
/// <summary>
/// 挂号状态
/// </summary>
public bool Status { get; set; }
/// <summary>
/// 状态说明
/// </summary>
public string Message { get; set; }
/// <summary>
/// 挂号信息
/// </summary>
public RegistrationInfo RegistrationInfo { get; set; }
}
public class RegistrationInfo
{
/// <summary>
/// 序号
/// </summary>
public int QueueNumber { get; set; }
/// <summary>
/// 手机号码
/// </summary>
public string PhoneNumber { get; set; }
/// <summary>
/// 挂号时间
/// </summary>
public DateOnly RegistrationTime { get; set; }
/// <summary>
/// 提交时间
/// </summary>
public DateTime SubmissionTime { get; set; }
}
1、Register(挂号)// 堆代码 duidaima.com
//挂号
public static string Register(string phoneNumber, DateOnly registrationTime)
{
if (_registrationInfos.Any(m => m.PhoneNumber == phoneNumber && m.RegistrationTime == registrationTime))
return JsonSerializer.Serialize(new RegistrationStatus
{
Status = false,
Message = "请勿重复挂号",
});
var registrationInfo = new RegistrationInfo
{
QueueNumber = ++_queueNumber,
PhoneNumber = phoneNumber,
RegistrationTime = registrationTime,
SubmissionTime = DateTime.Now
};
_registrationInfos.Add(registrationInfo);
// 获取给定位置的当前天气信息
return JsonSerializer.Serialize(new RegistrationStatus
{
Status = true,
Message = "挂号成功",
RegistrationInfo = registrationInfo
});
}
//查询
public static string Query(string phoneNumber, DateOnly registrationTime)
{
var registrationInfo = _registrationInfos.FirstOrDefault(m => m.PhoneNumber == phoneNumber && m.RegistrationTime == registrationTime);
var registrationStatus = new RegistrationStatus
{
Status = registrationInfo != null,
Message = registrationInfo != null ? "查询到挂号信息" : "未查询到挂号信息",
RegistrationInfo = registrationInfo
};
return JsonSerializer.Serialize(registrationStatus);
}
为了方便调用,我们定义一个 AvailableFunctions 变量来存储函数名和函数委托。

Install-Package Betalgo.OpenAI -Version 7.1.2-beta
new FunctionDefinition
{
Name = "gpt_register",
Description = "通过指定的手机号码和日期进行挂号",
Parameters = new FunctionParameters
{
Type = "object",
Properties = new Dictionary<string, FunctionParameterPropertyValue>
{
{
"phoneNumber",new FunctionParameterPropertyValue
{
Type = "string",
Description = "挂号使用的手机号码"
}
},
{
"registrationTime" ,new FunctionParameterPropertyValue
{
Type = "string",
Description = "挂号的日期,比如:明天",
Enum = new[] { "今天","明天","后天" }
}
}
},
Required = new[] { "phoneNumber", "registrationTime" }
},
}
我们定义了一个名为 gpt_register 的函数,它的功能是通过指定的手机号码和日期进行挂号。phoneNumber 参数的类型是字符串,它表示挂号使用的手机号码。registrationTime 参数的类型也是字符串,它表示挂号的日期,并且它只能取三个值:今天、明天或后天。这两个参数都是必须提供的,否则函数无法执行。new FunctionDefinition
{
Name = "gpt_query",
Description = "根据手机号码查询挂号信息",
Parameters = new FunctionParameters
{
Type = "object",
Properties = new Dictionary<string, FunctionParameterPropertyValue>
{
{
"phoneNumber",new FunctionParameterPropertyValue
{
Type = "string",
Description = "要查询的手机号码"
}
},
{
"registrationTime" ,new FunctionParameterPropertyValue
{
Type = "string",
Description = "挂号的日期,比如:明天",
Enum = new[] { "今天","明天","后天" }
}
}
},
Required = new[] { "phoneNumber", "registrationTime" }
}
}
我们定义了一个名为 gpt_query 的函数,它的功能是根据手机号码及日期查询挂号信息。phoneNumber 是一个字符串类型,表示要查询的手机号码。registrationTime 也是一个字符串类型,表示挂号的日期。这个参数只能取三个值:今天,明天,或后天。这两个参数都是必须提供的,否则函数无法执行。
await foreach (var completion in completionResult)
{
if (cancellationToken.IsCancellationRequested)
break;
// 堆代码 duidaima.com
if (completion.Successful)
{
var responseMessage = completion.Choices.First().Message;
if (responseMessage.FunctionCall != null)
{
var functionName = responseMessage.FunctionCall.Name;
var functionArgs = JsonSerializer.Deserialize<Dictionary<string, string>>(responseMessage.FunctionCall.Arguments);
var functionToCall = ChatGPTFunctionCalling.AvailableFunctions[functionName];
var registrationTime = functionArgs.GetValueOrDefault("registrationTime") switch
{
"后天" => DateOnly.FromDateTime(DateTime.Now.AddDays(2)),
"明天" => DateOnly.FromDateTime(DateTime.Now.AddDays(1)),
_ => DateOnly.FromDateTime(DateTime.Now.Date),
};
var functionResponse = functionToCall(functionArgs.GetValueOrDefault("phoneNumber"), registrationTime);
chatCompletionCreateRequest.Messages.Add(ChatMessage.FromFunction(functionResponse, functionName));
var completionTwo = await _openAiService.ChatCompletion.CreateCompletion(
new ChatCompletionCreateRequest
{
Messages = chatCompletionCreateRequest.Messages,
Model = OpenAI.ObjectModels.Models.Gpt_3_5_Turbo_0613
}, cancellationToken: cancellationToken);
if (completionTwo.Successful)
{
await Response.WriteAsync(completionTwo.Choices.First().Message.Content ?? "", cancellationToken);
await Response.Body.FlushAsync(cancellationToken);
}
else
{
if (completionTwo.Error == null)
throw new Exception("Unknown Error");
await Response.WriteAsync($"{completionTwo.Error.Code}: {completionTwo.Error.Message}");
await Response.Body.FlushAsync();
}
}
else
{
await Response.WriteAsync(responseMessage.Content ?? "");
await Response.Body.FlushAsync();
}
}
else
{
if (completion.Error == null)
throw new Exception("Unknown Error");
await Response.WriteAsync($"{completion.Error.Code}: {completion.Error.Message}");
await Response.Body.FlushAsync();
}
}
整个调用过程可以分为以下几个步骤: