ChatGPTのfunction callingを試す

これなーに

ChatGPTのfunction callingがなんだか良さそうな予感。
web browsingのように、内部でコールするファンクションをよしなに判断してくれるみたいです。

参考

toukei-lab.com

コード

github.com

どんなことができる?

以下のように、chatGPTのチャットじゃ絶対結果を返せないような特定の質問に回答できるようになるよ!

入力:田中さんの英語のテストの点数を教えて
出力:田中さんの英語のテストの点数は30点です。
入力:田中さんの6/30のスケジュールを教えて
出力:田中さんの6/30のスケジュールは以下の通りです:
- 10時:A社とのミーティング
- 12時:友人Bとのランチ

ご参考までに。
入力:アメリカの独立記念100周年時の大統領は?
出力:アメリカの独立記念100周年時の大統領は、1876年の時点で大統領を務めていたユリシーズ・S・グラントです。

お、さすが(これは通常のGPTが強いだけ)

入力:山田君の国語と算数の点数を教えて?
出力:山田君の国語の点数は60点で、算数の点数は85点です。

アメリカの独立記念100周年時の大統領の情報はネットにあるから答えられるけど、
田中君の国語の点数はネットにないから回答できないはず(めっちゃ有名な田中君なら知ってるかもだけど、大半のそうじゃない田中君)
いや別に大半のそうじゃない田中君をディスるつもりじゃないんけど?

概要

function callingは

  • openai.ChatCompletion.create()の引数function_call="auto"を指定すると、定義した複数の関数のどれかを利用するかの判定を自動で実施する。
  • openai.ChatCompletion.create()の引数functions=に利用したいfunctionをリストで渡すことができる。
  • functionsに指定するfunctionは、質問文のクエリから利用したいpropertiesをjson形式で取得して、特定の関数の引数にセットして値を取得する。
  • 値を取得するための関数の中に、検索したい対象のデータを突っ込んでおくことで、特定のデータからの検索を実現する。

詳細

大まかな流れは以下。

  1. 特定の関数を定義する
  2. function callingの利用を定義する
  3. 質問文を投げる
  4. functionを利用するかを自動で判定する
  5. 利用する場合、1で定義したpropatiesを引数にセットして検索を実施する
  6. 5を含んだクエリでGPTでレスポンスを生成

1. 特定の関数を定義する

例えば以下のように、テストの点数を取得するような関数を定義する。
これは本来なら、SQLから作ったり、llamaindexで特定のテキストをchunkごとに突っ込んどいて質問文との距離を取ったりするために使うのが良いと思う。例なので適当。

def get_test_score(test_kind, person):
    test_score_info = {
        "test_kind": test_kind,
        "person": person,
        "test_score": "国語は60点、算数は85点、社会は50点、理科は90点、英語は30点です。"
    }
    return json.dumps(test_score_info)

2. function callingの利用を定義する

以下のような感じ。
nameに1で作成した関数名を、propatiesに1で作成した関数の引数となる項目をセットする。
今回の例は、参考にしたページのスケジュール取得関数にテストの点数を取得する関数を追加して、リストの2番目に追加している。

functions = [
        {
            "name": "get_schedule",
            "description": "特定の日付のスケジュールを取得して返す",
            "parameters": {
                "type": "object",
                "properties": {
                    "date": {
                        "type": "string",
                        "description": "日付"
                    },
                    "person": {
                        "type": "string",
                        "description": "人の名前"
                    },
                },
                "required": ["date","person"]
            }
        },
        {
            "name": "get_test_score",
            "description": "国語・算数・社会・理科・英語のテストの成績を取得して返す",
            "parameters": {
                "type": "object",
                "properties": {
                    "test_kind": {
                        "type": "string",
                        "description": "科目"
                    },
                    "person": {
                        "type": "string",
                        "description": "人の名前"
                    },
                },
                "required": ["test_kind", "person"]
            }
        }
    ]

3. 質問文を投げる

4. functionを利用するかを自動で判定する

テキストを投げる際は以下のように投げる。
投げる際、openai.ChatCompletion.createの引数に、2で定義したfunctionsをセットする。
また、function_callにautoを指定することで、内部で勝手にどのfunctionを使うかを判定してくれる。
(この辺のぬるっとやってくれる感が絶妙にキモいんだよなぁ。。。褒め言葉)

prompt = '田中さんの英語のテストの点数を教えて'
messages = [{"role": "user", "content": prompt}]
response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=messages,
        functions=functions,
        function_call="auto",  # auto: 独自関数を利用するかどうかをGPTが自動で選択。
    )

5. 利用する場合、1で定義したpropatiesを引数にセットして検索を実施する

4のresponseの中から、function_callするかどうかの結果を取得し、使う判定になっていたら2で定義したpropatiesを取得して1の関数をコールする。

     response_message = response["choices"][0]["message"]

    if response_message.get("function_call"):  # function_call利用判定
        available_functions = {
            "get_schedule": get_schedule,
            "get_test_score": get_test_score,
        }
        function_name = response_message["function_call"]["name"] # 利用する関数名の取得
        fuction_to_call = available_functions[function_name] # 利用する関数オブジェクトをfunction_to_call にセット
        function_args = json.loads(response_message["function_call"]["arguments"])
        if function_name == 'get_schedule':
            function_response = fuction_to_call(
                date=function_args.get("date"),
                person=function_args.get("person")
            )
        elif function_name == 'get_test_score':
            function_response = fuction_to_call(
                test_kind=function_args.get("test_kind"),
                person=function_args.get("person")
            )

        # 独自関数のレスポンスを渡す
        messages.append(response_message)
        messages.append(
            {
                "role": "function",
                "name": function_name,
                "content": function_response,
            }
        )

6. 5を含んだクエリでGPTでレスポンスを生成

second_response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo-0613",
            messages=messages,
        ) 

これで終了。

補足

入力:山田君は国語と算数どっちが得意?
出力:山田君は国語の得意です。国語のテストで60点を取りましたが、算数のテストでは85点を取りました。

うーん、、、うーーーーーーん
以上!