OpenAIのFunctionCallingをTypeScriptなしでNode.jsで使ってみる

## 環境
* node.js v18.16.0
* OpenAIのモデル gpt-3.5-turbo-0613

## さっそくコード

```js
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形式の文字列で来る
	* オブジェクトを引数にわたすしかない
	* 自分的にこの点が今回学んだところ

```js
functions[functionCall.name](args);

  get_current_weather : (args) => {
    console.log(args);
    const location = args.location;
    const offsetDay = args.offsetDay;
    const unit = args.unit;
 ```

引数がオブジェクトなら、オブジェクトで受け取ってconstに展開していくのが、あんましスマートじゃないと感じてちょっと調査したところ

```js
functions[functionCall.name](args);

  get_current_weather : ({location, offsetDay, unit}) => {
```
引数をこんな感じで書くことで、展開する手間がはぶけた！

ただ、functions内にわたす`parameters`の中身と同じになるはずなので、ここらへんを共通化しつつ、うまく引数受取るのもできたらいいなと思ってます

## ちなみに...

参考サイトからoffsetDayを付け加えてみた。
今週の天気教えてってやるとoffsetDayは`0`に
今日は土曜なので、-6,-5,-4...って7回functionsが実行される感じだとおもろいなっておもた