OpenAI Function Call 就是给 GPT 描述一下我们的函数,然后 gpt 会告诉我们调用哪个函数以及通过什么参数调用。
下面是联网模式的例子
1. 首先准备我们的函数,这里以 google 搜索为例
yarn add googleapis
const searchGoogle = async (input: string): Promise<customsearch_v1.Schema$Result[]> => {
try {
const customSearch = google.customsearch('v1')
const res = await customSearch.cse.list({
cx: config.google.searchId,
key: config.google.apiKey,
q: input,
start: 1,
num: 5,
hl: 'zh-CN',
safe: 'active',
cr: 'countryCN',
gl: 'cn',
filter: '1',
})
const items = res.data.items
if (items && items.length) {
return items
}
return undefined
} catch (e) {
console.error(e)
}
return undefined
}
async function* search(json?: string): AsyncIterableIterator<string> {
if (!json) {
return
}
const args = JSON.parse(json)
const query = args.query
if (!query) {
return
}
const searchRes = await pluginService.googleSearch(query)
if (!searchRes) {
yield '搜索失败❌,请重试'
return
}
const msg = `这是我的提问:${question}\n这是我在${type}搜索“${query}”的结果:\n${
JSON.stringify(
searchRes.map(s => ({
title: s.title,
snippet: s.snippet,
}))
)
}\n请结合搜索结果回答`
yield* chatService.chatStream(user, msg)
}
2. 然后是给 openai 的 functions 描述
const searchGoogleGptFunction: ChatCompletionFunctions = {
name: 'search_google_when_gpt_cannot_answer',
description: '当 gpt 遇到无法回答的或者需要搜索引擎协助回答时从 google 搜索',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: '搜索句,支持中文或者英文',
}
},
}
}
3. 最后是调用 openai
async function* chatStream(
user: string | any
question: string,
): AsyncIterableIterator<string> {
const histories = [] //todo getHistories
const messages: GptMsg[] = [...histories, { role: 'user', content: question }]
const res = await openai.createChatCompletion({
model: model,
messages: messages,
stream: true,
functions: [searchGoogleGptFunction],
function_call: fun?.function_call,
}, {
responseType: 'stream',
})
let hasFunctionCall = false
let functionName = ''
let functionArguments = ''
readData: for await (const chunk of res.data) {
const lines = chunk
.toString()
.split('\n\n')
.filter((line: any) => line.trim().startsWith('data: '))
let text = ''
for (const line of lines) {
const data = line.replace(/^data: /, '').trim()
if (data === '[DONE]') {
break readData
}
const json = JSON.parse(data)
const delta = json.choices[0].delta
const functionCall = delta.function_call
if (functionCall) {
hasFunctionCall = true
if (functionCall.name) {
functionName = functionCall.name
}
if (functionCall.arguments) {
functionArguments += functionCall.arguments
}
} else {
const token = delta.content
if (token) {
text += token
}
}
}
if (text) {
yield text
}
}
if (hasFunctionCall && name === 'search_google_when_gpt_cannot_answer') {
yield* search(functionArguments)
}
}