はじめに
先日のre:Inventにて、Amazon Bedrockのマルチエージェントコラボレーション機能がパブリックプレビュー版として発表されました。エージェントに関するアップデートは関心が高く、もうかなりの数記事が上がっているかと思いますが、自身の理解を整理するためにも本記事でまとめてみました。
写真はオンデマンド配信されているKeynote映像より拝借
AIエージェントとは
まずAIエージェントとは、特定の目的を遂行するために、自律的にタスク分解を行い、外部環境と対話しながら遂行するシステムのことを指します。
下記はAWS公式ページに記載されている例ですが、エージェントはユーザからのリクエストに対して、ユーザデータにアクセスして、ユーザーの嗜好や関連情報を活用し、カスタマイズされたアウトプット(例: メール、推薦リストなど)を生成するような例が紹介されています。
AWSではAIエージェントのサービスとしてAmazon Bedrock Agents(旧:Agents for Amazon Bedrock)を提供しています。
本機能ではアクショングループを定義してLambda関数を呼び出し特定のアクションを実行することや、Amazon Bedrock Knowlegde Basesを通じて、AWS内のデータソースにアクセスすることで、複雑なユーザからのリクエストに対応することができます。
下記は私が去年のAgent for Amazon Bedrockについて登壇したときの資料です。
さて、このAmazon Bedrock Agentsですが、今までは単一のAIエージェントを定義して、すべてのタスクをそのエージェントが処理するようシングルエージェント構成のみサポートしていました。
このシングルエージェントにおける課題を次に挙げたいと思います。
シングルエージェントの課題
シングルエージェント構成における課題の一つとして、アクショングループ等の外部機能を追加するにつれて、単一エージェントの責務が肥大化するという点が挙げられます。
一つのエージェントが複数のタスクを任せるように設計をしてしまうと、エージェントを定義するプロンプトやコードが複雑化し、テストの実装も困難になります。
さらにプロンプトが長くなることによって、さらにコスト増加や処理が遅くなるだけではなく、Lost in the Middle(コンテキストの中間情報に注意が向かなくなる現象)が起こり、エージェントが適切なアクションを呼び出せなくなる可能性があります。
下記はAWS re:Invent 2024のマルチエージェントコラボレーションに関するセッションにて、一つのエージェントに複数のタスクを紐づけすぎた例を紹介しており、過剰に機能を詰め込むことで管理や設計が難しくなる問題を強調していました。
セッション「AIM304: Using multiple agents for scalable generative AI applications」より抜粋
これらの課題を解決する一つのアプローチとしてマルチエージェント方式の採用が考えられます。
マルチエージェントとは
かの有名な人工知能研究者のAndrew氏によると、マルチエージェントとはAIエージェントの設計パターンの一つであり、複数のAIエージェントが協力し、タスクを分担し、アイデアを議論し、討論することで、単一のエージェントよりも優れた解決策を導き出す手法です。
例えば、ソフトウェア開発という複雑なタスクの場合、マルチエージェントアプローチでは、タスクを役割ごとのサブタスクに分割します。ソフトウェアエンジニア、プロダクトマネージャー、デザイナー、QA(品質保証)エンジニアなど、各専門分野に特化したエージェントがそれぞれのサブタスクを実行する形です。
このようにタスクをサブタスクに分解し、それらを各専門のAIエージェントが担当することによって、各処理ごとのコンテキストサイズを最適化し、複雑なタスクであっても既存のシングルエージェントよりも高い精度で実行することができます。
ちなみに、AWS以外ではマルチエージェントのフレームワークやサービスが登場しています。
-
Autogen
Microsoft Researchが開発したオープンソースのマルチエージェントフレームワーク。.Net, Pythonをサポートしており、マルチエージェントの宣言的な定義が可能。 -
CrewAI
Pythonベースの軽量なマルチエージェントフレームワーク。役割ベースのエージェント設計が特徴的で、実装が比較的シンプル。 -
LlamaDeploy(旧名:LlamaAgents)
LlamaIndexが開発したオープンソースのフレームワーク。マイクロサービスアーキテクチャをベースに設計されており、コントールプレーン上でオーケストレーションや状態管理、メッセージキューによる非同期処理実現し、スケーラビリティが特徴。 -
LangGraph
LangChainのツール群の一つで、グラフによってLLMエージェントのステップ、アクションを定義可能。LLMエージェントをノードとして、それぞれの関係性をエッジとして定義することができることに加え、Stateによるノード間の状態管理や条件分岐処理等もでき、より柔軟なマルチエージェント構成を実装可能。 -
Swarm
OpenAIが開発したマルチエージェントフレームワークであり、他のフレームワークと比較してシンプルにマルチエージェントを実装できる。教育目的でのサンプルフレームワークであり、プロダクションでの利用は意図していないとのこと。
参考までに、下記は私が米国のハッカソンに参加したときに、CrewAIなどを活用して構築したマルチエージェントシステムの概要です。個人の金融商品のポートフォリオ分析を複数の分析エージェントで分析し、評価し、アドバイザエージェントがレコメンドを作成、マネージャが最後情報をまとめて、ユーザに分析結果を提供するような構成となっています。
マルチエージェントの設計パターン
マルチエージェントにはいくつかの設計パターンがあります。いろいろな考え方がありますが、今回は下記のLangchainのブログで紹介されている代表的なパターンを簡単に紹介します。
スーパーバイザ(監督)パターン
このパターンでは、単一のスーパーバイザ(監督)エージェントがユーザからの要求を受け付け、各エージェントと通信します。スーパーバイザエージェントは次にどのエージェントを呼び出すかを決定します。
Bedrockマルチエージェントコラボレーション機能は、本パターンを採用しているものと思われます。
流れの中心が1つに集約されるため、タスクの管理やエージェント間のやり取りを把握しやすいのがメリットである一方で、全通信がスーパーバイザを経由するため、スーパーバイザに負荷が集中し、ボトルネックになるような可能性があります。
ヒエラルキカル(階層)パターン
このパターンはスーパーバイザーパターンの派生したようなパターンで、単一のスーパーバイザーの下にさらに複数のサブスーパーバイザーがいて、多段的に各エージェントと通信を行うような構成です。
担当領域をより細分化してサブスーパーバイザに委任でき、スーパーバイザ機能を複数レイヤーに分けることができるため、上記のスーパーバイザパターンよりも負荷が集中しにくいことが考えられます。
ただ、逆に階層構造が深くなる分、処理の流れや連携が複雑になり、テストがより難しくなるという課題もあります。
ネットワークパターン
このパターンでは上記の2つの構成のようなスーパーバイザーを持たず、各エージェントは他のすべてのエージェントと通信でき、どのエージェントも次にどのエージェントを呼び出すかを自律的に決定できます。
スーパーバイザの役割をもつエージェントがいないため、単一点に処理が集中するようなリスクが少なく、エージェントの自由度が高いです。
一方で通信経路の自由度が高いため、通信経路の順序が毎回違うなどの再現性の低さや、問題箇所の特定が難しいといった課題が考えられます。
Bedrockのマルチエージェントコラボレーション機能
だいぶ遠回りしましたが、今回のアップデートであるBedrockマルチエージェントコラボレーション機能により複数のAIエージェントが自律的に相互作用しながら、ユーザからのリクエストを遂行できるようになりました。
上で整理した通り、今回のマルチエージェントコラボレーション機能はスーパーバイザパターンに該当し、下記の図のようにスーパーバイザがリクエストを理解、タスクに分解し、各タスクを各専門エージェントに委任して、各エージェントからの回答をもとに、最終的な回答をユーザに返却します。
参考: Introducing Multi-agent Collaboration Capability for Amazon Bedrock (公式AWSブログ)
スーパーバイザのルーティング方法ついては以下の2つを選ぶことができるようです。
-
Supervisor Mode
このモードではスーパーバイザエージェントが入力を分析し、タスクを分解、言い換えをし、後続のエージェントにタスクを委任する。 -
Supervisor with routing
このモードではスーパーバイザーはユーザからのリクエストに対してタスクを分解しようとはせず、直接後続のエージェントに処理をルーティングする。しかし、ユーザからのリクエストが曖昧だった場合は上記のSupervisor Modeに切り替わる。
上でまとめたマルチエージェントのパターン整理からスーパーバイザパターンではスーパーバイザに処理が集中することによるリスクを挙げましたが、Supervisor with routingを使うことで、スーパーバイザ上での不要な推論処理を避けることに繋がるのではないかと思いました。
マルチエージェントコラボレーションでは、以下のベースモデルが利用可能です。
- Anthropic Claude 3 Haiku / Opus / Sonnet / 3.5系各種
- Amazon Nova Pro / Lite / Micro
マルチエージェントの特性を活かし、あるエージェントにはAmazon Novaを活用し、別エージェントにはClaudeを活用するなど、タスクに応じて最適なモデルを選択することができます。
マルチエージェントシステムを構築してみる
下に公式が提供するサンプルコードがいくつかあり、PythonのAWS SDKを利用して簡単にBedrockのマルチAIエージェントをデプロイすることができます。
今回はコードを参考にし、コンソール上で構築、テストをしてみたいと思います。
上記のリポジトリにある、trip_planner_agent
を作ってみます。
このエージェントシステムではレストラン調整エージェント(Restaurant Scout)とアクティビティ検索エージェント(Activity Finder)がWeb検索を行って、アイディアを探し、最後にまとめ役のエージェント(Itinerary Compiler)が旅行計画をまとめてユーザに提案します。
事前準備:アクショングループで使用するLambda関数の定義
まずは下記のリポジトリで提供されているweb検索を行うLambda関数を定義します。
リポジトリを開き、Readmeを確認すると、Deploy web_search_stack.yamlという項目があり、下記のように「Launch Stack」というボタンがあるので、どちらかのリージョンのボタンを押下します。
以下のようにCloudformationのスタック作成画面に遷移し、リポジトリで定義されていたLambda関数をデプロイするCloudformationテンプレートがプリセットされています。なお、リージョンは右上でデプロイしたいリージョンに変更してください。問題なければ、Nextで次に進みます。
ParametersのAPI keysには下記のTavilyからAPIキーを発行し、キー情報を入力します。
次のページではデフォルトのまま、最後のチェックボックスにチェックを入れ、次に進みます。
次のレビュー画面で内容を確認をしたら、Submitを押下します。
CloudformationのStacks一覧画面に戻ります。暫く経つと、リソースの作成が完了します。
サブエージェントの構築
まずはそれぞれのエージェントの作成をAmazon Bedrock上で行います。
activity_finderの作成
Amazon Bedrockのページにて、左のメニューからAgentsを開き、「Create agent」を押下します。
まずはactivity_finderを作成します。Nameにactivity_finder
を入力し、Createを押下します。
すると、Agent builderというページが表示されます。このページにてAI Agentの定義を行うことができます。
Agent details
というボックス内にあるInstructions for the Agent
にて、エージェントの動作をプロンプトとして定義することができます。
今回は下記のように設定します。
役割: Activity Finder
目標: 目的地で楽しめるアクティビティやイベントを調査し、旅行者の興味や年齢層に合ったものを見つける。
指示: 旅行者の具体的な希望や属性に合わせて、最適なアクティビティを選定する。
次にAction Groupを定義します。Action Groupというボックス内の右上のAddを押下します。
Action Groupを定義する画面に遷移します。
まず、Enter Action group nameにactions_activity_finder
という名前を入力します。
Action Group invocationという項目で先に定義したLambda関数を定義します。
Select Lambda functionから事前準備で作成したLambda関数であるweb_search
を選択します。
次にAgentからLambda関数に渡すParametersを設定します。
ここでは、以下の項目を設定します。
Name | Description | Type | Required |
---|---|---|---|
days | 検索する履歴の日数。最近のイベントやニュースを探す際に役立ちます。 | string | False |
search_query | Webを検索するためのクエリです。 | string | True |
target_website | 検索対象の特定のWebサイト(ドメイン名を含む)。指定がない場合、関連するWebサイトが使用されます。 | string | False |
topic | 検索するトピック。'news'または'general'を指定します。ニュースを対象とする場合に検索を絞り込むのに役立ちます。 | string | False |
設定が完了したら、他の設定は変更せず、Saveを押下後、Cancelを押下します。
完了したら、Agent builder画面で右上のSaveを押下し、そのあとその横にあるPrepareを押下します。
その後、Agentの概要画面に遷移しますので、Agentを公開するためにAgent概要画面の上にCreate Aliasというボタンがあるので、押下します。
任意のAlias nameを設定してCreate aliasボタンを押下します。
restaurant_scoutの作成
先程と同様にrestaurant_scoutも作成します。
Agent Builder画面ではInstructions for the Agentには下記を設定します。
役割: Restaurant Scout
目標: 目的地で高評価のレストランや食事体験を見つけること。また、景色の良い場所や楽しいアクティビティを推薦すること。
指示: 食通として、最高の食事体験ができるスポットを知っていること。また、旅行にちょっとした特別感を加える場所を選ぶのも得意であること。
次にAction Groupを設定します。
Action group nameにはactions_restaurant_scout
を設定します。
Action group invocationはactivity_finder
で設定した内容と同一の内容を設定します。
Action group functionの設定もactivity_finder
のときと同じです。
設定が完了したら、Saveを押下後、Cancelを押下します。
完了したら、Agent builder画面で右上のSaveを押下し、そのあとPrepareを押下します。
保存完了後、Agentを公開するために先ほど同様にaliasを作成します。
Itinerary_compilerの作成
先ほど同様にitinerary_compilerを作成します。
Agent Builder画面ではInstructions for the Agentに下記を定義します。
役割: Itinerary Compiler
目標: 調査したすべての情報をまとめ、フライトやホテル情報を統合した、日ごとの包括的な旅程を作成すること。ツールは利用できないため、自身の知識のみを活用して、各日の活動を適切に整理する。
指示: アクティビティやおすすめのレストランに関するすべての情報を整理し、旅行者が旅行を最大限楽しめるよう、日ごとの旅程を作成すること。
他の設定は変更せず、Action Groupも追加せず、保存します。
完了したら、Agent builder画面で右上のSaveを押下し、そのあとPrepareを押下します。
保存完了後、Agentを公開するために先ほど同様にaliasを作成します。
スーパーバイザエージェントの作成
次に先程作成したAgentをオーケストレーションするスーパーバイザエージェントを作成します。
先ほどと同様にAgents作成画面にてCreate agent
を押下します。
Nameにはtrip_plannner
を入力します。また Enable multi-agent collaborationにチェックを入れて、Create
を押下します。
Instructions for the Agent
に下記の説明を追加します。
旅行プランナーとして、あなたは専門家たち(activity_planner、restaurant_scout、itinerary_compiler)を活用して、アクティビティを計画したり、良いレストランを見つけたりします。
また、それらをすべて明確な計画としてまとめるための旅程を作成します。
次にエージェントコラボレーションの設定を行います。
一番下の「Multi-agent collaboration」のEditボタンを押下します。
Multi-agent collaborationの設定はオンにし、Collaboration configurationではSupervisor
を選択します。
左下にあるAdd Collaborator
を押下してAgentを追加します。
コラボレーションするAgentを定義するボックスが追加されるので、ここで先ほど定義したAgentを順番に登録していきます。
Collaborator agentとして、activity_finder
を設定します。Collaborator nameはagentと同じ名前を設定します。Collaborator Instructionにはこのマルチエージェントにおける役割を記載します。本当は詳細にエージェントを役割を記述するのが望ましいですが、今回は簡素化したものにします。
Collaboratorとして以下の3つを追加し、設定します。
Collaborator Agent | Agent Alias | Collaborator Name | Collaborator Instruction |
---|---|---|---|
activity_finder | 自身で設定したalias名 | activity_finder | 旅行のためのアクティビティを決定する際は、activity_finder を使用します |
restaurant_scout | 自身で設定したalias名 | restaurant_scout | 旅行のためのレストランを見つけるためには、restaurant_scout を使用します |
itinerary_compiler | 自身で設定したalias名 | itinerary_compiler | アクティビティと食事を含む旅行全体の旅程をまとめるためには、itinerary_compiler を使用します |
完了したら、ページ上部のSaveを押下します。
最後にAgent builderのページ上部のSave and exitを押下したら設定が完了です。
動作確認
作ったマルチエージェントを試してみましょう。旅行アドバイザなので、来年のre:Inventに行ったときの旅行プランを提案してもらうことにします。
trip_plannerのAgent画面の右側にテスト用のチャット画面があるので、ここで質問をしてみます。
Las Vegasの観光プランを作って
と聞いてみます。すると、より詳細な旅行情報を聞いてきます。
ここで単に後続のエージェントに要求を渡すのではなく、要求を明確にするためにより詳細な情報について質問をしてくれます。
詳細な滞在情報や興味のあることを答えてみます。
すると、少し時間が経ったあとに、詳細な旅行プランを作成してくれました。
回答してくれた内容は以下のとおりです。
ラスベガス2日間の自然を楽しむ観光プランをご提案させていただきます:
1日目:グランドキャニオン ウエストリム日帰りツアー
- 朝6時:Peppermill Restaurantで朝食
- 朝7時:ツアー出発(ホテルピックアップ)
- 午前11時:グランドキャニオン到着、絶景ポイント観光
- 午後1時:ランチ(ツアーに含まれます)
- 午後4時:ラスベガスへ出発
- 夜8時:Battista's Hole in the Wallでディナー(イタリアン)
2日目:レッドロックキャニオン
- 朝8時:Blueberry Hill Restaurantで朝食
- 午前9時半:レッドロックキャニオンへ出発
- 午前10時:レッドロックキャニオン観光(ハイキング、ドライブ)
- 午後1時:ピクニックランチ
- 午後4時:ラスベガスへ戻る
- 夜7時:Lotus of Siamでディナー(タイ料理)
予算配分の目安:
- グランドキャニオンツアー:約300ドル
- レッドロックキャニオン入場料:15ドル
- 食事代:1食20-40ドル
- 交通費:レンタカーまたはライドシェア
12月は寒くなる可能性があるので、暖かい服装をご持参ください。グランドキャニオンツアーは人気のため、早めの予約をお勧めします。
かなり綿密なプランを練ってくれました。
さて、上記のような回答をどのようにエージェントが協力し合って作成されたのかを見てみましょう。
回答の右下にある、Show trace
をクリックします。
すると、以下のように左側にチャット画面、右側にTrace情報が表示されます。
右上のタイムラインから要求をSupervisorを受け付けたあと、まずrestaurant_scoutとactivity_finderを同時に呼び出して、2つのエージェントの処理が完了したあと、itinerary_compilerが呼び出されていることがわかります。
加えて、その下のTraceというところでは、より詳細なエージェントのステップのトレース情報を見ることができます。
例えば、ステップ1の中身を見てみると具体的に内部でどのような動作をしているのかがわかります。
ログの詳細の説明は割愛しますが、中身はJSON形式になっており、一階層目は下記の項目になっております。なお、それぞれの意味は公式のドキュメントに記述がなかったため、一部推測を含みます。
-
agentId:
エージェントの一意の識別子 -
callerChain:
エージェントの呼び出し連鎖を示す配列 -
approximateTime:
リクエストの処理時刻 -
modelInvocationInput:
Supervisor LLMへの入力パラメータ
推論設定、システムプロンプト、会話履歴など -
modelInvocationOutput:
Supervisor LLMへからの出力結果
トークン使用量、応答内容 -
rationale:
エージェントの思考プロセスや判断根拠
トレースIDと説明テキスト -
invocationInput:
エージェントへの入力リクエスト情報 -
observation:
各サブエージェントからの応答結果
大まかな流れとしては、まずSupervisorのLLMに対して、ユーザとの会話を含むプロンプトをリクエストして、どのエージェントに何を要求するのかをStructured Output形式で取得し、そのOutputをもとに各エージェントへのリクエストを送信、各エージェントからのアウトプットをもらうような流れになっています。
以上のような形でマルチエージェントがどのように動作しているのかを詳細に確認することができました。
備考 - ヒエラルキカル(階層)パターンの検証
ヒエラルキカルパターンが実装できるのかを試すためにもう一つSupervisorとサブエージェントを作成し、trip_plannnerのエージェントの一つとして紐づけてみました。
すると、保存時に下記のようなエラーとなり、設定することができず、現状では、ヒエラルキカルパターンについてはサポートしていないものと思われます。
試したみた上で感じた現状の課題
簡単にではありますが、Amazon Bedrockのマルチエージェントコラボレーション機能について動かしてみました。構築自体そこまで難しくなく、今後本機能を活用するAIエージェントユースケースが増えていくだろうと思いました。
一方で、課題として上にまとめたように要件に応じて、他のマルチエージェントアーキテクチャパターンを採用したほうがいいケースもあり、他のオープンソースが提供しているようなパターンもサポートされるとさらに使いやすくなるのではないかと感じました。
加えて、マルチエージェント構成になるとBedrockのAgentsリストに大量のエージェントが並んでしまい、どれが一つのマルチエージェントのグループなのか、どれがスーパーバイザでどれがサブエージェントなのか判別がしづらいと思いました。このあたりのUI/UX面でも今後アップデートがあればより使いやすくなるのではと思いました。
まとめ
まだ、プレビュー版なので、これから機能拡張や最適化も進んでいくはず。自社サービスや開発プロジェクトで「一つのエージェントにタスクが増えて管理がしづらい…」と悩んでいるなら、一度この新機能をチェックしてみてはいかがでしょうか?