使用 tools 让 gpt 更加强大
调用 tools 的流程
首先需要一个有 tools_call 能力的大模型,现在的 gpt-4 系列和 glm-4 系列, claude 部分模型基本都支持了,我这里用的是 glm-4-flash, 智谱免费的大模型。gpt 跟 glm 的调用是一样的。
javascript
const messages = [
{
role: 'user',
content: '帮我查询航班',
},
];
fetch('api_url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer xxx',
},
body: JSON.stringify({
model: 'glm-4-flash',
messages: messages,
}),
})
.then((response) => response.json())
.then((data) => {
message.push(data.choices[0].message);
});
这是一般调用接口的办法。这里没用库,直接用的 http 请求。如果要启用 tools 的话,应该加上 tools 字段,告诉大模型有哪些工具
js
const messages = [
{
role: 'user',
content: '帮我查询航班',
},
];
fetch('api_url', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer xxx',
},
body: JSON.stringify({
model: 'glm-4-flash',
messages: messages,
tools: tools,
}),
})
.then((response) => response.json())
.then((data) => {
console.log(data.choices[0].message.content);
message.push(data.choices[0].message);
});
其中 tools 是一个对象数组,这个对象应该是这样的
ts
interface tool {
type: string // 一般是'Function', 有些大模型本身提供了一些工具,如智谱提供了web_search
function?:{ // 如果type是'Function', 那应该有这个属性。一般与type相同
name:string // 函数名
description:string // 一段描述,告诉大模型这是什么,怎么用,什么时候用
parameters:{
type: 'object'
properties:{
// 参数名:参数描述
[key: string]: {
description: string // 告诉ai这个参数是什么
type:string // 告诉ai这个参数应该是什么类型
}
},
required:string[] // 声明哪些是必要的
}
}
}
我的查询航班功能应该是这样的
js
const tools = [
{
type: 'function',
function: {
name: 'get_flight',
description: '根据起点和终点、日期,查询对应日期的航班号',
parameters: {
type: 'object',
properties: {
departure: {
description: '起点',
type: 'string',
},
destination: {
description: '终点',
type: 'string',
},
date: {
description: '日期',
type: 'string',
},
},
required: ['departure', 'destination', 'date'],
},
},
},
];
如果没有提供参数的话,大模型会要求你提供(如果是能力低的模型有可能编造数据,不过应该不太可能吧)。
有了告诉了 ai 有什么 tools 之后还需要自己实现这些方法。如果参数齐全的话,ai 的回复会发生改变。一般data.choices[0].message
是这样的
ts
interface message {
role: string;
content: string;
}
启用 tools 后会变成这样
ts
interface message {
role: string;
content: string | null;
tool_calls?: {
id: string;
function: {
name: string; // 调用的函数名
arguments: string; // 参数
};
}[];
}
当 content 为空的时候,就说明调用了 tool,此时 tool_calls 存在
js
const tools = [
// 略
];
let messages = [
{
role: 'user',
content: '帮我查询2024-12-11,北京到上海的航班',
},
];
const sendToAI = async () => {
const res = await fetch(api_base, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer xxx',
},
body: JSON.stringify({
model: 'glm-4-flash',
messages: messages,
tools: tools,
}),
});
const data = await res.json();
const message = data.choices[0].message;
if (message.content) {
messages.push(message);
} else {
const result = useTools(message);
messages.push(result);
sendToAI();
}
};
const useTools = async (message) => {
const tool_call = message.tool_calls[0];
const result = callTool(
tool_call.function.name,
tool_call.function.arguments
);
return {
role: 'function', // 貌似一些旧的时function,一些是tool,我以前的代码时function
tool_call_id: tool_call.id,
name: tool_call.function.name,
content: result,
};
};
const callTool = async (toolName, arg) => {
switch (toolName) {
case 'get_flight': {
const obj = JSON.parse(arg);
return get_flight(obj);
}
default: {
break;
}
}
};
const get_flight = (arg) => {
// 获取航班信息的操作
return '1234';
};
这个 tool_calls 虽然是个数组,但是 openai 的文档里好像写不推荐并行函数调用,智谱的文档也没写并行函数调用该怎么做,我也没遇到过并行函数调用的情况,这里直接选择第一个元素了。
如果需要并行函数调用的话,那 useTools 应该返回一个数组,然后
js
messages = [...messages, ...result];
简单写了一下,如有错误还请指出