メインコンテンツへスキップ
見出し画像

Google Colab で OpenAI API の Function Calling を試す

    「Google Colab」で「OpenAI API」の「Assistant API」の「Function Calling」を試したので、まとめました。

    前回

    1. Function Calling

    「Assistant API」は、さまざまなタスクを実行できる強力な「AIアシスタント」を作成するためのAPIです。

    「Assistant API」は現在、次の3つのツールをサポートしています。

    ・Code Interpreter : Pythonコードを作成して実行
    ・Retrieval : モデル外部からの知識を取得
    ・Function Calling : 関数のレスポンスを取得

    今回は、「Function Calling」を使います。「Chat Completions API」と同様に、「Assistant API」は「Function Calling」をサポートしています。「Function Calling」は、アシスタントに関数定義を指定することで、呼び出す必要がある場合に、関数呼び出しの要求を返すことができます。関数呼び出しの要求時には「Run」は一時停止され、関数呼び出し結果を提供することで「Run」は続行されます。

    2. セットアップ

    Colabでのセットアップ手順は、次のとおりです。

    (1) パッケージのインストール。

    # パッケージのインストール
    !pip install openai

    (2) 環境変数の準備。
    以下のコードの <OpenAI_APIキー> にはOpenAIのサイトで取得できるAPIキーを指定します。(有料)

    import os
    os.environ["OPENAI_API_KEY"] = "<OpenAI_APIキー>"

    (3) クライアントの準備。
    「クライアント」は「OpenAI API」にアクセスするインタフェースになります。

    from openai import OpenAI
    
    # クライアントの準備
    client = OpenAI()

    4. アシスタントの実行

    アシスタントの実行手順は、次のとおりです。

    (1) アシスタントの作成。
    toolsに「function」と関数の定義を指定します。

    # アシスタントの作成
    assistant = client.beta.assistants.create(
        instructions="あなたはお天気ボットです。 提供されている関数を使用して質問に答えます。",
        model="gpt-4-1106-preview",
        tools=[{
            "type": "function",
            "function": {
                "name": "getCurrentTemperature",
                "description": "特定の場所の気温を取得",
                "parameters": {
                    "type": "object",
                    "properties": {
                        "location": {"type": "string", "description": "場所 (例:東京)"},
                        "unit": {"type": "string", "enum": ["c", "f"]}
                    },
                    "required": ["location"]
                }
            }
        }]
    )

    (2) スレッドの作成。
    会話するためスレッドを作成します。

    # スレッドの準備
    thread = client.beta.threads.create()

    (3) ユーザーメッセージの追加。

    # ユーザーメッセージの追加
    message = client.beta.threads.messages.create(
        thread_id=thread.id,
        role="user",
        content="群馬県の気温を教えてください。"
    )

    (4) スレッドのメッセージリストの確認。
    ユーザーメッセージが1つ追加されています。

    # スレッドのメッセージリストの確認
    messages = client.beta.threads.messages.list(
        thread_id=thread.id,
        order="asc"
    )
    for message in messages:
        print(message.role, ":", message.content[0].text.value)
    user : 群馬県の気温を教えてください。

    (5) アシスタントにリクエスト。

    # アシスタントにリクエスト
    run = client.beta.threads.runs.create(
        thread_id=thread.id,
        assistant_id=assistant.id,
    )

    (6) アシスタントへのリクエストの実行状況の確認。
    アシスタントは、関数をトリガーするユーザーメッセージを受け取ると、ステータス「require_action」に移行します。

    # 実行状況の確認
    run = client.beta.threads.runs.retrieve(
        thread_id=thread.id,
        run_id=run.id
    )
    print(run.status)
    requires_action

    「require_action」になったら、10分以内に関数の出力を提出しないと期限切れ状態「expired」になります。

    (7) require_actionのパラメータの取得。
    今回は、ツールID、関数名、引数を取得します。

    import json
    
    # require_actionのパラメータの取得
    tool_id = run.required_action.submit_tool_outputs.tool_calls[0].id
    tool_function_name = run.required_action.submit_tool_outputs.tool_calls[0].function.name
    tool_function_arguments = json.loads(run.required_action.submit_tool_outputs.tool_calls[0].function.arguments)
    
    print("id:", tool_id)
    print("name:", tool_function_name)
    print("arguments:", tool_function_arguments)
    id: call_6avyFMrvuLtLTr1BzYmVL8p7
    name: getCurrentTemperature
    arguments: {'location': '群馬県', 'unit': 'c'}

    (8) パラメータをもとに関数を実行。
    今回は簡易化のため、実処理を行わないダミー関数を使ってます。

    # 特定の場所の気温を取得する関数 (ダミー)
    def getCurrentTemperature(location, unit):
        print("location:", location)
        print("unit:", unit)
        return "22度"
    
    # 特定の場所の気温を取得
    tool_function_output = getCurrentTemperature(
        tool_function_arguments["location"], 
        tool_function_arguments["unit"]
    )

    (9) 関数の出力を提出。

    # 関数の出力を提出
    run = client.beta.threads.runs.submit_tool_outputs(
        thread_id=thread.id,
        run_id=run.id,
        tool_outputs=[
            {
                "tool_call_id": tool_id,
                "output": tool_function_output,
            }
        ]
    )

    (10) スレッドのメッセージリストの確認。
    ステータス「completed」の場合は、メッセージリストにアシスタントメッセージが追加されています。

    # スレッドのメッセージリストの確認
    messages = client.beta.threads.messages.list(
        thread_id=thread.id,
        order="asc"
    )
    for message in messages:
        print(message.role, ":", message.content[0].text.value)
    user : 群馬県の気温を教えてください。
    assistant : 群馬県の気温は22度です。

    5. Assistant Playground

    作成した「アシスタント」は、「Assistant Playground」でも動作確認できます。関数のコールバックを入力するUIも提供しています。

    画像

    【おまけ】 JSONモード と Function Calling の違い

    「JSONモード」と「Function Calling」の違いは、「JSONモード」が JSON形式のみ検証しているのに対し、「Function Calling」のJSON形式を検証かつスキーマの準拠 (関数定義にあわせる) を強制しています。

    次回



     
     

    npaka

     
     
    プログラマー。iPhone / Android / Unity / ROS / AI / AR / VR / RasPi / ロボット / ガジェット。年2冊ペースで技術書を執筆。アニソン / カラオケ / ギター / 猫 twitter : @npaka123

    あなたへのおすすめ