早期大模型本身只能处理已有知识库的问题,无法处理一些实时性的问题,例如:
今天天气如何?今天有什么爆炸性新闻?OpenAI 提出了 Function Calling 的概念,让大模型能够指导 Agent 调用工具获取实时信息。
Function Calling 已经更名为 Tools.
Tools 的 工作流程
注意,不同的模型厂商,使用工具时略有差异。
以 OpenAI 为例进行演示
注册工具#
-
注册一个叫
getCurrentWeather的函数,告诉模型它的作用和参数格式。 -
用户问:“北京的天气怎么样?”
-
模型就会自动决定:嗯,这需要调用那个工具。并返回函数名称和参数(location: “北京”)给我们。
Step 1:定义工具函数和 tools 描述
Tools 各项描述含义:
type: "function"目前只支持值为 function, 必须指定function是对具体函数的描述name是函数名, 需要跟函数的名称一致, 方便我们后续实现对函数名的调用descirption函数的描述, 你可以理解成对 LLM 决定什么是否调用该函数的唯一信息, 这部分清晰的表达函数的效果parameters函数的参数, OpenAI 使用的是通用的 JSON Schema 去描述函数的各个参数, 在我们这里使用了数组作为参数的输入, 其中有两个 keylocation一个 string 值表示位置unit表示请求的单位
required通过这个 key 告知 LLM 该参数是必须的
使用工具#
调用模型,观察是否生成函数调用请求。
使用 openai 官方库来调用 OpenAI 的 gpt-3.5-turbo 模型。
询问模型一个问题,如下:
// 前面代码略...
// 1. 模拟用户提问const messages = [{ role: 'user', content: '北京的天气怎么样?' }]
// 2. 模型判断是否调用函数const result = await openai.chat.completions.create({ model: 'gpt-3.5-turbo-1106', // 指定模型 messages, // 用户的问题 tools, // 工具箱🧰 让大模型知道有哪些工具可以使用})
console.log('模型返回内容:')console.dir(result.choices[0], { depth: null })模型输出内容:
{ index: 0, // 本条消息在返回数组中的位置(第 0 条) message: { role: 'assistant', // 角色:来自助手(模型) content: null, // 没有自然语言内容,因为这一步是“发起工具调用” tool_calls: [ // 工具调用列表:本例中只有 1 个 { id: 'call_WnSIQJJUjgifGqgAnzNkmgLR', // 此次工具调用的唯一 ID(后续对齐结果用) type: 'function', // 工具类型:函数调用 function: { name: 'getCurrentWeather', // 要调用的函数名(应与你声明的 tools 匹配) // 以“字符串化的 JSON”形式给出的参数(API 规范常见写法) // 使用时需要先 JSON.parse() 成对象再传给真实函数实现 arguments: '{"location":"北京","unit":"celsius"}' } } ], refusal: null, // 没有拒答(如果模型拒绝执行,会给出拒绝原因/说明) annotations: [] // 额外标注信息(如安全/敏感片段标注);此处为空 }, logprobs: null, // 未返回 token 级别的对数概率(一般调试/研究才会用到) // 结束原因:'tool_calls' 表示该轮对话以“请求调用工具”作为结尾 // 正确的下一步是:宿主/应用去执行这个工具,再把结果回传给模型 finish_reason: 'tool_calls'}后续步骤#
目前已经完成了:
- 用户提问
- 模型决定要不要调用函数
- 并返回
tool_calls,告诉我们该调用哪个函数、传什么参数
但这只是“调用的意图”,我们还需要完成“真正执行”与“继续对话”的闭环。
完整流程如下:
1. 用户提问 --> "北京天气如何?" ↓2. 模型返回 tool_calls ➜ getCurrentWeather({ location: "北京" }) ↓3. JS 调用本地函数 --> 得到天气结果 ↓4. 把结果再发送给模型 --> 模型生成最终回答课堂练习
完成后续代码。