Microsoft FoundryでParallel Fan-Out/Gather Patternを実装する ― 株式分析を題材にMulti-Agentを試す


⚠️ 本記事ではMulti-Agentのプロトタイプ設計・実装を検証するため、株式分析をユースケースの一例として扱います。掲載する分析内容は特定の金融商品の売買を推奨するものではありません。

Keywords: Multi-Agent, Parallel Fan-Out/Gather, Microsoft Foundry, Parallel Execution, Multimodal, Final Synthesizer, Deterministic Orchestration

こんにちは、Lena Nadir Blog 管理人です。

AI Agentを1体作ること自体は、それほど難しいものではなくなってきました。

一方、複数のAgentを組み合わせるMulti-Agentでは、

  • どこまで役割を分離するのか
  • SequentialとParallelのどちらで実行するのか
  • RouterやSupervisorは必要なのか
  • どこで結果を集約し、どこで意味づけするのか

といったオーケストレーション設計が重要になります。

そこで今回は、Microsoft Foundryを使って Parallel Fan-Out/Gather Pattern を実装しました。

ユースケースとして使用するのは、複数の専門的視点から個別株を分析し、2〜3カ月程度のスイングトレードにおけるEntry Pointを評価するケースです。AIに株価そのものを予測させることが目的ではありません。

トレードのEntry判断では、企業業績や成長性を見るFundamental Analystと、現在の価格位置やPrice Actionを見るRisk Analystを、それぞれ独立して評価できます。そして最後に、両者の結果を1つの判断材料として統合する必要があります。 他にも短期的な需給など考慮すべき要素はありますが、今回はユースケースを複雑にしすぎないよう、この2つの視点に絞ります。
このように、互いに依存しない複数の分析を並列に実行し、その結果を最後に統合する構成は、Parallel Fan-Out/Gather Patternと相性が良いと考えました。

異なる専門領域を複数のAgentへ分離し、それぞれを並列実行した後、結果を1つの意思決定材料へ統合する。

本記事では、まずこのデザインパターンを選んだ理由を整理した上で、Microsoft FoundryとPythonによる実装、実際の出力結果まで確認していきます。

🧩 Parallel Fan-Out/Gather Patternとは?

Google Developers Siteでは、Multi-Agent Systemの代表的な設計パターンとして、Sequential Pipeline、Coordinator / Dispatcher、Parallel Fan-Out/Gather、Hierarchical Decomposition、Generator & Critic、Iterative Refinementなどが紹介されています。

代表的なデザインパターンの違いを理解しやすくするために、筆者が以下のように整理しました。

Multi-Agentデザインパターンを、タスク間の依存性とフローの動的性の2軸で整理した図。縦軸は「動的・反復的(実行中に判断する)」と「固定的・決定論的(フローを事前定義する)」、横軸は「依存性が高い(前工程の結果を使う)」と「独立性が高い(同時に処理できる)」。左上にGenerator & CriticとIterative Refinement、右上にCoordinator / DispatcherとHierarchical Decomposition、左下にSequential Pipeline、右下に今回採用するParallel Fan-Out / Gatherを配置し、強調している。右側には今回選ぶパターンとしてParallel Fan-Out / Gatherの図解と、各タスクが独立実行できる・毎回すべての視点を使う・異なる専門的視点を同時に取得し最後に1つにまとめる、という3つの採用理由が添えられている。

今回は、Fundamental AnalystとRisk Analystを独立して並列実行し、最後に結果を統合する Parallel Fan-Out/Gather Pattern を採用しました。 2つのAgentは毎回必ず実行するため、Routerは置いていません。

独立した複数の専門分析を並列実行し、最後に結果を統合する

🏗️ アーキテクチャとコンポーネントの役割

今回のマルチエージェント構成は、以下の5つのコンポーネントで構成します。

Multi-Agentアーキテクチャ図。User Prompt(Ticker・Daily Chart Screenshot・Focus)がPython Orchestratorへ渡り、Fan-OutでFundamental AnalystとRisk Analystへ並列に処理が展開される。両者の出力はGatherで回収され、Final Synthesizerが統合してReportとして出力する。

各コンポーネントの役割は以下の通りです。

  • Fan-Out

    • Python Orchestratorが1つのInputをFundamental AnalystとRisk Analystへ展開し、2つを並列実行する
  • Fundamental Analyst

    • 銘柄コードをもとに公開情報を調査する
    • 直近決算、売上・利益、EPS、収益性、バリュエーション、Guidanceを確認する
    • 2〜3カ月程度で株価を動かし得るCatalystを整理する
    • 企業側から現在の投資環境を評価する
    • 公開情報の取得にはGrounding with Bing Searchを利用する
  • Risk Analyst

    • ユーザーが入力した日足チャートのScreenshotを分析する
    • Trend、Support / Resistance、直近高値・安値、出来高、現在の価格位置を確認する
    • 2〜3カ月のSwing Tradeを想定したRisk / Rewardを評価する
    • 市場・Price Action側からEntry Riskを評価する
    • 企業業績は参照せず、チャート画像に限定して判断する
  • Gather

    • 並列実行された2つのAgentの出力をPython側で回収する
    • この段階では、まだ結果の解釈や結論づけは行わない
  • Final Synthesizer

    • GatherされたFundamental AnalystとRisk Analystの結果を受け取る
    • 両者の共通点、相違点、Catalyst、Riskを整理する
    • Fundamental SetupとChart Setupを踏まえて、1つの分析レポートへ統合する

Agent同士は直接連携せず、Python Orchestratorを介して結果を集約します。

📥 Input Data

今回の検証にはフジクラを使用しました。

入力データはJSONで定義しています。

{
  "ticker": "5803.T",
  "company_name": "フジクラ",
  "market": "東証プライム",
  "chart": "./input/5803-Fujikura_20260825.png",
  "focus": "2〜3カ月程度のスイングトレードを想定した場合、現在はEntry Pointとして魅力的か?"
}

Risk Analystへ渡した画像データ(Screen-shot)は、スマートフォンの株価アプリから取得した日足チャートです。

Risk Analystへ入力した5803(フジクラ)の日足チャート。スマートフォンの株価アプリのScreenshotで、現在値5,320円、直近高値7,068円、直近安値3,665円、5日/25日/75日程度の移動平均線、出来高が確認できる。

今回は、株価APIから日足データを取得したり、RSIやMACDなどのテクニカル指標をPython側で計算したりはしていません。理由は、指標ベースのテクニカル分析を自動化するのではなく、Fundamental Analystは公開情報、Risk Analystは日足チャートから得られる情報をもとに、それぞれ独立した視点でEntry Pointを評価する設計 にしたためです。

もちろん、テクニカル指標をAgentへ入力し、より定量的な売買判断を行うトレーディングスタイルも考えられます。今回はMulti-Agentの役割分担を明確にするため、そのアプローチは採用していません。

🧪 Experiment

ここから、Microsoft Foundry上に作成した3つのAgentをPythonから呼び出して実行します。

今回使用した主なライブラリは以下です。

agent-framework-core
agent-framework-foundry
azure-identity
pydantic>=2.0
python-dotenv

Python側では、Microsoft Foundryへ接続するClientを作成し、Fundamental Analyst、Risk Analyst、Final Synthesizerを初期化します。

# Microsoft Foundryプロジェクトへ接続するClientを1つ作成し、
# 3つのAgentすべてで使い回す
client = agents.build_client()

# Foundry上に定義済みの3つのAgentを、それぞれの役割ごとに初期化する
fundamental_agent = agents.build_fundamental_agent(client)   # 企業のファンダメンタルズを分析
risk_agent = agents.build_risk_agent(client)                 # チャート画像からリスク・Entryを分析
synthesizer_agent = agents.build_synthesizer_agent(client)   # 2つの分析結果を1つのレポートに統合

Fundamental AnalystとRisk Analystを並列実行する

今回のParallel Fan-Out/Gather Patternで中心になるのが、2つのSpecialist Agentの並列実行です。

Pythonでは asyncio.gather() を使い、Fundamental AnalystとRisk Analystを同時に呼び出しています。

import asyncio

# asyncio.gather()に2つのコルーチンを渡すと、両方が並行に実行される。
# 片方の完了を待ってからもう片方を呼ぶSequentialな実装とは異なり、
# ここがまさにParallel Fan-Outにあたる部分
fundamental_result, risk_result = await asyncio.gather(
    # Fundamental Analystへ銘柄・分析観点・企業名・市場を渡し、レスポンスを待つ
    agents.run_fundamental_analyst(
        fundamental_agent,
        ticker,
        focus,
        company_name,
        market,
    ),
    # Risk Analystへ同じ観点に加え、日足チャート画像のパスも渡してレスポンスを待つ
    agents.run_risk_analyst(
        risk_agent,
        ticker,
        str(chart_path),
        focus,
        company_name,
        market,
    ),
)
# 2つの呼び出しが両方完了すると、それぞれのレスポンスがタプルとして返る

実行ログ上でも、最初に2つのAgentが並列実行されます。

[1/3] Fundamental Analyst / Risk Analyst を並列実行中...

この処理が、今回のアーキテクチャにおける Fan-Out に相当します。

Fundamental AnalystへのInput

Fundamental Analystには、銘柄コード、企業名、市場、分析観点を渡します。

Ticker:
5803.T

Company Name:
フジクラ

Market:
東証プライム

Analysis Focus:
2〜3カ月程度のスイングトレードを想定した場合、
現在はEntry Pointとして魅力的か?

上記について、公開情報を調査して
Fundamental Analysisを行ってください。

Fundamental Analystは、Grounding with Bing Searchを使って公開情報を調査し、業績やバリュエーション、短期的なCatalystを分析します。

Risk AnalystへのInput

Risk Analystには、同じ分析観点に加えて日足チャートの画像を渡します。

Ticker:
5803.T

Company Name:
フジクラ

Analysis Focus:
2〜3カ月程度のスイングトレードを想定した場合、
現在はEntry Pointとして魅力的か?

添付の日足チャート画像を分析し、
Risk Analysisを行ってください。

画像から読み取れない情報は推測しないこと。

Risk Analystは企業業績には踏み込まず、チャート画像からPrice ActionやEntry Riskを分析します。

この2つのAgentは互いの出力を参照せず、独立して実行されます。

各Agentの出力結果をGather + Final Synthesizerへ渡す

asyncio.gather() が完了すると、Python側には2つの分析結果が fundamental_result, risk_result に格納されます。

その後、Fundamental AnalystとRisk Analystの結果をFinal Synthesizerへ渡し、1つのレポートへ統合します。

# Gatherした2つの結果をFinal Synthesizerへまとめて渡し、
# 共通点・相違点を解釈させたうえで1つのレポートへ統合する
final_report = await agents.run_synthesizer(
    synthesizer_agent,
    ticker,
    focus,
    fundamental_result,   # Fundamental Analystの分析結果
    risk_result,          # Risk Analystの分析結果
)

📊 実験結果

今回の実行では、Fundamental AnalystとRisk Analystから次の結果が得られました。

Agent評価主な判断
Fundamental AnalystPositive業績拡大、収益性改善、業績予想修正、光ファイバ・データセンター関連需要をポジティブに評価。一方、高い成長期待とバリュエーションはリスク
Risk AnalystNeutral3,665円から反発しているものの、5,000〜5,300円近辺はもみ合い。明確なDeep PullbackでもBreakoutでもなく、Risk / Rewardは中立的

そして、Final Synthesizerが実際に出力したレポートは以下の通りです。Final Synthesizerは、Fundamental: Positive / Chart: Neutral を統合し、最終的に Entry Setup: Neutral と評価しました。

# 5803.T(フジクラ)

**Entry Setup:** Neutral(Fundamental: Positive / Chart: Neutral)

ファンダメンタルズは業績拡大と上方修正傾向から強く、2〜3カ月でも注目度は高いです。
一方、チャートは5,300円近辺のもみ合い・上値抵抗帯にあり、現水準は低リスクの押し目でも明確なブレイクでもなく、
強い業績に対しエントリー妙味はやや中立です。


※研究・教育目的の分析であり、投資助言ではありません。

💰 1回あたりの実行コスト

3つのAgentについて、InstructionsやPromptの文字量からToken数を概算しました。

AgentInput目安Output目安
Fundamental Analyst約1,700 tokens約300〜600 tokens
Risk Analyst約1,200 tokens + 画像約300〜500 tokens
Final Synthesizer約2,500〜2,900 tokens約1,100〜2,300 tokens

全体では、

  • Input: 約6,000〜7,000 tokens
  • Output: 約2,500〜3,500 tokens

程度です。

今回使用したモデル価格を基にすると、モデル呼び出し部分は1回あたり約$0.06〜$0.10の概算になりました。

ただし、Fundamental AnalystではGrounding with Bing Searchを利用しているため、検索トランザクション料金は別途発生します。また画像入力のToken量も解像度によって変わります。

正確な金額はAzure / Foundry側のCost Managementで確認する必要があります。

⚠️ SDKにはExperimentalな部分も残る

実行そのものは正常に完了しましたが、Grounding with Bing Search周辺では、

PydanticSerializationUnexpectedValue

というWarningが複数表示されました。

また、

ExperimentalWarning:
RawFoundryChatClient.get_bing_grounding_tool is experimental

という警告も出ています。 PoCとしては問題なく動作しましたが、Foundry / Agent Framework周辺にはまだExperimentalなAPIもあります。 本番利用する場合には、SDKバージョンの固定やAPI変更への追従を考慮する必要がありそうです。

⚖️ スケーリング設計について ― Agent数 x 分析銘柄数

今回の実装では、Fundamental AnalystとRisk Analystの2つのSpecialistだけを使いました。 株式分析をさらに細かく分解するなら、「Fundamental Analyst、Valuation Analyst、Technical Analyst、News Analyst、Macro Analyst、Sector Analyst」のように、Agentを増やすこともできます。

しかし、Agent数を増やせば、分析品質が比例して上がるわけではありません。

Agentが増えるほど、

  • Token Cost
  • Latency
  • Agent間の矛盾
  • オーケストレーション
  • デバッグ対象

も増えていきます。今回2つのAgentに留めたのは、Fundamental AnalystとRisk Analystに、それぞれ明確に異なるInputと責務があることも大きな要因です。

一方で、処理する銘柄数のスケーリングを検討する場合 (例えば、毎日数百〜数千銘柄へ広げる)、Agent内部のParallel実行とは別に、銘柄単位の分散処理が必要になります。以下の対比図をご覧ください。

銘柄単位の処理とスケーリングを示した図。左側「① 1銘柄の処理」では、Input(Ticker・Chart Image・Focus)がPython Orchestrator(Fan-Out)からFundamental AnalystとRisk Analystへ並列に渡り、Final Synthesizerで統合されてReportになる流れを示し、「1銘柄の中では、専門分析を並列実行して統合する」と補足されている。右側「② 複数銘柄へスケール」では、Ticker ListがAzure Queue Storage / Service Busへ積まれ、Azure FunctionsまたはContainer Apps Jobsを介して、Worker A・B・Cが水平スケールしながらそれぞれFundamental Analyst→Risk Analyst→Final Synthesizerを実行し、Reports / Resultsへ出力する流れを示し、「銘柄単位で分散処理し、Workerを水平スケールする」と補足されている。

1銘柄の中では、Fundamental AnalystとRisk Analystを並列実行し、Final Synthesizerで統合するParallel Fan-Out/Gatherを使います。 複数銘柄へスケールする場合、その処理自体を銘柄単位で分散させます。銘柄リストをQueue(Azure Queue StorageやService Bus)に積み、Azure FunctionsやContainer Apps JobsでWorkerを水平スケールさせながら、Worker単位でFundamental Analyst→Risk Analyst→Final Synthesizerの一連の処理を実行する構成が考えられます。

ただし、Workerを増やせば無制限に処理できるわけではありません。

実運用では、少なくとも以下を考慮する必要があります。

  • LLMのRate Limit / Quota
    • RPM(Requests per Minute)
    • TPM(Tokens per Minute)
    • 同時実行数
  • Grounding with Bing Searchの利用制限
    • 検索トランザクション数
    • 同時実行数
    • 課金量
  • Token Cost
    • 銘柄数 × Agent数に応じて増加する
  • Retry / Timeout
    • 一時的な429や外部サービス失敗への対応
  • Backpressure
    • 下流サービスのQuotaを超えないよう、Queueからの取り出し速度を調整する

つまり、今回の構成では、

  • 1銘柄の中では Fundamental AnalystとRisk Analystを並列実行する
  • 複数銘柄へ広げる場合は 銘柄単位でWorkerを分散させる

という、2段階の並列化を分けて考える必要があります。

1銘柄の中のParallel実行と、複数銘柄をさばくための分散処理は、別レイヤーとして設計する

🏁 まとめ

今回は、Microsoft Foundryで Parallel Fan-Out/Gather Pattern を実装しました。

実際に、フジクラ(5083.T)のEntry Point評価では、

Fundamental → Positive
Chart       → Neutral
Entry Setup → Neutral

という結果になりました。

企業のFundamentalと現在のEntry Timingを別々のAgentへ分離したことで、「良い会社であること」と「今が良いEntry Pointであること」を混同せずに評価できた点は、今回のMulti-Agent構成の明確なメリットでした。

一方で、分析専任のAgent数や銘柄数が増えれば、コストや複雑性も雪だるま式に増えることが予測されます。

今回の株式分析は、Parallel Fan-Out/Gather Patternの特徴を確認する題材として、比較的きれいに当てはまるユースケースでした。


参考


本記事はMulti-Agent Systemの技術検証を目的としており、掲載する銘柄・分析結果は投資助言ではありません。