概述
大家可能都知道在前天,也就是613日进行了版本更新,除了降低使用价格以及增加gpt3.5 16k版本外,最大个一个更新就是增加 funtion_call
函数回调功能,本文就通过一个简易的案例,来尝试下回调函数功能.
首先大家思考一个问题 openai能做什么,最一开始只能做自然语义的分析回复,简单来说就是一个交互问答。不能调用计算机硬件,不能获取网络数据。直到GPT4开发了插件功能,我们足以补足这些能力,但是插件系统只能使用在gpt官网而本次更新大家可以理解为开放了接口层级的权限包括但不限于 调用计算机硬件,获取网络数据
案例思路分析
使用语言:NodeJS
以获取天气以及发送邮件这两个功能进行分析
- 发送邮件
- 在本地实现发送邮件的函数,在调用chatgpt 接口时将本地函数相关信息携带
- gpt 在和用户交互的过程中去分析 用户是否有发送邮件的需求,如果有返回的数据增加,回调函数项
- 开发者检测gpt返回的信息是否有
回调函数项
如果有就调用本地函数 - 将本地函数的调用结果,发送给gpt在进行一次自然语言回复
- 获取天气
- 在本地实现获取天气的函数,在调用chatgpt 接口时将本地函数相关信息携带
- gpt 在和用户交互的过程中去分析 用户是否有获取天气的需求,如果有返回的数据增加,回调函数项
- 开发者检测gpt返回的信息是否有
回调函数项
如果有就调用本地函数 - 将本地函数的调用结果,发送给gpt在进行一次自然语言回复
模型更新
先通过模型列表接口查看现已有的模型列表
javascript复制代码const { Configuration, OpenAIApi } = require("openai");
const configuration = new Configuration({
apiKey:OpenKey,
});
const openai = new OpenAIApi(configuration);
async function getModalList() {
const response = await openai.listModels();
console.log(response.data)
}
getModalList();
得到更新结果
json复制代码 {
id: 'gpt-3.5-turbo-0301',
object: 'model',
created: 1677649963,
owned_by: 'openai',
permission: [Array],
root: 'gpt-3.5-turbo-0301',
parent: null
},
{
id: 'gpt-3.5-turbo-16k',
object: 'model',
created: 1683758102,
owned_by: 'openai-internal',
permission: [Array],
root: 'gpt-3.5-turbo-16k',
parent: null
},
{
id: 'gpt-3.5-turbo-0613',
object: 'model',
created: 1686587434,
owned_by: 'openai',
permission: [Array],
root: 'gpt-3.5-turbo-0613',
parent: null
}
包含以下版本更新
- GPT4(0613为版本日期)
- Gpt-4-0613包括一个更新和改进的模型与函数调用。
- Gpt-4-32k-0613包括与gpt-4-0613相同的改进,以及扩展的上下文长度,以便更好地理解较大的文本。
- gpt-4-0314 3月份版本,即将废弃可以使用到 9月13日
- gpt-4-32k-0314 3月份版本,即将废弃可以使用到 9月13日
- GPT3.5
- gpt-3.5-turbo-0613 gpt-3.5-turbo-0613包括与GPT-4相同的功能调用,以及通过系统消息更可靠的可操纵性,这两个功能允许开发人员更有效地指导模型的响应。
- gpt-3.5-turbo-16k 大容量的3.5 ,提供的上下文长度是gpt-3.5-turbo的4倍,价格是gpt-3.5-turbo的两倍:每1K输入代币0.003美元,每1K输出代币0.004美元。16k上下文意味着该模型现在可以在单个请求中支持约20页的文本。
- gpt-3.5-turbo-0301 3月份版本,即将废弃可以使用到 9月13日
1. 模拟一个简单的命令行对话
新建一个index.js
文件,里面加入如下代码
javascript复制代码const readline = require("readline");
const { chat } = require("./chat");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
function askQuestion() {
rl.question("user:", async (prompt) => {
const answer = await chat(prompt);
console.log("gpt:", answer);
askQuestion();
});
}
askQuestion();
新建一个chat.js
文件输入如下代码
javascript复制代码const axios = require("axios");
const { Configuration, OpenAIApi } = require("openai");
// 配置openai
const OpenKey = "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";
const configuration = new Configuration({
apiKey: OpenKey,
});
const openai = new OpenAIApi(configuration);
// 存储上下文记忆
const messages = [];
async function chat(prompt) {
// 每次用户输入的内容添加到记忆体
messages.push(
{ role: "user", content: prompt },
)
console.log(messages)
const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo-0613",
messages,
});
console.log(completion.data.choices[0].message);
const { message } = completion.data.choices[0];
messages.push(message)
return message.content;
}
module.exports = { chat }
运行 node index.js
能简单对话就ok 效果如下
写一个发送邮件的方法,交给GPT调用
只做测试所以发送方案并没有完整实现
javascript复制代码function sendMail(mail,msg) {
console.log("发送",msg,"给:",mail)
return "邮件已发送"
}
module.exports = {
sendMail
}
修改OpenAi的发送模型
function_call
: chat 接口中与 messages 平级的 function_call 用于指定模型是否启用函数式回答,如果传入的是 none 那么,就不需要传入 functions 参数,也就是跟今天之前的调用是一样的。
如果传入的是 auto ,那么你需要传入 functions 参数,给出一些函数,让模型去智能选择该用哪个函数。
而只有当 function_call 传入 auto 且 functions 传入了函数数组,且模型觉得需要用函数返回时,返回的message中才会有 function_call,我们就需要在自己的程序中去根据模型指示,调用合适的函数处理。
functions
: 数组格式,允许你传入一组函数定义,由模型来智能选择该用哪个函数。
注意,即使用 16k 版本,上下文长度也是有限的,你不可能把所有的函数定义都传进来。
所以需要在调用这个接口之前,先分析意图,找出最可能符合本次用户问题需求的函数。
这里就很考验大家的处理思路了,我建议用上嵌入,毕竟嵌入是最懂语义化的。
javascript复制代码const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo-0613",
function_call: "auto",
functions: [
{
name: "sendMail",
description: "给某一个邮箱发送邮件",
parameters: {
type: "object",
required: ["mail", "msg"],
properties: {
mail: {
type: "string",
description: "用户邮箱",
},
msg: { type: "string", description: "要发送的内容" },
},
},
},
],
messages,
});
检测模型是否要调用函数
LLM会根据当前的语义判断你是否要调用某一个本地的方法,比如我们刚才传递了一个sendMail
的方法,接下来在聊天的对话的过程中如果发现有需要发送邮件
的需求那么就会在返回的message数据里增加 function_call
字段表示要调用某一个方法,如下图
大家会发现前几次对话,gpt没有分析出调用函数的意向,所以返回数据不变,但是到了最后发现我们有发送邮件的意愿
返回的数据中多了一个 function_call
方法, 并且里面包含你需要调用哪个方法name
,以及调用该方法的参数arguments
所以我们判断function_call
字段是否存在来调用本地函数
javascript复制代码const funs = require("./utils");
const { message } = completion.data.choices[0];
messages.push(message);
const { content, function_call } =message;
// 判断是否执行本地函数
if(function_call) {
//执行回调逻辑
const { name, arguments } = function_call;
const {mail,msg} =JSON.parse(arguments)
console.log(JSON.parse(arguments))
console.log(mail,msg)
funs[name](mail,msg)
}
本地函数调用成功 结果如下
将函数的调用结果 通知gpt,进行结果输出
这时候大家发现这一次的gpt返回目的是为了调用本地函数,但是没有其他输出返回,所以我们还需要将调用的结果告诉chatgpt,让gpt根据函数的调用结果在进行一次自然语言回复 这个时候,请求时的 messages 中 role 为 assisant 中那条message 就需要增加 function_call,其实就是把模型上一次返回的数据原样放进去。 然后在 messages 中还需要增加一条 role 为 function 的数据,把我们本地函数执行完的结果告诉模型。
javascript复制代码async function chat(prompt) {
// 每次用户输入的内容添加到记忆体
messages.push({ role: "user", content: prompt });
// console.log(messages)
const completion = await openai.createChatCompletion({
model: "gpt-3.5-turbo-0613",
function_call: "auto",
functions: [
{
name: "sendMail",
description: "给某一个邮箱发送邮件",
parameters: {
type: "object",
required: ["mail", "msg"],
properties: {
mail: {
type: "string",
description: "用户邮箱",
},
msg: { type: "string", description: "要发送的内容" },
},
},
},
],
messages,
});
console.log(completion.data.choices[0].message);
const { message } = completion.data.choices[0];
const { content, function_call } = message;
// 保存gpt返回内容
messages.push(message);
// 判断是否执行本地函数
if (function_call) {
//执行回调逻辑
const { name, arguments } = function_call;
const { mail, msg } = JSON.parse(arguments);
console.log(JSON.parse(arguments));
console.log(mail, msg);
const function_response = funs[name](mail, msg);
// 将函数调用结果通知gpt
const completionFun = await openai.createChatCompletion({
model: "gpt-3.5-turbo-0613",
messages: [...messages, {
"role": "function",
"name": name,
"content": function_response,
},],
});
return completionFun.data.choices[0].message.content
}
return content;
}
总结与展望
到这大家基本也了解了funtion_call
功能的基本用法,用一句话概括就是gpt4插件能力,在3.5以及4上通过api接口的方式进行展现,赋予了开发者更大的权限与能力.
接下来以gpt为大脑的AGI应用将从出不穷,以下的功能都可以实现
- 结合sd或者mj实现 一句话完成小红书图文
- 小说自动拆解转图文
- 数字人自动创建
…