AI News HubLIVE
サイト内リライト5 分で読了

Amazon Bedrockでのプログラム的ツール呼び出しの実装

プログラム的ツール呼び出し(PTC)は、モデルがサンドボックス内で実行されるPythonコードを生成してツール呼び出しをオーケストレーションすることで、レイテンシーとトークン消費を削減するパラダイムシフトです。この記事では、Amazon BedrockでPTCを実装する3つの方法を紹介します:ECS上のセルフホストDockerサンドボックス、Amazon Bedrock AgentCore Code Interpreterを使用したマネージドソリューション、およびAnthropic SDK互換のプロキシパスです。

ソースAWS Machine Learning Blog著者: Shreyas Subramanian

プログラム的ツール呼び出し(PTC)は、大規模言語モデル(LLM)が外部ツールと対話する方法におけるパラダイムシフトです。従来のツール呼び出しワークフローでは、各ツール呼び出しにモデルへの完全なラウンドトリップが必要でした:モデルがツールを呼び出し、結果を受信し、推論し、次のツールを呼び出す、というサイクルです。複数のツール呼び出しを含むワークフローでは、すべての中間結果がモデルのコンテキストウィンドウを通過するため、レイテンシーとトークン消費が累積的に増加します。

PTCは異なるアプローチをとります。モデルはPythonコードを記述し、サンドボックス実行環境内でプログラム的に複数のツールを呼び出します。コードにはループ、条件分岐、フィルタリング、集約ロジックを含めることができます。モデルはコードを生成するために一度だけサンプリングされ、実行環境がツール呼び出しを処理し、最終処理結果のみがモデルのコンテキストに返されます。これにより、マルチツールワークフローのレイテンシーとトークン使用量が劇的に削減されます。PTCは大規模データ処理、精密な数値計算、マルチステッププロセスのオーケストレーション、および生データがモデルのコンテキストに入るべきではないプライバシー重視のシナリオに特に効果的です。

PTCは当初はプロバイダ固有の機能として登場しましたが、基礎となるパターン(モデルがコードを生成し、サンドボックスが実行し、最終出力のみがコンテキストに返される)はモデルに依存しません。この記事では、Amazon Bedrock上でPTCを実装する3つの方法を紹介します:最大限の制御を提供するECS上のセルフホストDockerサンドボックス、Amazon Bedrock AgentCore Code Interpreterを使用したマネージドソリューション、およびAnthropic SDK互換のプロキシパス(その開発者エクスペリエンスを好むチーム向け)です。

従来のツール呼び出しのボトルネック

例を考えてみましょう:「どのエンジニアリングチームメンバーがQ3の出張予算を超過したか?」従来のツール呼び出し(並列関数呼び出しなし)では、モデルは以下を実行する必要があります:ツールを呼び出してチームメンバーリストを取得(20人)、各人の経費記録を取得するために20回の個別ツール呼び出し(それぞれ50~100行の明細を返す)、追加ツールを呼び出して予算しきい値を取得、2000件以上の経費記録をコンテキストウィンドウに取り込み、自然言語でデータセット全体を推論してフィルタリング、比較、集約する。これらの各ツール呼び出しにはモデルへの完全なラウンドトリップが必要であり、以下の3つの問題が発生します:トークン消費(モデルが最終的に破棄する何千もの経費明細を含むすべての中間結果がコンテキストを通過する)、レイテンシー(20回の順次ツール呼び出しで20回の推論ラウンドトリップ)、正確性(言語モデルに自然言語で数千件のレコードをフィルタリング、集約、比較させるのはエラーが発生しやすく、数行のPythonコードで正確に処理できる操作です)。

PTCの解決方法

PTCはパターンを反転させます。モデルは単一のPythonコードブロックを記述し、ツール呼び出しをオーケストレーションし、結果を処理し、最終出力のみを返します。同じ経費監査の例で、PTCが有効な場合にモデルが生成するコードは以下の通りです:

import asyncio import json

team_json = await get_team_members(department="engineering") team = json.loads(team_json)

expense_tasks = [get_expenses(employee_id=m["id"], quarter="Q3") for m in team] expenses_results = await asyncio.gather(*expense_tasks)

exceeded = [] for member, exp_json in zip(team, expenses_results): expenses = json.loads(exp_json) total_travel = sum(e["amount"] for e in expenses if e["category"] == "travel" and e["status"] == "approved") if total_travel > 5000: budget_json = await get_custom_budget(user_id=member["id"]) budget = json.loads(budget_json) limit = budget["budget_limit"] if total_travel > limit: exceeded.append({"name": member["name"], "spent": total_travel, "limit": limit, "exceeded_by": total_travel - limit})

print(f"{len(exceeded)} members exceeded budget:") print(json.dumps(exceeded, indent=2))

ここで注目すべき点は2つあります。第一に、asyncio.gather()は20件の経費照会を逐次的ではなく並行して発行するため、ツール呼び出しはほぼ同時に発生します。第二に、フィルタリング、集約、予算比較はPython内で行われ、自然言語ではありません。最終的なprint()出力のみがモデルのコンテキストウィンドウに返され、2000件以上の生の経費記録は触れません。モデルはコード生成と最終出力解釈の2回のみサンプリングされ、その間のすべて(ツール呼び出し、データ処理、フィルタリング)はコンテナ内で追加のモデル推論なしに行われます。

第1部:Amazon BedrockとAmazon ECSを使用したセルフホストPTC

セルフホストの理由

マネージドPTC実装はプロバイダ管理のサンドボックス環境に依存しますが、セルフホストには十分な理由があります:モデル非依存(Amazon Bedrockで利用可能なClaude、Qwen、MiniMax、Llama、Novaなどのモデルをサポート)、完全制御(サンドボックス環境のカスタマイズ、ドメイン固有のPythonパッケージのインストール、要件に合わせたセキュリティポリシーの構成が可能)、プライベートデプロイ(コード実行と中間データを自身のAWSアカウント内に保持)。

アーキテクチャ

セルフホストソリューションには2つのコンポーネントがあります:オーケストレーター(Boto3を使用してInvokeModel APIを呼び出し、Dockerサンドボックスのライフサイクルを管理し、ツール呼び出しループを処理するアプリケーション。ECSタスク、Lambda、またはコンピュートリソース)とDockerサンドボックス(モデル生成のPythonコードを実行する隔離コンテナ。stdin/stderrを介したIPCでオーケストレーターと通信)。核となるアイデアは単純です:通常tool_configに入れるツール定義をシステムプロンプトに注入し、モデルにそれらのツールをオーケストレーションするPythonコードを記述するよう指示します。生成されたコードはDockerサンドボックスで実行され、オーケストレーターがコントロールプレーンとして機能し、IPCを介してツール呼び出しを傍受し、外部で実行し、結果をサンドボックスに注入します。

システムプロンプト

システムプロンプトは、モデルをネイティブにPTCをサポートしているかのように動作させる重要な要素です。実行環境、利用可能なツール、コード生成のルールを記述します。このプロンプトは、モデルがネイティブPTC実装と同じパターン(単一コードブロック、非同期ツール呼び出し、print()出力)に従った構造化されたPythonコードを生成するように導きます。

コアコンポーネント

SandboxExecutorは中心的なコンポーネントであり、隔離されたDockerコンテナのライフサイクルを管理し、モデル生成コードを安全に実行し、ツール呼び出しのためのIPCプロトコルを処理します。システムはデュアルプロセスアーキテクチャを使用します:オーケストレーター(ECSタスクで実行)がコード実行要求ごとにDockerコンテナを起動し、標準I/Oストリームを介して通信し、コンテナはツール呼び出し要求をstderrに書き込み、オーケストレーターはstdinを介してツール結果を注入します。

Runnerスクリプトはオーケストレーターによって動的に生成され、起動時に各Dockerコンテナに注入され、コード実行、IPCプロトコル、ツール関数生成を処理します。2つの実行モードをサポートします:シングルモード(コードを一度実行して終了。ステートレスの単発タスクに適する)とループモード(コンテナを実行し続けて複数のコード実行を受け入れ、セッション再利用と状態保持をサポート)。

IPCプロトコル

テキストストリーム内の異なるメッセージタイプを確実に分離するために、システムは境界マーカーを定義します:PTC_TOOL_CALL / PTC_END_CALLはツール呼び出し要求(ツール名+引数をJSONとして)をラップし、PTC_OUTPUTはコード実行の最終出力をマークします。Runnerスクリプトが実行コード内でツール呼び出しに遭遇すると、呼び出しをJSONとしてシリアライズし、マーカー境界間でstderrに書き込み、stdinで結果を待ってブロックします。オーケストレーターはstderrを読み取り、ツール呼び出しを解析し、ツールを実行し、結果をstdinに書き戻します。Runnerスクリプトはブロック解除され、実行を続行します。

オーケストレーターループ

Amazon BedrockでPTCを有効にするには3つの要素が必要です:モデルにツールオーケストレーション用のPythonコードを記述するよう指示するシステムプロンプト、モデルがコードをサンドボックスに送信するために使用するexecute_codeツール定義、およびシステムプロンプトに埋め込まれたビジネスツールの説明(Amazon Bedrockの個別ツールとしては設定しない)。オーケストレーターはAmazon BedrockとDockerサンドボックスを結び付け、コアループはBedrockの呼び出し、サンドボックスでのコード実行、ツール呼び出しの処理、モデルへの結果返却を含みます。

この記事ではまた、Amazon Bedrock AgentCore Code Interpreterを使用したマネージドソリューションとAnthropic SDK互換のプロキシパスについても紹介していますが、セルフホストソリューションは最大の柔軟性と制御を提供します。PTCを実装することで、マルチツールワークフローのレイテンシーとトークン消費を大幅に削減し、正確性と大規模データセットの処理能力を向上させることができます。