😸

[ツール編]OpenAI GPTsのAPI版、AssistantsAPIの仕様を読んでいく

2024/01/06に公開

はじめに

概要、アシスタントの仕組み、と見ていったので、最後となるツールの章を見ていきたいと思います。

https://platform.openai.com/docs/assistants/tools/code-interpreter

前回の記事はこちら
https://zenn.dev/kat/articles/ee0d8a7679963a

構成

ツールのページは、4つの構成に分かれています。

  • コードインタープリタ
  • 知識の検索
  • 関数呼び出し
  • サポートされているファイル

ツール

アシスタントAPIにおけるツールとは、従来ユーザの指示に対して言語モデルが回答を生成する機能だけであることに追加して、処理をさせることが出来る機能です。
OpenAI が提供しているツールの使用には追加料金がかかります。
これらのツールの価格設定の詳細については、ヘルプセンターの記事をご覧ください。
とありますが、結論的には、ツールのページに書いてある内容以上の詳細情報はありませんでした。
むしろ、ツールのページの方が詳細書いているまである。

https://help.openai.com/en/articles/8550641-assistants-api

コードインタープリタ(Code Interpreter)

コードインタープリターを使用すると、アシスタントAPI がサンドボックス実行環境で Python コードを作成して実行できるようになります。
このツールは、さまざまなデータと形式を含むファイルを処理し、データとグラフの画像を含むファイルを生成できます。コード インタープリターを使用すると、アシスタントはコードを繰り返し実行して、難しいコードや数学の問題を解決できます。アシスタントは、実行に失敗したコードを作成した場合、コードの実行が成功するまで別のコードの実行を試みることによって、このコードを反復処理できます。

コードインタープリターの料金

  • セッションあたり 0.03 ドルです。
  • 各セッションは、デフォルトで 1時間アクティブになります。
  • セッションはスレッド毎に別々。
    アシスタントが 2つの異なるスレッド (エンドユーザーごとに 1つのスレッドなど) で同時にコードインタープリターを呼び出す場合、2つのコードインタープリターセッションが作成されます。
  • 同一スレッドでも1時間でセッションが切れるため、1スレッドあたりで2セッション以上もあり得る。
  • ユーザーが同じスレッドでコードインタープリターを1時間操作した場合、料金は1セッション分。

以下のように、使用していない日は課金されないというのと、
セッション単位で課金されるため、0.03ドル × 3セッションで0.09ドルになっています。

コードインタープリタの有効化

Assistantオブジェクトのパラメータtoolscode_interpreterを渡すことで、コードインタープリタが有効になります。

curl https://api.openai.com/v1/assistants \
  -u :$OPENAI_API_KEY \
  -H 'Content-Type: application/json' \
  -H 'OpenAI-Beta: assistants=v1' \
  -d '{
    "instructions": "You are a personal math tutor. When asked a math question, write and run code to answer the question.",
    "tools": [
      { "type": "code_interpreter" }
    ],
    "model": "gpt-4-1106-preview"
  }'

次にモデルは、ユーザーリクエストの性質に基づいて、実行時にコードインタープリターをいつ呼び出すかを決定します。
この動作は、アシスタントのプロンプトに指示する事によって促進できます。
(「この問題を解決するコードを書いてください」など)
※あくまでツールを使用するのはモデルの判断によるけれど、ユーザの指示があれば、コードインタープリターをより使用してくれる可能性が高まるということですね。
この辺は実際触った感覚と同様です。

ファイルをコードインタープリターに渡す

コードインタープリターはファイルからデータを解析できます。
これは、大量のデータをアシスタントに提供する場合、またはユーザーが分析のために独自のファイルをアップロードできるようにする場合に便利です。
コードインタープリター用にアップロードされたファイルには、検索用のインデックスが作成されないことに注意してください。
※検索用のファイルのインデックス作成の詳細については、以下の「ナレッジの検索」セクションを参照

アシスタントレベルで渡されるファイルは、このアシスタントを使用するすべての実行によってアクセスできます。

# Upload a file with an "assistants" purpose
curl https://api.openai.com/v1/files \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -F purpose="assistants" \
  -F file="@/path/to/mydata.csv"

# Create an assistant using the file ID
curl https://api.openai.com/v1/assistants \
  -u :$OPENAI_API_KEY \
  -H 'Content-Type: application/json' \
  -H 'OpenAI-Beta: assistants=v1' \
  -d '{
    "instructions": "You are a personal math tutor. When asked a math question, write and run code to answer the question.",
    "tools": [{"type": "code_interpreter"}],
    "model": "gpt-4-1106-preview",
    "file_ids": ["file_123abc456"]
  }'

レスポンス

{
  "id": "asst_rRYWX1QsJPvR2mIKC4Uf0WoW",
  "object": "assistant",
  "created_at": 1703293867,
  "name": null,
  "description": null,
  "model": "gpt-4-1106-preview",
  "instructions": "You are a personal math tutor. When asked a math question, write and run code to answer the question.",
  "tools": [
    {
      "type": "code_interpreter"
    }
  ],
  "file_ids": [
    "file-EHaW1HlXihGdQfdVqSwAAQsj"
  ],
  "metadata": {}
}
curl https://api.openai.com/v1/threads/thread_abc123/messages \
  -u :$OPENAI_API_KEY \
  -H 'Content-Type: application/json' \
  -H 'OpenAI-Beta: assistants=v1' \
  -d '{
    "role": "user",
    "content": "I need to solve the equation `3x + 11 = 14`. Can you help me?",
    "file_ids": ["file_123abc456"]
  }'
  • アシスタントに対してファイルを渡せる。
  • アシスタントに渡したファイルは、コードインタープリターで参照可能。
  • アシスタントに渡したファイルは、アシスタントのどの実行でも参照可能。
  • コードインタープリターで添付されたファイルについては料金は発生しない。
  • 検索ツールが取得してきたファイルについては料金が発生する。
  • ファイルはスレッド単位でも渡せる。この場合は渡したスレッドでしかファイルのアクセスはできない。
  • ファイルの最大サイズは 512MB
  • コードインタープリターは、.csv.pdf.jsonなど、さまざまなファイル形式をサポート
    ※サポートされるファイル拡張子 (および対応する MIME タイプ) の詳細については、以下の「サポートされるファイル」セクションを参照

コードインタープリターによって生成された画像とファイルの読み取り

APIのコードインタープリターは、図式の画像の生成や、CSVやPDFなどのファイル出力も行います。
生成されるファイルには次の 2種類があります。

  1. 画像
  2. データファイル (例:csvアシスタントによって生成されたデータを含むファイル)
    file_idコードインタープリターがイメージを生成するとき、アシスタントメッセージ応答のフィールドでこのファイルを検索してダウンロードできます。
{
    "id": "msg_abc123",
    "object": "thread.message",
    "created_at": 1698964262,
    "thread_id": "thread_abc123",
    "role": "assistant",
    "content": [
    {
      "type": "image_file",
      "image_file": {
        "file_id": "file-abc123"
      }
    }
  ]
  # ...
}

ファイルID をFilesAPIに渡すことで、ファイルコンテンツをダウンロードできます。
アシスタント作成時にアシスタントに追加したファイルはダウンロード出来ないようでした。

  1. プレイグランドでアシスタントと会話してファイルを作成。

  2. 作成したファイルを確認
    やり取りした会話のパスをクリックするとファイルの詳細ページに移動します。
    ファイルIDが振られているのでファイルは作成されているようです。

※蛇足気味ですが、スレッドで作成したファイルはファイル一覧からは参照できませんが、スレッドの一覧やスレッドのメッセージ詳細から参照可能。
プレイグランドのやり取りはページを更新すると消えてしまうので、改めて確認したい場合はAPI経由で確認するしかなさそうです。

curl https://api.openai.com/v1/threads/thread_vQQFfpiJiUedUiKzMKcEJO3Q/messages \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1"

{
  "object": "list",
  "data": [
    {
      "id": "msg_76FwFsm34RIbwc6lF2VL3AgO",
      "object": "thread.message",
      "created_at": 1703918983,
      "thread_id": "thread_vQQFfpiJiUedUiKzMKcEJO3Q",
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": {
            "value": "関東地方の都道府県をリスト化したCSVファイルを作成しました。以下のリンクからダウンロードできます:\n\n[関東地方の都道府県 CSVファイルをダウンロード](sandbox:/mnt/data/kanto_prefectures.csv)",
            "annotations": [
              {
                "type": "file_path",
                "text": "sandbox:/mnt/data/kanto_prefectures.csv",
                "start_index": 79,
                "end_index": 118,
                "file_path": {
                  "file_id": "file-PLrFUYMh1VQSTkWQHH4PGltH"
                }
              }
            ]
          }
        }
      ],
      "file_ids": [
        "file-PLrFUYMh1VQSTkWQHH4PGltH"
      ],
      "assistant_id": "asst_TUkrMHYAlwoGEWOAMTU1G2Ez",
      "run_id": "run_HqUzHACgjSYZf7B7WHxFjFHd",
      "metadata": {}
    },
    … 以下略

コードインタープリターがファイルを参照する場合、ファイルパスはannotationsにリストされます。

  1. スレッドで作成したファイルをダウンロード
curl https://api.openai.com/v1/files/file-PLrFUYMh1VQSTkWQHH4PGltH/content \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  --output prefecture.csv

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100    94    0    94    0     0    229      0 --:--:-- --:--:-- --:--:--   229

cat prefecture.csv 
都道府県
東京都
神奈川県
埼玉県
千葉県
茨城県
栃木県
群馬県

アシスタント外で作成したファイルをダウンロードしようとした場合、エラーです。

curl https://api.openai.com/v1/files/file-EHaW1HlXihGdQfdVqSwAAQsj/content \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  --output mydata.csv

  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100   165  100   165    0     0    495      0 --:--:-- --:--:-- --:--:--   494

cat mydata.csv 
{
  "error": {
    "message": "Not allowed to download files of purpose: assistants",
    "type": "invalid_request_error",
    "param": null,
    "code": null
  }
}

コードインタープリタの入出力ログ

stepsAPIを呼び出すことで、コードインタープリターを呼び出した実行ステップの情報を確認できます。
実行ステップの中には、コードインタープリターのinputとoutputsログを確認することができます。
inputには実行したソースコード、outputにはコードの実行によって出力されたログ(ファイル)が出力されます。

curl https://api.openai.com/v1/threads/{スレッドID}/runs/{実行ID}/steps \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1" \
{
  "object": "list",
  "data": [
    {
      "id": "step_fZV62VZR3oJts3eL5g1YeWO0",
      "object": "thread.run.step",
      "created_at": 1703918983,
      "run_id": "run_HqUzHACgjSYZf7B7WHxFjFHd",
      "assistant_id": "asst_TUkrMHYAlwoGEWOAMTU1G2Ez",
      "thread_id": "thread_vQQFfpiJiUedUiKzMKcEJO3Q",
      "type": "message_creation",
      "status": "completed",
      "cancelled_at": null,
      "completed_at": 1703918987,
      "expires_at": null,
      "failed_at": null,
      "last_error": null,
      "step_details": {
        "type": "message_creation",
        "message_creation": {
          "message_id": "msg_76FwFsm34RIbwc6lF2VL3AgO"
        }
      }
    },
    {
      "id": "step_WiiCOqqw6l2G3OSV7yWjQeHd",
      "object": "thread.run.step",
      "created_at": 1703918967,
      "run_id": "run_HqUzHACgjSYZf7B7WHxFjFHd",
      "assistant_id": "asst_TUkrMHYAlwoGEWOAMTU1G2Ez",
      "thread_id": "thread_vQQFfpiJiUedUiKzMKcEJO3Q",
      "type": "tool_calls",
      "status": "completed",
      "cancelled_at": null,
      "completed_at": 1703918983,
      "expires_at": null,
      "failed_at": null,
      "last_error": null,
      "step_details": {
        "type": "tool_calls",
        "tool_calls": [
          {
            "id": "call_zW14v791RIHBYyfBD9LMe0r1",
            "type": "code_interpreter",
            "code_interpreter": {
              "input": "import csv\n\n# 関東地方の都道府県のリスト\nkanto_prefectures = [\"東京都\", \"神奈川県\", \"埼玉県\", \"千葉県\", \"茨城県\", \"栃木県\", \"群馬県\"]\n\n# ファイルのパス\nfile_path = '/mnt/data/kanto_prefectures.csv'\n\n# CSVファイルに保存\nwith open(file_path, mode='w', newline='', encoding='utf-8') as file:\n    writer = csv.writer(file)\n    writer.writerow([\"都道府県\"])  # ヘッダー\n    for prefecture in kanto_prefectures:\n        writer.writerow([prefecture])\n\nfile_path",
              "outputs": [
                {
                  "type": "logs",
                  "logs": "'/mnt/data/kanto_prefectures.csv'"
                }
              ]
            }
          }
        ]
      }
    }
  ],
  "first_id": "step_fZV62VZR3oJts3eL5g1YeWO0",
  "last_id": "step_WiiCOqqw6l2G3OSV7yWjQeHd",
  "has_more": false
}

inputの部分にコードインタープリターが生成したpythonコードが含まれています。

import csv

# 関東地方の都道府県のリスト
kanto_prefectures = ["東京都", "神奈川県", "埼玉県", "千葉県", "茨城県", "栃木県", "群馬県"]

# ファイルのパス
file_path = '/mnt/data/kanto_prefectures.csv'

# CSVファイルに保存
with open(file_path, mode='w', newline='', encoding='utf-8') as file:
    writer = csv.writer(file)
    writer.writerow(["都道府県"])  # ヘッダー
    for prefecture in kanto_prefectures:
        writer.writerow([prefecture])

file_path

最後の行のfile_pathが謎ですが、csvファイル作成するコードになっています。

知識の検索機能(Knowledge Retrieval)

検索機能により、独自の製品情報やユーザーから提供されたドキュメントなど、モデルの外部からの知識でアシスタントが強化されます。
アップロードしたファイルをアシスタントに追加すると、OpenAIは自動的にドキュメントをチャンク化し、埋め込みのインデックスを作成して保存し、ユーザーのクエリに答えるために関連するコンテンツを取得するベクトル検索を実装します。

検索機能の有効化

アシスタントのパラメータtoolsretrievalを渡すことで、検索機能が有効になります。

curl https://api.openai.com/v1/assistants \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1" \
  -d '{
    "instructions": "You are a customer support chatbot. Use your knowledge base to best respond to customer queries.",
    "tools": [{"type": "retrieval"}],
    "model": "gpt-4-1106-preview"
  }'

検索機能の料金

  • 添付されたすべてのファイルに自動的にインデックスが作成されます
  • アシスタントごとに 1日あたり 0.20 ドル/GB が課金されます。
  • Modify Assistantエンドポイントを使用して、検索機能を有効/無効にできます。

※上の解説見て、あまり使用していないアシスタントにファイル追加したままだと料金みに1日当たり0.20 ドル/GBについてですが、ファイルを追加したアシスタントAPIだと毎日課金されるのかと思われそうですが、実際の料金見た感じ、使用した日のみの課金のような気がします。

使い方

次にモデルは、ユーザーのメッセージに基づいてコンテンツをいつ取得するかを決定します。
アシスタントAPI は、次の2つの取得手法から自動的に選択します。

  1. 短いドキュメントのプロンプトでファイルのコンテンツを渡す
  2. 長いドキュメントに対してベクトル検索を実行

とありますが、正直何を意味しているのか不明でした。
意味が分かったら加筆修正するかもしれません。

検索機能のためにファイルをアップロードする

コードインタープリターと同様に、ファイルはアシスタントに追加したり、個別のメッセージに追加することができます。

以下はアシスタントに追加する方法

# ファイルアップロード
curl https://api.openai.com/v1/files \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -F purpose="assistants" \
  -F file="@/var/www/html/README.md"

{
  "object": "file",
  "id": "file-AvvL7CYVvDIhuB3lfyAcWTk8",
  "purpose": "assistants",
  "filename": "README.md",
  "bytes": 8295,
  "created_at": 1704010979,
  "status": "processed",
  "status_details": null
}

# アシスタント作成(検索機能の有効化とファイルを追加)
curl https://api.openai.com/v1/assistants \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1" \
  -d '{
    "instructions": "You are a customer support chatbot. Use your knowledge base to best respond to customer queries.",
    "name": "Math Tutor",
    "tools": [{"type": "retrieval"}],
    "model": "gpt-4-1106-preview",
    "file_ids": ["file-AvvL7CYVvDIhuB3lfyAcWTk8"]
  }'

{
  "id": "asst_OYM33iMGUUaMpJOB45jpm6TB",
  "object": "assistant",
  "created_at": 1704010995,
  "name": "Math Tutor",
  "description": null,
  "model": "gpt-4-1106-preview",
  "instructions": "You are a customer support chatbot. Use your knowledge base to best respond to customer queries.",
  "tools": [
    {
      "type": "retrieval"
    }
  ],
  "file_ids": [
    "file-AvvL7CYVvDIhuB3lfyAcWTk8"
  ],
  "metadata": {}
}

メッセージにファイルを追加する方法

curl https://api.openai.com/v1/threads/{スレッドID}/messages \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1" \
  -d '{
    "role": "user",
    "content": "I can not find in the PDF manual how to turn off this device.",
    "file_ids": ["{ファイルID}"]
  }'

検索機能にアップロードするファイルの制限

  • 最大ファイル サイズは 512 MB
  • 最大トークンサイズは2,000,000 トークン(ファイルを添付するときに自動的に計算されます)
  • .pdf.md.docxなど、さまざまなファイル形式をサポート
    ※サポートされるファイル拡張子 (および対応する MIME タイプ) の詳細については、以下の「サポートされるファイル」セクションを参照。

検索機能の料金

  • アシスタントあたり 1日 0.20 ドル/GB。
  • 検索機能が有効にし、ファイルを添付したアシスタントを使用すると、アシスタントごとに上記の1日あたりの料金が発生。
  • つまり、同じファイルだとしても別々なアシスタントに添付している場合、アシスタント毎に料金が発生します。
  • この料金は、アシスタントを使用してメッセージを何度やり取りしても変わりません。
  • 別な例として、検索を有効にしたアシスタントで、各メッセージのやり取りに別々なファイルを添付して実行すると、(アシスタントに添付されたファイルに加えて) 添付したファイルすべてに対して 1日あたりの料金が発生します。

※ドキュメントを解釈すると上記の通りではあるのですが、実際使用して料金を確認してみても0.20ドルの課金は確認できませんでした。

ファイルの削除

アシスタントからファイルを削除するには、アシスタントからファイルを切り離します。

curl https://api.openai.com/v1/assistants/{アシスタントID}/files/{ファイルID} \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1" \
  -X DELETE

{
  "id": "file-jadRHccSZhmFpCIQcxgqkfgV",
  "object": "assistant.file.deleted",
  "deleted": true
}

アシスタントからファイルを切り離すと、そのファイルは検索インデックスから削除され、インデックス付きファイルのストレージ料金は請求されなくなります。

関数呼び出し(Function Calling)

ChatCompletionsAPIと同様に、アシスタントAPIは関数呼び出しをサポートしています。
関数呼び出しを使用すると、アシスタントに関数を記述し、呼び出す必要がある関数を引数とともにインテリジェントに返すことができます。
アシスタントAPIは、実行中に関数を呼び出すときに実行を一時停止します。
関数コールバックの結果を提供して、実行の実行を続行できます。

関数の定義

まず、アシスタントを作成するときに関数を定義します。
functionの部分です。

curl https://api.openai.com/v1/assistants \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1" \
  -d '{
    "instructions": "You are a weather bot. Use the provided functions to answer questions.",
    "tools": [{
      "type": "function",
      "function": {
        "name": "getCurrentWeather",
        "description": "Get the weather in location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"},
            "unit": {"type": "string", "enum": ["c", "f"]}
          },
          "required": ["location"]
        }
      }	
    },
    {
      "type": "function",
      "function": {
        "name": "getNickname",
        "description": "Get the nickname of a city",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {"type": "string", "description": "The city and state e.g. San Francisco, CA"}
          },
          "required": ["location"]
        }
      }	
    }],
    "model": "gpt-4-1106-preview"
  }'

ここでは、getCurrentWeathergetNicknameという2つの関数をアシスタントに定義しています。

アシスタントによって呼び出される関数の読み取り

関数をトリガーするユーザーメッセージで実行を開始すると、実行はpending状態になります。
処理が完了すると、実行状態を取得するAPIのレスポンスのrequires_actionで確認できます。
このモデルは、並列関数呼び出しを使用して一度に呼び出す複数の関数を提供できます。

説明とレスポンスの例を見た段階ではさっぱりでしたが、実際に操作してみてある程度雰囲気がつかめました。

  1. アシスタントを作成(この時点では作成済み)
  2. スレッドの開始
  3. 実行の開始(=関数が実行されるようなメッセージを送信)
  4. 関数が実行中となり、追加アクションが求められる。実行は関数の結果を待つため一時停止
  5. 関数に対して出力したい結果を渡す。
  6. 関数が結果を返し、実行が再開され、関数の結果をアシスタントの返答として出力。

アシスタントは作成済みなので、スレッド開始から始めます。

2. スレッドの開始 と 3.実行の開始

スレッド作成と実行を開始は別々なAPIとして提供されていますが、
スレッド開始と同時に実行を開始するAPIもあります。

curl https://api.openai.com/v1/threads/runs \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "Content-Type: application/json" \
  -H "OpenAI-Beta: assistants=v1" \
  -d '{
      "assistant_id": "asst_DD8uOvrJ0PyNPU0AvM5x4jfj",
      "thread": {
        "messages": [
          {"role": "user", "content": "横浜市の天気は?横浜市のニックネームは?"}
        ]
      }
    }'

レスポンス

{
  "id": "run_tyJrdU1WYzFAvNou640RJwaE",
  "object": "thread.run",
  "created_at": 1704513363,
  "assistant_id": "asst_DD8uOvrJ0PyNPU0AvM5x4jfj",
  "thread_id": "thread_BJx7VBxHG4uOU3e27Sax3jgM",
  "status": "queued",
  "started_at": null,
  "expires_at": 1704513963,
  "cancelled_at": null,
  "failed_at": null,
  "completed_at": null,
  "last_error": null,
  "model": "gpt-4-1106-preview",
  "instructions": "You are a weather bot. Use the provided functions to answer questions.",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "getCurrentWeather",
        "description": "Get the weather in location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The city and state e.g. San Francisco, CA"
            },
            "unit": {
              "type": "string",
              "enum": [
                "c",
                "f"
              ]
            }
          },
          "required": [
            "location"
          ]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "getNickname",
        "description": "Get the nickname of a city",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The city and state e.g. San Francisco, CA"
            }
          },
          "required": [
            "location"
          ]
        }
      }
    }
  ],
  "file_ids": [],
  "metadata": {}
}

4. 実行状態の確認

スレッドと実行の開始によって、スレッドIDと実行IDが発行されるので、それを用いて確認します。

curl https://api.openai.com/v1/threads/thread_BJx7VBxHG4uOU3e27Sax3jgM/runs/run_tyJrdU1WYzFAvNou640RJwaE \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1"

レスポンス

{
  "id": "run_scPYDkvkXs5rW4am7LbCHsrK",
  "object": "thread.run",
  "created_at": 1704514456,
  "assistant_id": "asst_DD8uOvrJ0PyNPU0AvM5x4jfj",
  "thread_id": "thread_L0vLoQnz0miavyGFZh6R1Sia",
  "status": "requires_action",
  "started_at": 1704514456,
  "expires_at": 1704515056,
  "cancelled_at": null,
  "failed_at": null,
  "completed_at": null,
  "required_action": {
    "type": "submit_tool_outputs",
    "submit_tool_outputs": {
      "tool_calls": [
        {
          "id": "call_SOMluLRjthL04pNVThCXMygM",
          "type": "function",
          "function": {
            "name": "getCurrentWeather",
            "arguments": "{\"location\": \"Yokohama, Japan\"}"
          }
        },
        {
          "id": "call_2QQlQJYhJBZCJhG4vX3u639B",
          "type": "function",
          "function": {
            "name": "getNickname",
            "arguments": "{\"location\": \"Yokohama, Japan\"}"
          }
        }
      ]
    }
  },
  "last_error": null,
  "model": "gpt-4-1106-preview",
  "instructions": "You are a weather bot. Use the provided functions to answer questions.",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "getCurrentWeather",
        "description": "Get the weather in location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The city and state e.g. San Francisco, CA"
            },
            "unit": {
              "type": "string",
              "enum": [
                "c",
                "f"
              ]
            }
          },
          "required": [
            "location"
          ]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "getNickname",
        "description": "Get the nickname of a city",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The city and state e.g. San Francisco, CA"
            }
          },
          "required": [
            "location"
          ]
        }
      }
    }
  ],
  "file_ids": [],
  "metadata": {}
}

required_actionsubmit_tool_outputsに関数出力の送信を待っている関数がリスト化されます。
call_で始まるidが大事で、関数出力の送信時に出力対象の関数を指定するために使用します。
``が関数に渡される引数を表しており、これはユーザが送ったメッセージから自動的に設定されるようです。
日本語メッセージの内容から使用すべき関数と引数となる横浜市を抜き出しています。
Yokohama, Japanとなっていることから、GPT内で変換されているのが分かります。

横浜市の天気は?とだけメッセージを送ると、getCurrentWeatherの関数だけが呼び出され、getNicknameの関数は実行されません。

関数出力の送信

submit_tool_outputsAPIを使用することで関数が返却する出力を指定できます。
tool_call_idに先ほど実行状態の確認で得たcall_で始まるidを設定します。

curl https://api.openai.com/v1/threads/thread_L0vLoQnz0miavyGFZh6R1Sia/runs/run_scPYDkvkXs5rW4am7LbCHsrK/submit_tool_outputs \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1" \
  -d '{
    "tool_outputs": [{
      "tool_call_id": "call_SOMluLRjthL04pNVThCXMygM",
      "output": "{\"temperature\": \"22\", \"unit\": \"celsius\"}"
    }, {
      "tool_call_id": "call_2QQlQJYhJBZCJhG4vX3u639B",
      "output": "{\"nickname\": \"はま\"}"
    }]
  }'

レスポンス

レスポンス
{
  "id": "run_scPYDkvkXs5rW4am7LbCHsrK",
  "object": "thread.run",
  "created_at": 1704514456,
  "assistant_id": "asst_DD8uOvrJ0PyNPU0AvM5x4jfj",
  "thread_id": "thread_L0vLoQnz0miavyGFZh6R1Sia",
  "status": "queued",
  "started_at": 1704514456,
  "expires_at": 1704515056,
  "cancelled_at": null,
  "failed_at": null,
  "completed_at": null,
  "last_error": null,
  "model": "gpt-4-1106-preview",
  "instructions": "You are a weather bot. Use the provided functions to answer questions.",
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "getCurrentWeather",
        "description": "Get the weather in location",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The city and state e.g. San Francisco, CA"
            },
            "unit": {
              "type": "string",
              "enum": [
                "c",
                "f"
              ]
            }
          },
          "required": [
            "location"
          ]
        }
      }
    },
    {
      "type": "function",
      "function": {
        "name": "getNickname",
        "description": "Get the nickname of a city",
        "parameters": {
          "type": "object",
          "properties": {
            "location": {
              "type": "string",
              "description": "The city and state e.g. San Francisco, CA"
            }
          },
          "required": [
            "location"
          ]
        }
      }
    }
  ],
  "file_ids": [],
  "metadata": {}
}

レスポンスは実行の情報を返すだけなので、エラーが返されていないかくらいの確認で良さそうです。
ちなみに、関数出力の送信をAPIで送るのに時間がかかってしまうと期限切れになってしまいます。
実行状態の確認などした際の、statusexpiredの場合は期限切れなので、もう一度メッセージを送りなおす必要があります。

メッセージのやりとりを確認

関数の結果を送信したことを踏まえてメッセージがどのように出力されたかを確認します。

curl https://api.openai.com/v1/threads/thread_BJx7VBxHG4uOU3e27Sax3jgM/runs/run_tyJrdU1WYzFAvNou640RJwaE/messages \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -H "OpenAI-Beta: assistants=v1"

レスポンス

{
  "object": "list",
  "data": [
    {
      "id": "msg_QSDCDInszGxtL4XdKlhO8wNR",
      "object": "thread.message",
      "created_at": 1704514594,
      "thread_id": "thread_L0vLoQnz0miavyGFZh6R1Sia",
      "role": "assistant",
      "content": [
        {
          "type": "text",
          "text": {
            "value": "横浜市の現在の天気は22℃です。横浜市のニックネームは「はま」とされています。",
            "annotations": []
          }
        }
      ],
      "file_ids": [],
      "assistant_id": "asst_DD8uOvrJ0PyNPU0AvM5x4jfj",
      "run_id": "run_scPYDkvkXs5rW4am7LbCHsrK",
      "metadata": {}
    },
    {
      "id": "msg_8nbNH4gRjjiRdJyRJ1G7Fchi",
      "object": "thread.message",
      "created_at": 1704514456,
      "thread_id": "thread_L0vLoQnz0miavyGFZh6R1Sia",
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": {
            "value": "横浜市の天気は?横浜市のニックネームは?",
            "annotations": []
          }
        }
      ],
      "file_ids": [],
      "assistant_id": null,
      "run_id": null,
      "metadata": {}
    }
  ],
  "first_id": "msg_QSDCDInszGxtL4XdKlhO8wNR",
  "last_id": "msg_8nbNH4gRjjiRdJyRJ1G7Fchi",
  "has_more": false
}

アシスタントが関数の結果をもとにメッセージを返却出来ているのがわかります。
横浜市の現在の天気は22℃です。横浜市のニックネームは「はま」とされています。

サポートされているファイル

text/のMIMEタイプの場合、エンコードはutf-8utf-16、またはasciiのいずれかである必要があります。

ファイル形式 MIMEタイプ コードインタープリタ 検索機能
.c text/x-c ⚪︎ ⚪︎
.cpp text/x-c++ ⚪︎ ⚪︎
.csv application/csv ⚪︎
.docx application/vnd.openxmlformats-officedocument.wordprocessingml.document ⚪︎ ⚪︎
.html text/html ⚪︎ ⚪︎
.java text/x-java ⚪︎ ⚪︎
.json application/json ⚪︎ ⚪︎
.md text/markdown ⚪︎ ⚪︎
.pdf application/pdf ⚪︎ ⚪︎
.php text/x-php ⚪︎ ⚪︎
.pptx application/vnd.openxmlformats-officedocument.presentationml.presentation ⚪︎ ⚪︎
.py text/x-python ⚪︎ ⚪︎
.py text/x-script.python ⚪︎ ⚪︎
.rb text/x-ruby ⚪︎ ⚪︎
.tex text/x-tex ⚪︎ ⚪︎
.txt text/plain ⚪︎ ⚪︎
.css text/css ⚪︎
.jpeg image/jpeg ⚪︎
.jpg image/jpeg ⚪︎
.js text/javascript ⚪︎
.gif image/gif ⚪︎
.png image/png ⚪︎
.tar application/x-tar ⚪︎
.ts application/typescript ⚪︎
.xlsx application/vnd.openxmlformats-officedocument.spreadsheetml.sheet ⚪︎
.xml application/xml or "text/xml" ⚪︎
.zip application/zip ⚪︎

Discussion