見出し画像

OpenAIのFunction Callingについて試してみた

2023/6/14にFunction callingなるものが出てきましたので、早速簡単に試してみました。

今年初めにgpt-3.5-turboとgpt-4をリリースし、わずか数ヶ月でこれらのモデルを基に開発者たちが素晴らしいアプリケーションを作り上げてきたことを嬉しく思います。

今日は、その続報として以下の興奮すべきアップデートをお知らせします:
- Chat Completions APIにおける新たな関数呼び出し機能
- gpt-4とgpt-3.5-turboの更新版とより操作可能なバージョン
- gpt-3.5-turboの新たな16kコンテキストバージョン(標準は4kバージョン)
- 最先端の埋め込みモデルのコストを75%削減
- gpt-3.5-turboの入力トークンのコストを25%削減
- gpt-3.5-turbo-0301とgpt-4-0314モデルの廃止タイムラインの発表


これら全てのモデルは、3月1日に導入した同じデータプライバシーとセキュリティ保証を持っています — 顧客は自分たちのリクエストから生成されたすべての出力を所有し、そのAPIデータは訓練に使用されません。

Function calling and other API updatesから一部抜粋


実装コード

Function callingチュートリアルの参考として、API Docsのこちらの部分を参考にしました。少しコードを変えて見やすくしています。

!pip install openai

import os
os.environ['OPENAI_API_KEY'] = "your-open-api-key"

import openai
import json

import openai
import json

def get_current_weather(location, unit="celsius"):
    """指定された場所の現在の天気を取得する"""
    weather_info = {
        "location": location,
        "temperature": "22",
        "unit": unit,
        "forecast": ["晴れ", "風"],
    }
    return json.dumps(weather_info, ensure_ascii=False)

def run_conversation(user_query):
    # リクエストのログ
    print(f"ChatGPTにユーザーメッセージを送信中: {user_query}\n")

    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=[{"role": "user", "content": user_query}],
        functions=[
            {
                "name": "get_current_weather",
                "description": "指定された場所の現在の天気を取得する",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "都市と都道府県、例えば 東京都",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                    },
                    "required": ["location"],
                },
            }
        ],
        function_call="auto",
    )

    # レスポンスのログ
    print(f"ChatGPTからのレスポンスを受信: {response}\n")

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

    if message.get("function_call"):
        function_name = message["function_call"]["name"]
        args = json.loads(message["function_call"]["arguments"])
        function_response = get_current_weather(
            location=args.get("location"),
            unit=args.get("unit"),
        )

        second_response = openai.ChatCompletion.create(
            model="gpt-3.5-turbo-0613",
            messages=[
                {"role": "user", "content": user_query},
                message,
                {
                    "role": "function",
                    "name": function_name,
                    "content": function_response,
                },
            ],
        )

        # レスポンスのログ
        print(f"ChatGPTからの二回目のレスポンスを受信: {second_response}\n")

        return second_response

user_query = "東京の天気はどうですか?"
response = run_conversation(user_query)

# レスポンスメッセージの取得
response_message = response["choices"][0]["message"]["content"]
print(f"レスポンスメッセージ: {response_message}")
このコードは、ユーザーからの問い合わせに対して現在の天気情報を取得し、その結果をユーザーに返すという処理を行います。具体的には以下のようなステップで動作します。
  1. ユーザーからの問い合わせを受け取ります。

  2. 問い合わせをGPT-3.5-turboモデルに送信します。

  3. モデルからの応答を解析し、天気情報の取得が必要か判断します。

  4. 必要があれば天気情報を取得し、それを含む新たなメッセージをモデルに送信します。

  5. モデルからの最終的な応答をユーザーに返します。

  • get_current_weather関数:

    • 指定された場所と単位(デフォルトは摂氏)に基づいて天気情報を作成します。

    • 作成した天気情報をJSON形式で返します。

  • run_conversation関数:

    • ユーザーからの問い合わせを受け取り、それをGPT-3.5-turboモデルに送信します。

    • モデルからの応答を解析し、天気情報の取得が必要かどうかを判断します。

    • 必要があれば、get_current_weather関数を呼び出して天気情報を取得します。

    • 取得した天気情報を含む新たなメッセージをモデルに送信します。

    • モデルからの最終的な応答を返します。


以下、出力結果です。

ChatGPTにユーザーメッセージを送信中: 東京の天気はどうですか?

ChatGPTからのレスポンスを受信: { "id": "chatcmpl-7R9ArarztLRclro8rr9J7DehZZfu0", "object": "chat.completion", "created": 1686704477, "model": "gpt-3.5-turbo-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": null, "function_call": { "name": "get_current_weather", "arguments": "{\n \"location\": \"\u6771\u4eac\u90fd\"\n}" } }, "finish_reason": "function_call" } ], "usage": { "prompt_tokens": 102, "completion_tokens": 19, "total_tokens": 121 } }

ChatGPTからの二回目のレスポンスを受信: { "id": "chatcmpl-7R9ArUs4tRD4SoBL2qr9vkG5RM9V9", "object": "chat.completion", "created": 1686704477, "model": "gpt-3.5-turbo-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": "\u6771\u4eac\u306e\u5929\u6c17\u306f\u6674\u308c\u3067\u3001\u6c17\u6e29\u306f22\u5ea6\u3067\u3059\u3002\u98a8\u3082\u5439\u3044\u3066\u3044\u307e\u3059\u3002" }, "finish_reason": "stop" } ], "usage": { "prompt_tokens": 75, "completion_tokens": 29, "total_tokens": 104 } }

レスポンスメッセージ: 東京の天気は晴れで、気温は22度です。風も吹いています。

出力結果


分かったこと

  • ツールの扱いについては、既存の手法のメンタルモデルと似てる

    • ユーザーからのメッセージに対して、関数を使うべきかという判断をchagpt API側で行える。

    • また利用できるツール(関数)はシステムメッセージとして事前に伝えることで拡張が可能。

  • 関数を扱うと判定した場合のレスポンスについて

    • 従来の `response["choices"][0]["message"]["content"]` は null として扱われる

    • その代わり、以下のような形で function_call に name, arguments が格納されて返却される(locationはUnicodeエスケープされた文字列で、デコードすると"東京都"です)

{ "id": "chatcmpl-7R9ArarztLRclro8rr9J7DehZZfu0", "object": "chat.completion", "created": 1686704477, "model": "gpt-3.5-turbo-0613", "choices": [ { "index": 0, "message": { "role": "assistant", "content": null, "function_call": { "name": "get_current_weather", "arguments": "{\n \"location\": \"\u6771\u4eac\u90fd\"\n}" } }, "finish_reason": "function_call" } ], "usage": { "prompt_tokens": 102, "completion_tokens": 19, "total_tokens": 121 } }

function_callの場合のレスポンス
  • 引数には型指定をすることができ、ここで生成するデータの(ある程度)強制することができそう

    • parameters.propertiesで引数名・型指定・詳細を指示している。またenumも使えるので、二値分類(yes/no)や多値分類タスクも扱うことができそう

    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo-0613",
        messages=[{"role": "user", "content": user_query}],
        functions=[
            {
                "name": "get_current_weather",
                "description": "指定された場所の現在の天気を取得する",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {
                            "type": "string",
                            "description": "都市と都道府県、例えば 東京都",
                        },
                        "unit": {"type": "string", "enum": ["celsius", "fahrenheit"]},
                    },
                    "required": ["location"],
                },
            }
        ],
        function_call="auto",
    )


おわりに

結構エージェント的な振る舞いではあるものの、生成するデータを指定することができるので、活用の幅は広いなと感じました。

以下のnotebookや、openai.ChatCompletion.createの中身を見たりして、もう少し理解を深めていこうと思います。


この記事が気に入ったらサポートをしてみませんか?