天気を教えてくれるプログラムに"わんこそば食べたい"と無茶振り Function Calling実験


OpenAIのAPIのFunction Callingを使うと曖昧な命令から文脈を理解して適切な関数を呼び出してくれます。

Function Callingの理解も兼ねて、盛岡の天気を教えてくれるプログラムを組みつつ、どんな命令にどう対応するか見てみます。

## 実験手順

1. 盛岡の天気を教えてくれる関数を入れたプログラムを作る
2. 盛岡の天気は？明日の天気は？などを聞いてみる
3. ねこの名前付けたいと関係ないことを言ってみる
4. **わんこそば食べたいと盛岡っぽいけど天気関係ないことを聞いてみる**

みたいなことをやってみます。

## 1. 盛岡の天気を教えてくれる関数を入れたプログラムを作る

getMoriokaWeather()関数を設定しているプログラムです。

ちなみにpropertiesに関数への引数を設定できますが、今回は呼び出しだけなので引数などは設定していません。

```javascript
const OpenAI = require('openai');
const openai = new OpenAI({
    apiKey: 'APIキー',
});

const prompt = {
    role: 'user',
    content: process.env.USERTEXT, //ターミナルから実行時に命令を入力して受ける
};

//盛岡の天気を取得する関数
const getMoriokaWeather = async () => {
	return '晴れです。';
}

//実行
const main = async () => {
    const gptOptions = {
        model: "gpt-4-0613",
        messages: [prompt],
        function_call: "auto",
        functions: [
            {
                name: "getMoriokaWeather",
                description: "盛岡の天気を取得",
                parameters: {
                    type: "object",
                    properties: {}
            },
          },
        ],
    }
    
    const completion = await openai.chat.completions.create(gptOptions);
    console.log(completion.choices[0]);
}


main(); //実行
```

こんな感じで実行します。 **明日の天気は？** などと聞いてみます。

```bash
$ USERTEXT=明日の天気は? node fc.js 
```

## 2. 天気を聞いてみる

通常の使い方ですね。

```bash
$ USERTEXT=明日の天気は? node fc.js 

{
  index: 0,
  message: {
    role: 'assistant',
    content: null,
    function_call: { name: 'getMoriokaWeather', arguments: '{}' }
  },
  finish_reason: 'function_call'
}
```

そうすると **GPTが文脈を理解してくれて、getMoriokaWeatherを呼び出し**てくれてますね。

**明日の天気は? 盛岡の天気は?** などを聞いても同じくgetMoriokaWeatherを呼び出してくれました。

## 3. ねこの名前を付けたいと無茶振り

**"ねこの名前を付けたい"** と、盛岡とも天気とも全く関係ないことを聞いてみます。

```bash
$ USERTEXT=ねこの名前をつけたい node fc.js 

{
  index: 0,
  message: {
    role: 'assistant',
    content: 'もちろんです、ねこちゃんにふさわしい名前を考えましょう。ねこちゃんの性別や、特徴や性格について教えていただけると名前をつけやすいです。また、ご希望があればその内容も教えてください。'
  },
  finish_reason: 'stop'
}
```

## 4. わんこそば食べたい と呟いてみる

リアルで発言してたらしらねーよ、勝手に食えよって言われそうですが

```bash
$ USERTEXT=わんこそば食べたい node fc.js 
{
  index: 0,
  message: {
    role: 'assistant',
    content: 'それは良いですね！盛岡のわんこそばは一度食べてみる価値がありますよ。せっかくなので天気が良い時に行きましょうか。盛岡の天気を確認しますね。\n' +
      '\n' +
      '{ "functions.getMoriokaWeather": {} }'
  },
  finish_reason: 'stop
```

`それは良いですね！盛岡のわんこそばは一度食べてみる価値がありますよ。せっかくなので天気が良い時に行きましょうか。盛岡の天気を確認しますね。`

と返事がありましたね、さらに

`{ "functions.getMoriokaWeather": {} }`


も呼び出してくれています。

どっちもやってくれるのか、、すごいな

## おまけ1. 福田パン食べたい

わんこそばはあまりにも知名度があって盛岡っぽいかもなと思ったのでもう少しマイナーな食べ物を指定してみます。

```bash
$ USERTEXT=福田パン食べたい node fctest1.js 

{
  index: 0,
  message: {
    role: 'assistant',
    content: 'それは良い選択ですね！福田パンは盛岡市の特産品で、そのユニークな形状と美味しさで人気ですね。どんな種類の福田パンがお好みですか？胡麻あんパンやクリームパン、チーズパンなど、たくさんのバリエーションがありますよね。\n'
  },
  finish_reason: 'stop'
}
```

福田パンが盛岡のものだってのは認知してくれているけど、天気調べようぜって話にはならないらしい。

https://ja.wikipedia.org/wiki/%E7%A6%8F%E7%94%B0%E3%83%91%E3%83%B3

## おまけ2. グルージャ盛岡勝ってるかな? 

盛岡のサッカーチームでJ3のチームです。

https://grulla-morioka.jp/

```bash
$ USERTEXT=グルージャ盛岡勝ってるかな node fctest1.js 

{
  index: 0,
  message: {
    role: 'assistant',
    content: 'すみませんが、現状私の機能では特定のスポーツチームの試合結果を取得することはできません。直接公式サイトやスポーツニュースサイトなどをご確認いただくことをおすすめします。'
  },
  finish_reason: 'stop'
}
```

J3でマイナーすぎてGPTの辞書になかったのかなと一瞬思いましたが、速報っぽいのは元々GPTの学習データ外なのでこれこそFunction Callingの活用ポイントかもしれないですね。

## まとめ

- A. ど真ん中な質問（盛岡の天気は?など）
	- 設定している関数をそのまま実行するような挙動
- B. 全然関係ない質問（ねこの名前つけたい など）
	- ChatGPTなどの挙動と同様にプログラム関係なく会話してくれる
- C. ちょっと関係しそうな質問（わんこそば食べたい）
	- AとBの両方の間を取ったような挙動で、会話もしつつ関数も呼び出してくれる

**わんこそば食べたいと聞いてもちょっといい感じに会話しつつ関数呼び出しも** してくれることが分かりました。