Chat GPT のFunction Callingについて（基礎の基礎編）

# 記事を書こうと思ったきっかけ
Fuction Callingについて業務で使っていたので自習として色々な記事を読んでみたが、あまり使い方だったり、概要を掴むことができなかったので、僕なりに理解できたポイントや躓いたポイントを紹介することで、少しでもFunction Callingの学習をしようと思う人への第一歩となればと思い作成しました。
本当の本当に入門編のつもりで書きました。

# 本記事の対象
- Chat GPT APIを少し触った程度でまだ入門者
- Function Callingについてまだあまり理解できていない方
- Function Callingに一度挫折した方

# 本記事の構成
今回はFunction Callingの基礎の基礎を理解することを目的としています。
構成は以下のような感じです。

- Chat GPT APIの簡単な使い方の説明
- Function Callingの概要について実装例を交えながら
- まとめ

※今回はコードは全てJavascriptで記述をしていますが、普段PHPをメインで使用しているため、
コード内に誤りがあるかもしれませんが、その際は優しく教えていただけますと助かります・・・。

次回の記事として細かい検証を交えた記事を書こうと思っています。
そちらの記事も合わせて読んでいただくことでより理解が深まるかと思います。


# Chat GPT APIの簡単な使い方の説明

では、早速内容に入ってきます。
簡単に言うとAPIキーを用意して、
必要なパラメーターを設定して
エンドポイントへアクセスする

例を出してみます。
「こんにちは」というテキストをAPIへ送信する場合です。

<details><summary>サンプルコード</summary>

```js
const API_KEY = "ご自身で取得したAPIキーを設定";
const URL = "https://api.openai.com/v1/chat/completions";

const text = "こんにちは";

    const response = await axios.post(
        URL,
        {
            "model": "gpt-3.5-turbo",
            "messages": [
                {"role": "user", "content": text}
            ]
        },
        {
            headers: {
                "Content-Type": "application/json",
                Authorization: `Bearer ${API_KEY}`,
            },
        }
    );

```
</details>

このあたりの使い方については他の記事でもよくあることなので詳細は省きます。
基本の使い方は以上です。

# Function Callingの概要について

## 体験談・・
Function Callingについては、一言でいうと
**「次にどのFunctionを呼び出すのかを判定して、呼び出すFunctionに必要なデータを
抜き出してくれる機能」**

　躓きポイントかもしれませんが、「Function Calling」という名前から
**「Chat GPTが必要なFunctionを呼び出して実行するのかな」**
　という印象があるままに色々な記事を見たことが理解の妨げになっていました・・・。
もし、同じような印象を持たれている方はそうではないと思って読みすすめてください

## Function Callingの説明

例を出しながら説明をします。
今とある会話形のアプリを作ろうとしています。そのアプリの機能として以下の3つがあるとします。
- 普通の会話ができる。
- 天気を尋ねられたら、天気予報を取得するAPIを実行して天気予報の情報を返す。
- 料理のレシビを尋ねられたら、レシビを取得するためのAPIを実行してレシビ情報を返す。

この際に問題になるのは、ユーザーから送信されたテキストの内容が
①普通の会話を望んでいる
②天気予報を知りたい
③料理のレシピを知りたい

まず、このいずれかのパターンであることを判定する必要があります。
そして②だった場合は、ユーザーから送信されたテキストから「天気を知りたい都市の名前」と「天気を知りたい日付」の情報を抜き出す必要があります。
③だった場合は同じく「レシピを知りたい料理名」を抜き出す必要があります。

図にまとめるとこのような流れになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1031387/082c3022-e074-0ae1-5038-99c7c37399ee.png)


青い部分に注目してください。
ユーザーからの入力は例えば、「明日の東京の天気は？」みたいな入力だったり、「東京の9月1日の天気を教えて」みたいに色々な表現があるわけです。
的確に「都市の名前」と「日付」のみを取得するのは、まぁ面倒くさいです。（「こんにちは」というテキストが来る可能性も考慮しなきゃいけないので・・・、正規表現でゴニョゴニョしたり等考えるとぞっとします。）

そこでFunction Callingの出番です。
まずは天気予報を取得するFunctionの定義です。

<details><summary>サンプルコード</summary>

```js
function getWeather(gptResponseArguments) {

    // 天気の情報を返す処理。本来は外部のAPIを使用して実際のデータを取得することが理想だが、暫定的にダミーデータを返すように
    let date = gptResponseArguments["date"];
    let cityName = gptResponseArguments["cityName"];

    const weatherData = {
        date: date,
        city_name: cityName,
        temperature: "30度",
        weather: "晴れ"
    };

    return JSON.stringify(weatherData);
}
```
</details>

このFunctionはDateとCityNameの情報が必要であると仮定をします。（天気予報を取得する外部のAPIを実行する時に必要と仮定）
gptResponseArgumentsについてはここでは、一旦はChatGPTAPIの戻り値だと思っていて大丈夫です。

このFunctionを動かすためには、gptResponseArgumentsに["date"]と["cityName"]が含まれていることが条件となります。

Chat GPT APIを呼び出す時に以下のパラメーターを渡します。

<details><summary>サンプルコード</summary>

```js

async function getResponse() {
    const functions= setDefinition();
    try {
        const response = await axios.post(
            URL,
            {
                "model": "gpt-3.5-turbo",
                "messages": [
                    {"role": "user", "content": text}
                ],
                "functions": functions
            },
            {
                headers: {
                    "Content-Type": "application/json",
                    Authorization: `Bearer ${API_KEY}`,
                },
            }
        );
    // 後続の処理は省略・・・

function setDefinition() {

    const functions= [
        {
            "name": "getWeather",
            "description": "指定された日付と地域の天気を取得します。",
            "parameters": {
                "type": "object",
                "properties": {
                    "cityName": {
                        "type": "string",
                        "description": "地域の名前"
                    },
                    "date": {
                        "type": "string",
                        "description": "対象の日付"
                    },
                },
                "required": ["city_name", "date"]
            }
        }
    ];
    return functions;
}
```
</details>

新たに"function"という項目が増え、値はJsonスキーマを渡しています。

このJsonスキーマで重要なところは3つです。
- name 
呼び出したいファンクションの名前 （こちらで用意したもの）
- description
ファンクションの説明。どういう機能であるのか。（こちらで用意したもの）
- propaties
呼び出したいファンクションで使用したい値。ユーザーの入力の中から値を抜き取って指定したキーに値を格納してくれる。

これらを定義することで、ユーザーが天気予報を知りたいと思っているなら
次に実行するFunctionの名前とその実行に必要な値を返してくれるという訳です。

わかりやすく言い換えると
**ユーザーは天気予報が知りたいみたいだから、必要な情報を渡すね。あとはその情報を使ってgetWeather()を呼び出してね。**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1031387/a59fce47-4049-e455-bbef-c0f2ae65e096.png)


では、ここまでの流れに追加してレシピの情報も返せるように追加したいと思います。
まずはレシピの情報を返すFunctionの定義。
今回は知りたいレシピの対象となる料理名で外部のAPIを実行すると仮定します。
天気予報のときと同じく、gptResponseArgumentsはChatGPTAPIからの戻り値と思ってください。

<details><summary>サンプルコード</summary>

```js
function getRecipe(gptResponseArguments) {

    // 指定された料理のレシビを返す処理。本来は外部のAPIを使用して実際のデータを取得することが理想だが、暫定的にダミーデータを返すように
    let food = gptResponseArguments["food"];

    const recipe = {
        food: food,
        ingredients: "米、カレー粉、玉ねぎ、にんじん、じゃがいも",
        instructions: "材料を切り、炒めて、水とカレー粉を加えて煮込む。"
    };

    return JSON.stringify(recipe);
}
```
</details>

そしてChatGPTAPIのパラメーターのfunctionを以下のように修正します。
変更点はgetRecipeについての記述を追加しました。
ユーザーの会話の内容がレシピを知りたがっているなら、次に実行するFunctionは、getRecipeで
foodを会話の中から抜き取ってねという指示です。

<details><summary>サンプルコード</summary>

```js
function setDefinition() {

    const functions= [
        {
            "name": "getWeather",
            "description": "指定された日付と地域の天気を取得します。",
            "parameters": {
                "type": "object",
                "properties": {
                    "cityName": {
                        "type": "string",
                        "description": "地域の名前"
                    },
                    "date": {
                        "type": "string",
                        "description": "対象の日付"
                    },
                },
                "required": ["city_name", "date"]
            }
        },
        {
            "name": "getRecipe",
            "description": "指定された料理の作り方を取得します。",
            "parameters": {
                "type": "object",
                "properties": {
                    "food": {
                        "type": "string",
                        "description": "作りたい料理の名前"
                    }
                },
                "required": ["food"]
            }
        }
    ];
    return functions;
}

```
</details>

こうすることにより、ユーザーからの入力が
①普通の会話を望んでいるのか
②天気予報を知りたいのか
③レシピを知りたいのか
を判定してそれぞれで必要な情報を返してくれるというわけです。


# まとめ
Function Callingは分かりやすくまとめると、
ユーザーからの会話の内容を判定して、アプリケーション側で次に実行すべきFunctionの名称を教えてくれると同時に
そのFunctionの実行に必要な値を会話の中身から指定した型で引き出してくれる機能です。
アプリケーション側で用意したFunctionを呼び出すべきではないと判断された場合には普通の会話としての回答を返してくれます。
ここまでをChatGPTAPIが行うのであとは、アプリケーション側でFunctionを実行する流れになります。

次回、実際に作成したコードを元に検証をしてみようと思います。