Bedrock Converse API の tool use はモデルが1ターンで複数ツールを並列呼び出しする
Bedrock Converse API の tool use で A/B テストのバリアント選択を検証したときの発見。3 種類のツール(get_user_profile、get_similar_users、get_variant_performance)を定義し、バリアント A/B 両方のパフォーマンスを取得するため計 4 回のツール呼び出しが必要なシナリオだった。
最初は「モデルが 1 つずつツールを呼び、結果を見て次のツールを呼ぶ」逐次パターンを想定していた。しかし実際には、モデルは 1 回のレスポンスで 4 つのツール呼び出しを同時に要求した。
Turn 1: get_user_profile({"user_id": "user_001"})
Turn 1: get_similar_users({"user_id": "user_001", "limit": 10})
Turn 1: get_variant_performance({"experiment_id": "cta_test_2024", "variant_id": "A"})
Turn 1: get_variant_performance({"experiment_id": "cta_test_2024", "variant_id": "B"})すべて Turn 1 で並列に要求され、結果を返すと Turn 2 で最終判断が返る。全検証で 2 ターンだった。
実装上のポイントは、output_message["content"] に複数の toolUse ブロックが含まれうるため、ループで全件処理して toolResult をまとめて返す必要がある点だ。
if response["stopReason"] == "tool_use":
tool_results = []
for block in output_message["content"]:
if "toolUse" in block:
tool = block["toolUse"]
result = execute_tool(tool["name"], tool["input"])
tool_results.append({"toolResult": {
"toolUseId": tool["toolUseId"],
"content": [{"json": json.loads(result)}],
}})
messages.append({"role": "user", "content": tool_results})ドキュメントの tool use のサンプルコードは単一ツールの例が中心だが、for ループで複数の toolUse ブロックを処理する構造にはなっている。ツール間に依存関係がなければ、モデルは並列呼び出しを選択する可能性が高い。
