環境
- node.js v18.16.0
- OpenAIのモデル gpt-3.5-turbo-0613
さっそくコード
const { Configuration, OpenAIApi } = require("openai");
const configuration = new Configuration({
apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);
functions = {
get_current_weather : ({location, offsetDay, unit}) => {
// console.log(args);
// const location = args.location;
// const offsetDay = args.offsetDay;
// const unit = args.unit;
// .. 実際に天気APIなど使ったり
weather_info = {
"location": location,
"temperature": "-10",
"unit": unit,
"offsetDay": offsetDay,
"forecast": ["晴れ"],
}
return JSON.stringify(weather_info);
}
}
async function main() {
const prompt = {role: "user", content: "昨日の浪江の天気を教えて"};
const response1 = await openai.createChatCompletion({
model: "gpt-3.5-turbo-0613",
messages: [prompt],
functions: [
{
name: "get_current_weather",
description: "与えられた場所の天気の情報を返す",
parameters: {
type: "object",
properties: {
location: {
type: "string",
description: "天気を知りたい場所の県や市の名前 例)愛知県名古屋市"
},
offsetDay: {
type: "number",
description: "天気を知りたい今日の日付からの差分の日付。例えば、昨日の天気を知りたい場合は-1"
},
unit: {
type: "string",
enum: ["celsius", "fahrenheit"]
}
},
required: ["location", "offsetDay"]
}
}
]
});
const message = response1.data.choices[0].message;
// console.log(message);
// 関数を実行すると判断
const functionCall = message?.function_call;
if(functionCall) {
const args = JSON.parse(functionCall.arguments || "{}");
const funcResponse = functions[functionCall.name](args);
const response2 = await openai.createChatCompletion(
{
model: 'gpt-3.5-turbo-0613',
messages: [prompt, message, {
role: 'function',
content: funcResponse,
name: functionCall.name
}]
}
);
const message2 = response2.data.choices[0].message;
console.log(message2);
}
}
main();
参考サイト
- https://dev.classmethod.jp/articles/function_calling_nodejs/
- https://github.com/supershaneski/openai-api-function-call-sample/blob/main/lib/mockapi.js
- https://qiita.com/windows222/items/bc2c25b3e7526d8926ec
工夫した点
まず実装上の仕様として、
- 実行したい関数の名前の文字列で来る
- 関数名の文字列からその関数を実行する
- この点はjs的に簡単にできそう!
- 引数はjson形式の文字列で来る
- オブジェクトを引数にわたすしかない
- 自分的にこの点が今回学んだところ
functions[functionCall.name](args);
get_current_weather : (args) => {
console.log(args);
const location = args.location;
const offsetDay = args.offsetDay;
const unit = args.unit;
引数がオブジェクトなら、オブジェクトで受け取ってconstに展開していくのが、あんましスマートじゃないと感じてちょっと調査したところ
functions[functionCall.name](args);
get_current_weather : ({location, offsetDay, unit}) => {
引数をこんな感じで書くことで、展開する手間がはぶけた!
ただ、functions内にわたすparameters
の中身と同じになるはずなので、ここらへんを共通化しつつ、うまく引数受取るのもできたらいいなと思ってます
ちなみに...
参考サイトからoffsetDayを付け加えてみた。
今週の天気教えてってやるとoffsetDayは0
に
今日は土曜なので、-6,-5,-4...って7回functionsが実行される感じだとおもろいなっておもた