LoginSignup
29
20

Azure OpenAI の Assistants API によるエージェント開発メモ①

Last updated at Posted at 2024-02-13

Assistants API を使うと、データを分析し、ソリューションを提案し、タスクを自動化できる、高度な Copilot のようなエクスペリエンスを備えたアプリケーションを簡単に作成できます。2024/02/06 に Azure OpenAI Service でもパブリックプレビューが開始されました。

ステートレスな Chat Completions API であるのに比べて、ステートフルな Assistants API は、自動的に管理される永続的なスレッドがサポートされます。つまり、会話状態管理システムを開発し、モデルのコンテキスト ウィンドウの制約を回避する必要がなくなります。さらにアシスタントは、必要に応じて複数のツールに並列アクセスすることもできます。 以下のツールが利用可能です。

動画でも解説しています

記事構成

第1回 Assistants API の開発(本記事)
第2回 AutoGen によるマルチエージェントの検証
第3回 マルチエージェントシステムの実装

あったらいいなシナリオ

Azure OpenAI Developers セミナー第2回でこんなのあったらいいなぁ的なイメージを共有しました。

image.png

当時は ChatGPT Plugin を用いて外部の様々なアプリケーションと接続するような構成となっていましたが、Assistants API ではプラグインは Tool という概念となり、その中で "type": "function" のように指定できるようになりました。また、"type": "code_interpreter" のように指定して Code Interpreter を有効化することもできます。

アシスタントの役割

上の図では一つのエージェントがすべての Tools をオーケストレートしていますが、Assistants API を使うと簡単にマルチエージェントを開発できます。マルチエージェントアシスタントでは、ユーザーからの質問を複数のエージェントに割り振るプロキシエージェントを前段に配置します。それぞれのアシスタントの専門性ごとにシステムプロンプトを用意してキャラクターを分けます。今回はトラベルアシスタントから開発していきます。

image.png

それぞれのアシスタントに計算機能をつけるために、Code Interpreter は全部で有効化してもいいですね。

リアリティのある Tools を作ろう

まずは RAG 機能から実装していきます。今回は無料で使える Web API を使ってテストしてみたいと思います。ホテル検索は楽天トラベルさんの API を、レストラン検索はホットペッパーグルメさんの API を使わせていただきたいと思います。交通機関の検索については無料の API がありませんでした… 注意:今後 LLM 経由で API を使った場合の利用規約が追加されるかもしれません。

tools_list = [
    {
        "type": "code_interpreter"
    },
    {
        "type": "function",
        "function": {
            "name": "search_hotpepper_shops",
            "description": "ホットペッパーグルメAPIを利用し、キーワードや個室の有無などのオプションフィルターで飲食店を検索できます。",
            "parameters": {
                "type": "object",
                "properties": {
                    "keyword": {
                        "type": "string",
                        "description": "飲食店を検索するためのキーワード。店名、住所、駅名、お店ジャンルなどを指定できる。ユーザーメッセージから検索キーワードとなる文字を抽出して検索クエリーにしてください。例: ###大阪駅 和食###"
                    },
                    "private_room": {
                        "type": "integer",
                        "description": "個室ありの店舗のみを検索, 0:絞り込まない, 1:絞り込む。オプション",
                        "enum": [0, 1]
                    }
                },
                "required": ["keyword"]
            }
        }
    },
    {
        "type": "function",
        "function": {
            "name": "search_vacant_hotels",
            "description": "楽天トラベルのAPIを使って、場所、チェックイン日、チェックアウト日、予算、大人の人数など、さまざまなフィルターで空室ホテルを検索できます。",
            "parameters": {
                "type": "object",
                "properties": {
                    "latitude": {
                        "type": "number",
                        "description": "ホテル検索場所の緯度(WGS84), ex:35.6065914"
                    },
                    "longitude": {
                        "type": "number",
                        "description": "ホテル検索場所の経度(WGS84), ex:139.7513225"
                    },
                    "searchRadius": {
                        "type": "number",
                        "description": "緯度経度検索時の検索半径(単位km), 0.1 to 3.0"
                    },
                    "checkinDate": {
                        "type": "string",
                        "description": "yyyy-MM-dd 形式のチェックイン日。年の指定がない場合は2024年を指定する。"
                    },
                    "checkoutDate": {
                        "type": "string",
                        "description": "yyyy-MM-dd 形式のチェックアウト日。年の指定がない場合は2024年を指定する。"
                    },
                    "maxCharge": {
                        "type": "integer",
                        "description": "上限金額, int 0 to 999999999"
                    },
                    "adultNum": {
                        "type": "integer",
                        "description": "宿泊者数, int 1 to 99"
                    }
                },
                "required": ["latitude", "longitude", "searchRadius", "checkinDate", "checkoutDate"]
            }
        }
    }
]

Function calling の作成に必要なパラメータは namedescriptionparameters の 3 つです。もし ChatGPT Plugins で OpenAPI Spec. に従って開発しているのであればそのまま持ってくることができます!開発したプラグインが無駄になることはありません。

"function": {
    "name": "search_vacant_hotels",
    "description": "この説明を読んで使うべきツールを選定します",
    "parameters": {
        "type": "object",
            "properties": {
                "query": {
                    "type": "string",
                    "description": "検索クエリーを指定します。"
                },
            }
    }
}
 ...

1. アシスタントの作成

Assistants API の開発で便利な Python コードをここにメモっておきます。ベースのコードは OpenAI Cookbook を参考にさせてもらいます。Azure で Assistants API を使うには、clientAzureOpenAI に切り替えるだけです。とっても便利!

from openai import AzureOpenAI
client = AzureOpenAI(
    api_key=os.getenv("AZURE_OPENAI_API_KEY"),
    api_version="2024-02-15-preview",
    azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
)

# Create an assistant
assistant = client.beta.assistants.create(
    name="Travel Assistant",
    instructions="""
あなたは Contoso 社の社員の出張を支援するためのアシスタントです。あなたは以下の業務を遂行します。
 - 旅程を作成します
 - ホテルを検索したり予約します
 - 交通機関を検索します
 - 出張で行くべきレストランや居酒屋を提案します
 - 出張にかかる概算費用を計算します

#制約事項
 - ユーザーからのメッセージは日本語で入力されます
 - ユーザーからのメッセージから忠実に情報を抽出し、それに基づいて応答を生成します。
 - ユーザーからのメッセージに勝手に情報を追加したり、不要な改行文字を追加してはいけません

""",
    tools=tools_list,
    model="gpt-4-1106-preview" #You must replace this value with the deployment name for your model.
)

instructions にシステムプロンプトを、model に使用する GPT モデルを指定します。Azure OpenAI ではまだ gpt-4-0125-preview モデルが Assistants API で使用できませんが、検証したところ、gpt-4-1106-preview よりも gpt-4-0125-preview のほうが無駄な文字列の生成が無くなるなどの改善がありました。v0125 はよ。並列 Function calling を使用したい場合は v1106 以降が必要です。

1.1. アシスタントを一覧表示

既存のアシスタントを利用したい場合、存在するアシスタントを一覧表示できます。

assistants = client.beta.assistants.list()
print(assistants.model_dump_json(indent=2))

1.2. アシスタントの取得

特定のアシスタントにアクセスするには assistant_id を指定することで Assistant オブジェクトを取得できます。

assistant = client.beta.assistants.retrieve(assistant_id="Your Assistant ID") 
print(assistant.model_dump_json(indent=2))

2. スレッドを作成する

スレッドとメッセージは、アシスタントとユーザーの会話セッションを表します。スレッドに保存できるメッセージの数に制限はありません。メッセージのサイズがモデルのコンテキストウィンドウを超えると、スレッドはコンテキストウィンドウに収まるできるだけ多くのメッセージを含めようとし、最も古いメッセージを削除します。

このようにメッセージの初期リストを持つスレッドを作成することができます。

# Create a thread
thread = client.beta.threads.create()
print(thread.model_dump_json(indent=2))

2.1. スレッドの取得

すでにスレッドが存在していて、そのスレッドにアクセスしたい場合、thread_id を指定することでスレッドオブジェクトを取得できます。

thread = client.beta.threads.retrieve(thread_id="Your Thread ID")
print(thread.model_dump_json(indent=2))

2.2. スレッドメッセージの一覧表示

messages = client.beta.threads.messages.list(
  thread_id=thread.id
)
print(messages.model_dump_json(indent=2))

この状態ではまだメッセージは存在していません。

3. Run を作成する

スレッドはアシスタントから独立して存在します。特定のアシスタント ID からスレッドを辿ることはできません。

指定されたスレッドに対してアシスタントから生成結果を得るには、Run を作成する必要があります。Run を作成すると、アシスタントはスレッドのメッセージを見て、1つの応答を追加するか、ツールを使用してアクションを実行する必要があるか判断します。

Run を作成しましょう。threads.runs.createsubmit_message ヘルパー関数の中で記述しています。アシスタントとスレッドの両方を指定する必要があります。

#指定されたスレッドにユーザーメッセージを送信し、その後に同じスレッドで Run を開始
def submit_message(assistant_id, thread_id, user_message):
    client.beta.threads.messages.create(
        thread_id=thread_id, role="user", content=user_message
    )
    return client.beta.threads.runs.create(
        thread_id=thread_id,
        assistant_id=assistant_id,
        
# すでに作成されたスレッドにメッセージを送信する
def create_run(user_input):
    run = submit_message(assistant.id, thread.id, user_input)
    return run

run = create_run("有楽町駅近辺でイタリアンのレストランを探しています")
run = wait_on_run(run, thread.id)
# スレッドのメッセージ一覧を出力 client.beta.threads.messages.list
pretty_print(get_response(thread.id))

Initial Run status: queued
Run status: queued
Run status: in_progress
Run status: in_progress
Run status: in_progress
Run status: in_progress
Run status: requires_action
Final Run status: requires_action
# Messages
user: 有楽町駅近辺でイタリアンのレストランを探しています

Chat Completions API で完了を作成するのとは異なり、Run の作成は非同期操作 です。Runのメタデータはすぐに返され、そのメタデータには status が含まれます。アシスタントが操作(ツールの使用やメッセージの追加など)を実行すると、statusは更新されます。

Run のステータス遷移に注目してください。
queued -> in_progress -> requires_action

image.png
(画像は OpenAI 社より引用)

Run のステータス遷移をすべて出力する

アシスタントの処理がいつ完了したかを知るには、ループ内で Run をポーリングします。

#Run の非同期処理が完了するまでそのステータスを監視し続けるためのループを実装
import time
def wait_on_run(run, thread_id):
    # 最初のステータス出力
    print(f"Initial Run status: {run.status}")
    while run.status == "queued" or run.status == "in_progress":
        run = client.beta.threads.runs.retrieve(
            thread_id=thread_id,
            run_id=run.id,
        )
        print(f"Run status: {run.status}")
        time.sleep(1)
    # ループ終了後の最終ステータス出力
    print(f"Final Run status: {run.status}\n")
    return run

4. Tools (Function の呼び出し)

Assistants API の重要な機能は、Code Interpreter、検索、カスタム関数などの Tool をアシスタントに装備できることです。Tool は最大 128 個まで登録できます。

アシスタントが正確に何をしているのかもっと詳しく知りたい場合は、Run のステップを見る必要があります。

4.1. ステップ

Run の statusrequires_action になると Run の実行が一時停止され、Tool の実行結果の待ち状態となります。この時、当該 Run の内部ではどうなっているのでしょうか。Run の下位の実行単位である Run step を参照してみましょう。

run_steps = client.beta.threads.runs.steps.list(
    thread_id=thread.id, run_id=run.id, order="asc"
)

print(run_steps.model_dump_json(indent=2))

いっぱい出てきちゃいましたね。呼ぶべき Tool は step_details の中にあります。

for step in run_steps.data:
    step_details = step.step_details
    print(step_details.model_dump_json(indent=2))

tool_calls の中に呼ぶべき Function name と引数 arguments が抽出されました!

{
  "tool_calls": [
    {
      "id": "call_Je8w5sIPkdaB8WzEjC2PrDK5",
      "function": {
        "arguments": "{\"keyword\":\"有楽町駅 イタリアン\"}",
        "name": "search_hotpepper_shops",
        "output": null
      },
      "type": "function"
    }
  ],
  "type": "tool_calls"
}

4.2. tool_call オブジェクトの取得

Assistant API の Function calling はあくまで呼ぶべき関数の選択とパラメータの抽出のみしか行いません。実際の関数実行はアプリケーション側で行います。関数の実行に必要な namearguments を解析して必要な情報を抽出しましょう。

tool_call = run.required_action.submit_tool_outputs.tool_calls[0]
name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
arguments

4.3. ホットペッパー API の実行

ホットペッパーさんのグルメ API はめちゃめちゃパラメータがありますので、今回サンプルで使用する分だけ選別しています。

tool_call オブジェクトから得られた検索パラメータを search_hotpepper_shops 関数のパラメータとして流し込みます。

import requests

def search_hotpepper_shops(api_key, keyword=None, private_room=0, start=1, count=3, response_format='json'):
    base_url = "http://webservice.recruit.co.jp/hotpepper/gourmet/v1/"
    params = {
        "key": api_key,
        "format": response_format,
        "start": start,
        "count": count,
        "private_room": private_room,  # 個室ありの店舗のみを検索, 0:絞り込まない, 1:絞り込む
        #"budget": "B005"
    }
    
    # キーワードが指定されている場合、パラメータに追加
    if keyword:
        params["keyword"] = keyword

    response = requests.get(base_url, params=params)
    
    # レスポンス形式に応じて結果を処理
    if response_format == 'json':
        return response.json()  # JSON形式のレスポンスを返す
    else:
        return response.text  # XML形式の場合は、レスポンスのテキストをそのまま返す

# 使用例
api_key = "Your API Key"  # APIキーを設定
keyword = arguments.get("keyword")  # 検索キーワードを設定
private_room = arguments.get("private_room", 0) # 個室ありの店舗のみを検索, 0:絞り込まない, 1:絞り込む
response = search_hotpepper_shops(api_key, keyword=keyword, private_room=private_room, response_format='json')

print(response)  # 検索結果を表示

出力を構造化

GPT-4 に渡すデータだけ構造化しています。

shops = []
for s in response["results"]["shop"]:
    shop = {"name": s["name"],
            "address": s["address"],
            "station_name": s["station_name"],
            "access": s["access"],
            "genre": s["genre"],
            "budgetAverage": s["budget"]["average"],
            "open": s["open"],
            "close": s["close"]
    }
    shops.append(shop)

shops
[{'name': 'ウメ子の家 銀座 有楽町駅前店',
  'address': '東京都千代田区有楽町2-3-5\u3000aune有楽町7F',
  'station_name': '有楽町',
  'access': '地下鉄日比谷線、千代田線日比谷駅A1出口直結/地下鉄銀座駅C1出口すぐです!!絶品のイタリアン料理とビールで乾杯♪',
  'genre': {'name': '居酒屋',
   'catch': '居酒屋 イタリアン ステーキ 個室\u3000宴会',
   'code': 'G001'},
  'budgetAverage': '3700円',
  'open': '月~日、祝日、祝前日: 17:00~23:30 (料理L.O. 22:30 ドリンクL.O. 23:00)',
  'close': ''},
 {'name': 'マイアミヴィッラ 有楽町イトシアプラザ店',
  'address': '東京都千代田区有楽町2-7-1-S11\u3000有楽町駅前ビルディング1F',
  'station_name': '有楽町',
  'access': '有楽町駅前広場前のイトシアプラザ1Fにございます!!',
  'genre': {'name': 'イタリアン・フレンチ', 'catch': '有楽町駅前広場前のイタリアン☆', 'code': 'G006'},
  'budgetAverage': '2000円',
  'open': '月~日、祝日、祝前日: 10:00~23:00 (料理L.O. 22:00 ドリンクL.O. 22:30)',
  'close': 'なし'},
 {'name': 'イルバロッコ 有楽町店',
  'address': '東京都千代田区有楽町2‐1‐16',
  'station_name': '有楽町',
  'access': 'JR有楽町駅徒歩3分/地下鉄千代田線日比谷駅徒歩3分/地下鉄丸ノ内線銀座駅徒歩3分',
  'genre': {'name': 'イタリアン・フレンチ', 'catch': 'イタリアン・バール', 'code': 'G006'},
  'budgetAverage': '2000円(通常平均)/850円(ランチ平均)',
  'open': '月~日、祝日、祝前日: 11:00~22:00 (料理L.O. 21:30)',
  'close': '不定休'}]

ホットペッパーグルメ Webサービス

4.4. Tool 出力の送信

それでは、ホットペッパー API のレスポンスをアシスタントに送信してみましょう。先ほど解析した tool_call で見つかった tool_call ID が必要です。

input={"answer": shops, "success": True}

run = client.beta.threads.runs.submit_tool_outputs(
    thread_id=thread.id,
    run_id=run.id,
    tool_outputs=[
        {
            "tool_call_id": tool_call.id,
            "output": json.dumps(input),
        }
    ],
)

run = wait_on_run(run, thread.id)
# スレッドのメッセージ一覧を出力 client.beta.threads.messages.list
pretty_print(get_response(thread.id))

Initial Run status: queued
Run status: queued
Run status: in_progress
Run status: completed
Final Run status: completed

image.png

user: 有楽町駅近辺でイタリアンのレストランを探しています
assistant: 有楽町駅近辺でイタリアンのレストランをいくつか見つけました。以下のオプションをご検討ください。

1. ウメ子の家 銀座 有楽町駅前店
   - 住所: 東京都千代田区有楽町2-3-15 aune有楽町7F
   - アクセス: 地下鉄日比谷線、千代田線日比谷駅!1出口直結/地下鉄銀座線銀座駅C1出口すぐです!!
   - ジャンル: 居酒屋、イタリアン、ステーキ、個室 宴会
   - 平均予算: 3,700円
   - 営業時間: 月~日、祝日、祝前日: 17:00~23:30(料理L.O. 22:30 ドリンクL.O. 23:00)

2. マイアミヴィラ 有楽町イトシアプラザ店
   - 住所: 東京都千代田区有楽町2-7-1-S11 有楽町駅前ビルディング1F
   - アクセス: 有楽町駅前広場前のイトシアプラザ1Fにございます!!
   - ジャンル: イタリアン・フレンチ
   - 平均予算: 2,000円
   - 営業時間: 月~日、祝日、祝前日: 10:00~23:00(料理L.O. 22:00 ドリンクL.O. 22:30)

3. イルバロッコ 有楽町店
   - 住所: 東京都千代田区有楽町2-1-16
   - アクセス: JR有楽町駅徒歩3分/地下鉄千代田線日比谷駅徒歩3分/地下鉄丸ノ内線銀座駅徒歩3分
   - ジャンル: イタリアン・バール
   - 平均予算: 2,000円(通常平均)/850円(ランチ平均)
   - 営業時間: 月~日、祝日、祝前日: 11:00~22:00(料理L.O. 21:30)

これらの情報を元にお好みのレストランを選んでください。

注意
requires_action ステータスの継続時間は約 10 分です。この時間を過ぎると expired となり Run が終了されます。

4.5. Run ステップの確認

これでもう一度 Run が完了するのを待ち、Run ステップをチェックします。

run_steps = client.beta.threads.runs.steps.list(
    thread_id=thread.id, run_id=run.id, order="asc"
)

for step in run_steps.data:
    step_details = step.step_details
    print(step_details.model_dump_json(indent=2))

2 つのステップの step_details が存在しているかと思います。

  1. tool_calls (複数形。1つのステップで複数になる可能性があるため)
  2. message_creation メッセージの作成

最初のステップは tool_calls で、今回の functioncode_interpreter の呼び出しが格納されます。

  • input は Tool が呼ばれる前に生成された Python コード
  • Tool を実行した結果の output が格納

2 つ目のステップは message_creation で、結果をユーザーに伝えるためにスレッドに追加された message が含まれます。

4.6. メッセージの確認

messages = client.beta.threads.messages.list(
  thread_id=thread.id
)
print(messages.model_dump_json(indent=2))
{
  "data": [
    {
      "id": "msg_xxx",
      "assistant_id": "asst_xxx",
      "content": [
        {
          "text": {
            "annotations": [],
            "value": "有楽町駅近辺でイタリアンのレストランをいくつか見つけました。以下のオプションをご検討ください。\n\n1. ウメ子の家 銀座 有楽町駅前店\n   - 住所: 東京都千代田区有楽町2-3-15 aune有楽町7F\n   - アクセス: 地下鉄日比谷線、千代田線日比谷駅!1出口直結/地下鉄銀座線銀座駅C1出口すぐです!!\n   - ジャンル: 居酒屋、イタリアン、ステーキ、個室 宴会\n   - 平均予算: 3,700円\n   - 営業時間: 月~日、祝日、祝前日: 17:00~23:30(料理L.O. 22:30 ドリンクL.O. 23:00)\n\n2. マイアミヴィラ 有楽町イトシアプラザ店\n   - 住所: 東京都千代田区有楽町2-7-1-S11 有楽町駅前ビルディング1F\n   - アクセス: 有楽町駅前広場前のイトシアプラザ1Fにございます!!\n   - ジャンル: イタリアン・フレンチ\n   - 平均予算: 2,000円\n   - 営業時間: 月~日、祝日、祝前日: 10:00~23:00(料理L.O. 22:00 ドリンクL.O. 22:30)\n\n3. イルバロッコ 有楽町店\n   - 住所: 東京都千代田区有楽町2-1-16\n   - アクセス: JR有楽町駅徒歩3分/地下鉄千代田線日比谷駅徒歩3分/地下鉄丸ノ内線銀座駅徒歩3分\n   - ジャンル: イタリアン・バール\n   - 平均予算: 2,000円(通常平均)/850円(ランチ平均)\n   - 営業時間: 月~日、祝日、祝前日: 11:00~22:00(料理L.O. 21:30)\n\nこれらの情報を元にお好みのレストランを選んでください。"
          },
          "type": "text"
        }
      ],
      "created_at": 1707668144,
      "file_ids": [],
      "metadata": {},
      "object": "thread.message",
      "role": "assistant",
      "run_id": "run_xxx",
      "thread_id": "thread_xxxxx"
    },
    {
      "id": "msg_xxx",
      "assistant_id": null,
      "content": [
        {
          "text": {
            "annotations": [],
            "value": "有楽町駅近辺でイタリアンのレストランを探しています"
          },
          "type": "text"
        }
      ],
      "created_at": 1707667772,
      "file_ids": [],
      "metadata": {},
      "object": "thread.message",
      "role": "user",
      "run_id": null,
      "thread_id": "thread_xxxxx"
    },

最終的に 2 つのメッセージが作成されて対話が完成しました。

5. Code Interpreter

計算が必要な質問をすると、自動的に Code Interpreter を起動して質問文から計算式を立式して計算結果を返却します。

run = create_run("2/14 なにわ亭で10,000円、2/15 朝食3,000円、宿泊代16,000円、新幹線代は片道で14,920円でした。合計金額を計算してください。あ、新幹線代は往復でね。")
run = wait_on_run(run, thread.id)
# スレッドのメッセージ一覧を出力
pretty_print(get_response(thread.id))

Initial Run status: queued
Run status: in_progress
Run status: in_progress
Run status: completed
Final Run status: completed
# Messages
user: 2/14 なにわ亭で10,000円、2/15 朝食3,000円、宿泊代16,000円、新幹線代は片道で14,920円でした。合計金額を計算してください。あ、新幹線代は往復でね。
assistant: 新幹線代を往復で計算すると、出張全体での合計金額は58,840円となります。

run_steps = client.beta.threads.runs.steps.list(
    thread_id=thread.id, run_id=run.id, order="asc"
)
for step in run_steps.data:
    step_details = step.step_details
    print(step_details.model_dump_json(indent=2))
"tool_calls": [
    {
      "id": "call_uHpmvNT5qFLHVrIgAvB9fsjR",
      "code_interpreter": {
        "input": "# 各項目の金額
            restaurant_cost = 10000
            breakfast_cost = 3000
            accommodation_cost = 16000
            shinkansen_one_way_cost = 14920
            
            # 往復なので新幹線の費用は片道の2倍
            shinkansen_round_trip_cost = shinkansen_one_way_cost * 2
            
            # 合計金額の計算
            total_cost = restaurant_cost + breakfast_cost + accommodation_cost + shinkansen_round_trip_cost
            
            total_cost",
        "outputs": [
          {
            "logs": "58840",
            "type": "logs"
          }
        ]
      },
      "type": "code_interpreter"
    }
  ],

Code Interpreter のおかげでモデルが苦手な計算問題も任せられますね。

6. 並列 Function calling

複数の Tool の結果が必要となるような複雑な指示を投入します。

run = create_run("2/14に大阪に1人で出張します。大阪駅近辺の当日の居酒屋と20000円以内で泊まれる大阪駅のホテルを提案してください。")
run = wait_on_run(run, thread.id)
# スレッドのメッセージ一覧を出力
pretty_print(get_response(thread.id))

Initial Run status: queued
Run status: in_progress
Run status: in_progress
Run status: requires_action
Final Run status: requires_action
# Messages
user: 2/14に大阪に1人で出張します。大阪駅近辺の当日の居酒屋と20000円以内で泊まれる大阪駅のホテルを提案してください。

run_steps = client.beta.threads.runs.steps.list(
    thread_id=thread.id, run_id=run.id, order="asc"
)

for step in run_steps.data:
    step_details = step.step_details
    print(step_details.model_dump_json(indent=2))
"tool_calls": [
  {
    "id": "call_mLV4SqqoZszCjt0utqaaQtcw",
    "function": {
      "arguments": "{\"keyword\": \"大阪駅 居酒屋\"}",
      "name": "search_hotpepper_shops",
      "output": null
    },
    "type": "function"
  },
  {
    "id": "call_IkYzUhdJs6dc0doMkwePmFr6",
    "function": {
      "arguments": "{\"latitude\": 34.702485, \"longitude\": 135.495951, \"searchRadius\": 1.0, \"checkinDate\": \"2024-02-14\", \"checkoutDate\": \"2024-02-15\", \"maxCharge\": 20000, \"adultNum\": 1}",
      "name": "search_vacant_hotels",
      "output": null
    },
    "type": "function"
  }
],

COOL!😎

7. 付録 Assistants API 操作でよく使う API

Run のキャンセル

in_progress 状態の Run をキャンセルします。

client.beta.threads.runs.cancel(thread_id=thread.id, run_id="run_xxx")

スレッドの削除

スレッドの削除はできますが、メッセージの削除は… ありません。

client.beta.threads.delete(thread_id="thread_xxx")

アシスタントのアップデート

途中でアシスタントの設定を修正したい場合、assistant_id を指定してアップデートできます。

client.beta.assistants.update(assistant_id="Your Assistant ID",
    name="Travel Assistant",
    instructions="""xxx"""
    tools=tools_list,
    model="gpt-4-0125-preview"
    )

アシスタントの削除

client.beta.assistants.delete("asst_xxx")

Github

もっと細かく一連の動作を確認するにはこちらの Notebook を参照ください。

第2回

AutoGen を使用してマルチエージェントアーキテクチャの技術検証を行います。

参考

以下を参考に作成しました。

29
20
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
29
20