如何使用函数调用在ChatGPT中构建JSON响应结构
如何在ChatGPT中使用函数调用来构建JSON响应结构
ChatGPT对JSON的问题
打开ChatGPT UI并向其要求一些JSON。很大程度上,你会得到一个类似封面照片上所示的响应:一个用markdown格式呈现的JSON对象,两侧都有一些文本解释JSON的内容。
如果你在OpenAI Playground尝试相同的提示,你会看到JSON被包含在三个反引号(markdown语法)中。
这太棒了。ChatGPT通过易于理解的方式解释了响应,但…只对人类来说易于理解。对于机器来说并非如此。机器需要以可靠、一致和可预测的模式来提取数据。
理想情况下,你希望解析来自ChatGPT的响应,并对其进行有用的操作,就像这样:
// 从npm中使用openai包调用ChatGPT
import OpenAI from "openai";
// 使用我们的API密钥创建openai客户端的新实例
const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY });
// 调用ChatGPT的completion端点并请求一些JSON
const gptResponse = await openai.chat.completions.create({
model: "gpt-3.5-turbo",
temperature: 1,
messages: [
{
role: "user",
content: "给我一个表示猫的对象的JSON。"
}
],
});
// 尝试以JSON格式读取响应,
// 很可能会因为语法错误而失败...
const json = JSON.parse(gptResponse.choices[0].message.content);
但只有在每次gptResponse.choices[0].message.content
是有效的JSON时才会起作用。
同时,我们希望返回的JSON始终遵循一个模式:
type Cat = {
name: string,
colour: "brown" | "grey" | "black",
age: number
}
// 读取响应的JSON并将其类型定义为我们的Cat对象模式
const json = <Cat>JSON.parse(gptResponse.choices[0].message.content);
无法依赖ChatGPT以可预测的格式返回有效的JSON可能会在你的应用程序中引入错误,特别是一致性至关重要时。当你编写依赖于ChatGPT实时响应触发特定动作或更新的代码时,这将成为一个真正的问题,因此我们需要找到解决方案。我们有几种方法可以应对这个问题…
如何通过提示工程解决
解决此问题的一种方法是通过提示工程。
在这里,我们向用户提示和系统提示添加了一些说明。这些说明试图强制模型只返回我们想要的JSON,以及我们想要的格式。
对于许多用例来说,这是可以接受的。从上面的截图中可以看到,”Assistant”的响应只是JSON,而且JSON符合我们为其描述的模式。
但这并不是100%都起作用。
下面是完全相同的一对提示,模型的温度设置在1以上。请注意返回的JSON中color
字段不再遵循用户提示中指定的允许的值之一:
函数调用解救
函数调用是使用ChatGPT API的一种新方法。无需从语言模型获取消息,而是收到一个调用函数的请求。
如果您曾在ChatGPT用户界面中使用过插件,则“函数调用”是幕后的功能,它使插件能够与LLM的响应集成。
插件定义了模型可用的函数,并允许它在用户提示的响应中调用这些函数。
在您自己的代码中使用API时,您还可以利用函数调用来更好地控制从模型获取的数据,包括强制它以可预测的格式返回JSON数据。
下面是一个使用函数调用返回响应中的message.function_call
对象的基本请求。
在这里,我们只是要求ChatGPT调用一个函数(“getName”),并在functions: []
数组中提供函数的描述。我们还指示ChatGPT在响应中调用这个函数,通过将function_call
的值设置为我们函数的名称。
const gptResponse = await openai.chat.completions.create({ model: "gpt-3.5-turbo-0613", messages: [ { role: "user", content: "调用函数'getName'并告诉我结果。" } ], functions: [ { name: "getName", parameters: { type: "object", properties: {} } } ], function_call: { name: "getName" }});// 将打印 "getName"...console.log(gptResponse.choices[0].message.function_call.name);
这里要注意的重要一点是,getName()
函数不需要真实存在于我们的代码库中。我们只是告诉ChatGPT它存在,并且可以被调用。
如何添加函数参数
函数调用很棒,因为它使ChatGPT的响应变得可预测和结构化。
在上面的示例中,我们将在gptResponse.choices[0].message.function_call
中得到一个对象,其中包含ChatGPT想要执行我们(虚构的)函数的详细信息。
该对象如下所示,包含函数的名称以及应该调用的任何函数参数的字符串 版本:
{ name: "functionName", arguments: "{ \"arg1\": \"value\" }"}
我们可以利用这第二个arguments
值,描述函数参数的形状,并让ChatGPT填充一些符合我们形状的JSON值。
以下是将原始示例作为函数调用的方式。请注意,在createCatObject
函数的定义中,我们已经向ChatGPT描述了Cat
对象的架构:
type Cat = { name: string, colour: "brown" | "grey" | "black", age: number } const openai = new OpenAI({ apiKey: process.env.OPENAI_KEY }); const gptResponse = await openai.chat.completions.create({ model: "gpt-3.5-turbo-0613", messages: [ { role: "user", content: "创建一个新的Cat对象。" } ], functions: [ { name: "createCatObject", parameters: { type: "object", properties: { name: { type: "string" }, colour: { type: "string", enum: ["brown", "grey", "black"] }, age: { type: "integer" } }, required: ["name", "colour", "age"] } } ], function_call: { name: "createCatObject" } }); const functionCall = gptResponse.choices[0].message.function_call; const json = <Cat>JSON.parse(functionCall.arguments);
这个完成请求会指示ChatGPT创建一个新的猫对象,并将其以特定的格式发送给createCatObject()
函数。最后一行:
const json = <Cat>JSON.parse(functionCall.arguments);
将ChatGPT的参数解析为我们的Cat
类型,该类型与我们给模型描述的预期对象的形状相匹配。
结论
通过ChatGPT进行函数调用不仅能为结果带来清晰度和可预测性,还引入了一种与机器学习模型交互的范式转变。
这种结构化方法确保了来自ChatGPT的响应是可扩展的,能够更轻松地集成到各种应用程序中,无论是简单的Web应用程序还是更复杂的机器学习流水线。
Leave a Reply