仕事上、これまで生成 AI の基礎学習のトレーニングや、Amazon Bedrock の API を使用してシンプルな生成 AI アプリケーションを構築するトレーニングを数多く実施してきましたが、最近では「AI エージェント」に関するトレーニングを実施する機会が増えてきました。
そのため、Kiro や Amazon Quick、Claude Code などの完成形の AI エージェントを使いこなすためのカリキュラムや、AI エージェントそのものを構築するカリキュラムを数多く検討しています。
その中で、AI エージェントの構築というトピックにおいては、「マルチエージェントの設計」というところが最近では無視できなくなってきたと感じています。
これまでは「いかに(単体の)エージェントを構築するか」という観点だけにフォーカスしてきましたが、本格的な AI エージェントの環境を実装する上では、どのような役割分担で、どのように複数のエージェントを連携させるか、という観点も必要になります。
マルチエージェントの技術や設計・実装には様々なものがありますが、今回は Strands Agents SDK のドキュメントに記載されている 3つのマルチエージェントのパターン(Workflow / Graph / Swarm)にもとづきサンプルを作成してそれぞれの違いをまとめていきます。
strandsagents.com
なお、この記事に記載している内容は、2026年8月に検証した結果に基づいています。
マルチエージェントとは / 3 パターンの位置づけ
まず、マルチエージェントシステムとは何かを簡単に整理しておきます。
これは、1 つのエージェントだけでは複雑すぎたり大きすぎたりするタスクを、複数の自律的なエージェントが協力して解決するための仕組みです。Strands Agents SDK の公式ドキュメントでは、その要点として次の 3 つが挙げられています。
| 要点 |
内容 |
| オーケストレーション(Orchestration) |
エージェント間の情報やタスクの流れを管理する制御構造 |
| 専門化(Specialization) |
各エージェントが特定の役割・専門性と、使えるツールを持つこと |
| 協調(Collaboration) |
エージェント同士が情報を共有し、互いの成果を踏まえて作業を進めること |
そして Strands Agents SDK では、このオーケストレーションのやり方として Workflow / Graph / Swarm という 3 つのパターンが用意されています。
この 3 つ、名前だけ見ると「どれも複数エージェントを動かすもの」で似ているように感じますが、一番の違いは「実行の経路(次にどのエージェントが動くか)を誰が決めるのか」 という点にあります。ここさえ押さえておけば、3 つのパターンの使い分けはかなりスッキリ理解できると思います。
| パターン |
経路を決めるのは |
内容 |
| Workflow |
開発者 |
実行する順番をコードで固定します |
| Graph |
開発者 |
ノード(エージェント)とエッジ(依存関係)で経路を定義します |
| Swarm |
エージェント自身 |
次に誰へ渡すかを自律的に判断します(ハンドオフ) |
重要な区別:3 つは「同じ立ち位置」ではない
ここで、もう 1 つ押さえておきたい大事な点があります。それは、この 3 つは SDK での提供のされ方が違う ということです。私も最初は「3 つとも SDK の機能なんだろう」と思っていたのですが、公式ドキュメントを読むと、実はそうではありませんでした。
- Graph と Swarm は、SDK 本体(
strands パッケージ)が提供する 組み込みのオーケストレーターです。公式ドキュメントでも "built-in SDK orchestrators" と表現されており、strands.multiagent から GraphBuilder や Swarm をインポートすれば、実行制御や状態共有、ハンドオフといった面倒な部分を SDK が担ってくれます。
- 一方で Workflow は、専用のオーケストレーターがあるわけではなく、エージェントをコードで連結して自分で実装する「設計パターン」 です。公式ドキュメントでも "a pattern you implement in code by chaining agents together" と説明されています。
つまり「Strands Agents SDK のマルチエージェント機能」と一括りにされがちですが、Graph・Swarm は SDK の機能そのもの、Workflow はコードで組む設計パターン、という違いがあるわけですね。
なお、Workflow については、strands-agents-tools(strands_tools)パッケージに、タスクの依存解決や並列実行を自動化してくれる workflow ツールも用意されています。今回のサンプルでは、パターンの本質を理解しやすいように、あえて素の Agent をコードで連結する方式にしています。
3 パターンの比較
ここまでの内容を表にまとめると、次のようになります。
| |
Workflow |
Graph |
Swarm |
| 経路を決めるのは |
開発者(コードで固定) |
開発者(ノード+エッジで定義) |
エージェント自身(自律的にハンドオフ) |
| 実行の性質 |
決定的・逐次 |
決定的(依存のないノードは並列) |
創発的・自律的 |
| ループ |
なし |
あり(条件付きエッジで可能) |
あり |
| SDK での提供のされ方 |
設計パターン(別途 workflow ツールあり) |
組み込みオーケストレーター |
組み込みオーケストレーター |
| 実装の入口 |
素の Agent をコードで連結 |
GraphBuilder |
Swarm |
この「経路を誰が決めるか」という軸を頭に入れたうえで、次の章から実際に 3 つのパターンを 1 つずつ試していきます。
共通の題材とモデル
3 つのパターンは、それぞれ得意なことも書き方も違います。ただ、いきなり別々の題材で説明してしまうと「何がどう違うのか」が見えにくくなってしまいます。
そこで今回は、3 つとも同じ題材を実装して比べる ことにしました。題材はシンプルに、あるトピックについて次の 3 ステップで処理するものです。
- 調査(researcher) … トピックの重要な事実を挙げる
- 分析(analyst) … 調査結果から示唆を導く
- まとめ(writer) … 内容を短くまとめる
今回のトピックには、 「観葉植物を室内に置くこと」 を使います。この「調査 → 分析 → まとめ」という同じ流れを、Workflow / Graph / Swarm の 3 通りで書いてみることで、「同じ仕事でも、経路の決め方がこんなに違う」 という部分に注目できるようにしています。
使用するモデル
使用するモデルは、すべて Amazon Nova Lite 1.0 v1(us.amazon.nova-lite-v1:0)にしました。トレーニングで使うハンズオン環境での実行を考慮した選定ですが、他のモデルでも動作するので、必要に応じて変更してください。
3 つのサンプルとも、次のように共通の BedrockModel を定義して使い回しています。
from strands import Agent
from strands.models.bedrock import BedrockModel
model = BedrockModel(
model_id="us.amazon.nova-lite-v1:0",
region_name="us-west-2",
temperature=0.3,
)
エージェントは、この model を渡しつつ、system_prompt で役割だけを変えて作成します。例えば調査担当(researcher)は次のようなイメージです。
researcher = Agent(
model=model,
system_prompt="あなたは調査担当です。トピックの重要な事実を箇条書きで3つ挙げてください。",
)
なお、今回のサンプルコードは以下の GitHub リポジトリにまとめています。記事中ではポイントとなる部分だけを抜粋して紹介するので、全体のコードはこちらを参照してください。
StrandsAgentsBasic/multiagent at main · tetsuo-nobe/StrandsAgentsBasic · GitHub
それでは、次から実際に 3 つのパターンを 1 つずつ試していきます。
パターン 1:Workflow
最初は、一番シンプルな Workflow です。
Workflow は、開発者がコードで実行順序を固定する パターンです。前の章で触れたとおり、これは SDK の専用オーケストレーターではなく、素の Agent をコードで順番に呼び出していく「設計パターン」になります。「1 番目のエージェントを呼んで、その結果を 2 番目に渡して、さらにその結果を 3 番目に渡す」——それをそのまま Python のコードとして書くだけです。
コード
まず、役割の違う 3 つのエージェントを用意します。それぞれ system_prompt で役割を変えているだけで、モデルは共通です。ここでは、後で結果だけをまとめて表示したいので callback_handler=None を指定し、途中のストリーム出力を抑制しています。
researcher = Agent(
model=model,
system_prompt="あなたは調査担当です。トピックの重要な事実を箇条書きで3つ挙げてください。",
callback_handler=None,
)
analyst = Agent(
model=model,
system_prompt="あなたは分析担当です。与えられた調査結果から、最も重要な示唆を1つ導いてください。",
callback_handler=None,
)
writer = Agent(
model=model,
system_prompt="あなたは執筆担当です。与えられた分析を3行以内の日本語でまとめてください。",
callback_handler=None,
)
そして、実行の順序は次のように コードで固定 します。ここがこのパターンの肝です。あるエージェントの出力を、次のエージェントの入力として 自分で手渡し している点に注目してください。
def run_workflow(topic: str) -> str:
research = researcher(f"次のトピックを調査してください: {topic}")
analysis = analyst(f"次の調査結果を分析してください:\n{research}")
summary = writer(f"次の分析をまとめてください:\n{analysis}")
return str(summary)
run_workflow("観葉植物を室内に置くこと")
見てのとおり、「次にどのエージェントを呼ぶか」は完全にコードで決まっています。LLM が経路を判断する余地はなく、分岐もループもありません。だからこそ、毎回まったく同じ順序で実行される(決定的) という安心感があります。
実行結果
実際に実行すると、researcher → analyst → writer の順に処理が進み、各ステップの出力が得られます。
--- ステップ1: 調査結果 ---
観葉植物を室内に置くことに関する重要な事実を以下に挙げます。
1. **ストレス軽減と心理的利点**:
(以下略)
--- ステップ2: 分析結果 ---
調査結果から最も重要な示唆を1つ導くならば、**「観葉植物は心理的健康と室内の空気浄化の両面で重要な役割を果たす」**ということです。
(以下略)
--- ステップ3: 最終まとめ ---
観葉植物は心理的健康と室内の空気浄化の向上に役立つことが示唆されました。空気浄化とストレス軽減が相乗効果を生み、生活の質を高めます。観葉植物は単なる装飾ではなく、健康と快適さを総合的に向上させる手段です。
Workflow のポイント
Workflow は、とにかく コードを読めば挙動がそのままわかる のが良いところだと思います。処理の順序が決まっている定型作業を、複数のエージェントで分担させたいときには、これが一番シンプルで確実です。
一方で、「調査は複数の観点で並列にやりたい」「状況によって次の担当を変えたい」といった要求が出てくると、手続きコードだけでは少しずつ複雑になっていきます。そこで登場するのが、次の Graph です。
パターン 2:Graph
次は Graph です。
Graph は、開発者がノードとエッジで経路を定義する パターンです。エージェントを「ノード」として登録し、その間の依存関係を「エッジ」でつなぎます。Workflow との大きな違いは 2 つあります。
- 出力の受け渡しを自分で書かなくてよい。エッジに沿って、あるノードの出力が次のノードの入力へ自動的に伝わります。
- 依存関係のないノードは並列で実行される。
この「並列実行」を体感するために、ここでは調査を 利点担当(positive) と 課題担当(negative) の 2 つに分け、両方が終わってから まとめ担当(writer) が合流する形にしてみます。図にすると次のようなイメージです。
positive ─┐
├─▶ writer
negative ─┘
コード
グラフの組み立ては GraphBuilder で行います。ノードを登録し、エッジで依存関係を宣言するだけです。
from strands.multiagent import GraphBuilder
builder = GraphBuilder()
builder.add_node(positive, "positive")
builder.add_node(negative, "negative")
builder.add_node(writer, "writer")
builder.add_edge("positive", "writer")
builder.add_edge("negative", "writer")
graph = builder.build()
result = graph("観葉植物を室内に置くこと")
ポイントは、positive と negative のどちらにもエッジが向いていない(=依存がない)ため、この 2 つは並列で実行される点です。そして両方に対して writer へのエッジが張られているので、2 つの調査が揃ってから writer が動きます。Workflow のときのように「researcher の結果を analyst に手で渡す」といったコードは、どこにも書いていないことに注目してください。
実行結果
実行後、result.execution_order で実行順序を確認できます。
--- 実行順序 ---
['positive', 'negative', 'writer']
--- 最終まとめ(writer ノードの出力)---
観葉植物を室内に置くことは、空気の浄化や心理的利点がある一方、光や湿度の管理が課題となる。適切な環境を提供することが植物の健康に重要である。
positive と negative(並列)→ writer(合流)という、宣言したグラフどおりの順序になっています。最終的なまとめは result.results["writer"].result から取り出せます。
Graph のポイント
Graph は、処理の流れを「構造」として宣言できる のが魅力だと思います。依存のない処理は自動で並列化されますし、条件付きのエッジを使えば分岐やループも表現できます。「業務プロセスのように、経路がある程度決まっているが並列や分岐も入る」というケースに向いていると感じました。
ただし、Workflow も Graph も 経路を決めるのは開発者 です。「経路そのものをエージェントに任せたい」という場合は、最後の Swarm が候補になります。
パターン 3:Swarm
最後は Swarm です。
Swarm は、これまでの 2 つと決定的に違います。Workflow も Graph も経路を決めるのは開発者でしたが、Swarm では 経路をエージェント自身が決めます。開発者がやることは、専門性の違うエージェントを「集まり(プール)」として渡すだけ。あとは各エージェントが「この先は自分より適任がいる」と判断すると、handoff_to_agent というツールで次の担当に制御を渡していきます(ハンドオフ)。
コード
エージェントには description を付けておきます。これは、他のエージェントが「誰にハンドオフすべきか」を判断するための手がかりになります。
researcher = Agent(
name="researcher",
model=model,
description="トピックの事実や情報を調査する担当",
system_prompt=(
"あなたは調査担当です。トピックの重要な事実を簡潔に挙げてください。"
"調査が済んだら analyst にハンドオフしてください。"
),
callback_handler=None,
)
そして Swarm 本体は、エージェントのリストを渡すだけ です。Graph のようなエッジも、Workflow のような呼び出し順のコードも書きません。
from strands.multiagent import Swarm
swarm = Swarm(
[researcher, analyst, writer],
entry_point=researcher,
max_handoffs=10,
max_iterations=10,
)
result = swarm("観葉植物を室内に置くこと")
entry_point で最初の担当だけは指定していますが、そこから先に誰へ渡すかはエージェントが自律的に判断 します。順序を保証するコードはどこにもありません。
実行結果
誰がどの順で担当したかは、result.node_history で確認できます。
--- ハンドオフの履歴(誰がどの順で担当したか)---
['researcher', 'analyst', 'writer']
--- 最終まとめ(writer の出力)---
<thinking>これまでの調査結果と分析を基に、観葉植物を室内に置くことに関する示唆を文章にまとめる必要がある。他エージェントへのハンドオフは不要。</thinking>
観葉植物を室内に置くことは、室内空気の質を改善し、ストレスを軽減する可能性がありますが、適切な管理が必要です。
このケースでは researcher → analyst → writer ときれいに流れましたが、これは開発者が固定した結果ではなく、エージェントたちが判断した結果 である点が Workflow・Graph との根本的な違いです。
Swarm のポイント
Swarm は、経路をあらかじめ決められない、あるいは決めたくないタスク に向いていると感じました。探索やブレインストーミング、複数の専門家の視点を持ち寄って結論を出すような場面では、この「自律的にハンドオフする」性質が活きてきます。
一方で、経路がエージェント任せになる分、挙動は毎回同じとは限りません。実際、今回のように小さめのモデル(Amazon Nova Lite)を使うと、担当が行ったり来たりして node_history が researcher → analyst → writer → analyst → writer のように揺れることもありました。「毎回きっちり同じ順序で流したい」のであれば、経路を固定できる Graph の方が適しています。この揺らぎ自体が、Swarm の「創発的(emergent)」という性質を体感できる部分でもあります。
3 パターンの比較と使い分け
ここまで 3 つのパターンを実際に動かしてきました。改めて、違いを表で整理しておきます。
| |
Workflow |
Graph |
Swarm |
| 経路を決めるのは |
開発者(コードで固定) |
開発者(ノード+エッジで定義) |
エージェント自身(自律的にハンドオフ) |
| 実行の性質 |
決定的・逐次 |
決定的(依存のないノードは並列) |
創発的・自律的 |
| ループ |
なし |
あり(条件付きエッジで可能) |
あり |
| SDK での提供のされ方 |
設計パターン(別途 workflow ツールあり) |
組み込みオーケストレーター |
組み込みオーケストレーター |
| 実装の入口 |
素の Agent をコードで連結 |
GraphBuilder |
Swarm |
こうして並べてみると、やはり 一番の判断軸は「実行の経路を誰が決めるか」 だと感じます。ここを起点に考えると、使い分けはシンプルに整理できます。
- 決まった順序どおりに、シンプルに処理したい → Workflow
処理の流れが固定で、素直にコードで書けるなら、これが一番読みやすく確実です。
- 経路は決まっているが、並列や分岐・ループも入れたい → Graph
業務プロセスのように「流れはあるが、途中で並列化や条件分岐が必要」というケースに向いています。
- 経路を事前に決められない/決めたくない → Swarm
探索やブレインストーミング、複数の専門家の視点を持ち寄って結論を出すような、経路が読めないタスクに向いています。
言い換えると、「決定的にしたいか、それとも創発的(自律的)にしたいか」 が選択の大きな分かれ目になります。Workflow と Graph は開発者が経路をコントロールする決定的なアプローチ、Swarm はエージェントに任せる創発的なアプローチ、というわけですね。
なお、今回はどれも「調査 → 分析 → まとめ」という同じ題材で試しましたが、実際のアプリケーションでは、この 3 つを組み合わせて使うこともできます。例えば Graph のノードの 1 つとして Swarm を配置する、といった入れ子構成も可能です。まずはそれぞれの特性を押さえたうえで、解きたい問題に合わせて選んでいくのがよいと思います。
注意点
実際に試してみて気づいた点や、事前に知っておくとよい点をいくつかまとめておきます。
Swarm は経路が毎回同じとは限らない
Swarm は経路をエージェント自身が決めるため、同じ入力でも実行のたびに担当の順序が変わることがあります。特に今回のように小さめのモデル(Amazon Nova Lite)を使うと、担当が行ったり来たりして node_history が長くなることもありました。
「毎回きっちり同じ順序で流したい」という要件があるなら、無理に Swarm を使わず、経路を固定できる Graph(や Workflow)を選ぶ方が確実です。なお Swarm には、こうした無限ループやピンポン状態を防ぐための安全装置として、max_handoffs(ハンドオフの上限)や max_iterations(総反復回数の上限)といったパラメータが用意されています。タスクの複雑さに応じて調整しておくとよいでしょう。
実行結果はそのまま print すると冗長になる
Graph や Swarm の実行結果オブジェクトを print(result) でそのまま出力すると、各エージェントのメトリクスやトークン使用量などの内部情報まで一気に表示され、かなり読みにくくなります。
最終的な出力だけを見たい場合は、次のように必要な部分だけを取り出すのがおすすめです。
print(result.results["writer"].result)
あわせて、各エージェントに callback_handler=None を指定しておくと、途中のストリーム出力(<thinking>...</thinking> やツール呼び出しの経過)が抑制され、結果だけをすっきり確認できます。
Workflow は「オーケストレーター」ではない
これは冒頭でも触れましたが、あらためて注意点として挙げておきます。Graph・Swarm は SDK 組み込みのオーケストレーターですが、Workflow は SDK の専用機能ではなく、コードでエージェントを連結する設計パターンです。
そのため「Workflow クラスをインポートする」といった使い方はしません。ただし、タスクの依存解決や並列実行を自動化したい場合は、strands-agents-tools(strands_tools)パッケージの workflow ツールを利用するという選択肢もあります。
モデルやリージョンは環境に合わせて変更する
今回のサンプルはモデルに us.amazon.nova-lite-v1:0、リージョンに us-west-2 を指定しています。実行する環境によっては、モデルの有効化(Amazon Bedrock のモデルアクセス設定)やリージョンの変更が必要になります。手元の環境に合わせて BedrockModel の設定を調整してください。
最後に
今回は、Strands Agents SDK のマルチエージェントの 3 パターン、Workflow / Graph / Swarm を、同じ「調査 → 分析 → まとめ」という題材で実装して比べてみました。
最初は「どれも複数エージェントを動かすもの」で似たようなものだと思っていたのですが、実際に手を動かしてみると、一番の違いは「実行の経路を誰が決めるか」 にある、というのがはっきり実感できました。
- Workflow … 開発者がコードで順序を固定する(設計パターン)
- Graph … 開発者がノードとエッジで経路を定義する(並列や分岐も表現できる)
- Swarm … エージェント自身が自律的に経路を決める(創発的)
そして、Graph・Swarm は SDK 組み込みのオーケストレーターである一方、Workflow はコードで組む設計パターンである、という立ち位置の違いも整理できました。
パターン選びで迷ったときは、「経路を決定的にしたいか、それとも創発的にしたいか」 をまず考えるとよいと思います。決まった流れを確実に回したいなら Workflow や Graph、経路が読めない探索的なタスクならば Swarm、という具合ですね。
今回はそれぞれを単独で試しましたが、Graph のノードに Swarm を入れ子にするなど、組み合わせて使うこともできます。今後はもう少し実践的なテーマで、これらのパターンを組み合わせた構成にも挑戦してみたいと思います。
なお、今回使用したサンプルコードは以下の GitHub リポジトリにまとめています。よければ手元でも動かしてみてください。
StrandsAgentsBasic/multiagent at main · tetsuo-nobe/StrandsAgentsBasic · GitHub