SJ blog
frontend
A

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

AG-UIプロトコル——AIエージェントとフロントエンドをつなぐ第三の標準がMCP・A2Aに加わり、オープンエージェントプロトコルスタックが完成

AG-UIはAIエージェントとUIの双方向通信を標準化するオープンプロトコル。MCP・A2Aと三層を形成し主要クラウドが採用。CopilotKitの$27M調達で加速するエージェントフロントエンド設計を解説する。

一言結論

AG-UIはMCPがツール・データ接続を、A2Aがエージェント間通信を担うのに対し、エージェント→UIのストリーミング・状態共有・フロントエンドからのツール呼び出し(ヒューマン・イン・ザ・ループ)を標準化する。CopilotKitの実装は週数百万インストール、Google・Microsoft・Amazon・Oracleが採用済み。$27M Series Aでエンタープライズ版も始動し、AIアプリのフロントエンドアーキテクチャが根本から変わる転換点だ。

何が起きたか

2026年5月5日、Seattle発のスタートアップCopilotKitが$27MのSeries Aを調達した。リードインベスターはGlilot Capital・NFX・SignalFire。同時に発表されたのがAG-UI(Agent-User Interaction Protocol)——AIエージェントとフロントエンドUIの間の双方向通信を標準化するオープンプロトコルだ。

AG-UIはすでにGoogle・Microsoft・Amazon・OracleといったメジャークラウドプロバイダーとLangChain・Mastra・PydanticAI・Agnoなどのフレームワークが採用しており、週数百万インストールに達している。Deutsche Telekom・DocuSign・Cisco・S&P Globalが本番利用中だ。


なぜAG-UIが必要か——プロトコルスタックの「空白地帯」

AIエージェントエコシステムには2024〜2025年にかけて2つの主要プロトコルが確立された。

既存プロトコルのカバー範囲

MCP(Model Context Protocol)  → エージェント ↔ ツール・データソース
A2A(Agent2Agent Protocol)   → エージェント ↔ エージェント

❌ カバーされていなかった空白地帯: エージェント ↔ ユーザーインターフェース

問題:
  - エージェントの実行状態をUIにどうリアルタイム表示するか?
  - ユーザーがエージェントの途中判断に介入するにはどうするか?
  - エージェントが生成するUIコンポーネントをどう制御するか?

AG-UIはこの空白地帯を埋める。

完成したプロトコルスタック(2026年5月時点)

ユーザー ←→ [AG-UI] ←→ エージェント ←→ [MCP]  ←→ ツール・データ
                                         ←→ [A2A]  ←→ 他エージェント

AG-UIの技術仕様

AG-UIはイベントベースのストリーミングプロトコルで、Server-Sent Events(SSE)またはWebSocketで実装される。

基本イベント型

// AG-UIの主要イベント型定義
type AgentEvent =
  // テキストストリーミング
  | { type: "text_delta"; delta: string }
  | { type: "text_done"; text: string }

  // ツール呼び出しの状態
  | { type: "tool_call_start"; tool_name: string; args: unknown }
  | { type: "tool_call_done"; tool_name: string; result: unknown }

  // UI状態の同期
  | { type: "state_snapshot"; state: Record<string, unknown> }
  | { type: "state_delta"; delta: StateDelta }

  // ヒューマン・イン・ザ・ループ
  | { type: "interrupt"; prompt: string; options?: string[] }
  | { type: "custom"; name: string; payload: unknown };

// フロントエンドからエージェントへの入力
type UserInput =
  | { type: "message"; text: string }
  | { type: "interrupt_response"; value: string }
  | { type: "tool_approval"; tool_call_id: string; approved: boolean };

フロントエンド実装(React)

import { useAgent } from "@copilotkit/react-core";
import { CopilotTextarea } from "@copilotkit/react-ui";

function FinancialAgentUI() {
  const {
    messages,
    isRunning,
    interrupt,   // エージェントが停止して承認を待っている状態
    resumeWith,  // ユーザーが承認/拒否を返す
    agentState,  // エージェントの内部状態をリアルタイム表示
  } = useAgent("financial-agent");

  return (
    <div>
      {/* エージェントの進捗をリアルタイム表示 */}
      {agentState.currentStep && (
        <ProgressBar
          step={agentState.currentStep}
          total={agentState.totalSteps}
        />
      )}

      {/* ヒューマン・イン・ザ・ループ: エージェントが承認待ち */}
      {interrupt && (
        <ApprovalDialog
          message={interrupt.prompt}
          onApprove={() => resumeWith({ approved: true })}
          onReject={() => resumeWith({ approved: false })}
        />
      )}

      <MessageList messages={messages} />

      <CopilotTextarea
        placeholder="エージェントに指示を送る..."
        disabled={isRunning && !interrupt}
      />
    </div>
  );
}

バックエンド実装(Python + LangChain)

from langgraph.graph import StateGraph
from copilotkit.langchain import emit_state_update, wait_for_interrupt

def create_financial_agent():
    graph = StateGraph(AgentState)

    async def research_step(state: AgentState, config):
        # AG-UIプロトコル経由でUIに進捗を送信
        await emit_state_update(config, {
            "currentStep": "market_research",
            "totalSteps": 5,
            "progress": 0.2,
        })

        # 重要な判断でヒューマン・イン・ザ・ループを発動
        if state.requires_human_approval:
            user_decision = await wait_for_interrupt(
                config,
                prompt=f"以下の取引を実行しますか?\n{state.proposed_action}",
            )
            if not user_decision["approved"]:
                return {"status": "cancelled"}

        result = await run_market_research(state.query)
        return {"research_result": result}

    graph.add_node("research", research_step)
    return graph.compile()

MCP・A2Aとの役割分担を整理する

プロトコル比較

┌─────────────┬──────────────────────────────────────────────────────────┐
│ プロトコル   │ 担当領域                                                  │
├─────────────┼──────────────────────────────────────────────────────────┤
│ MCP         │ エージェント → ツール呼び出し(DB・API・ファイルシステム)   │
│ A2A         │ エージェント ↔ エージェント(タスク委譲・結果受け渡し)      │
│ AG-UI       │ エージェント ↔ ユーザーUI(表示・承認・状態共有)            │
└─────────────┴──────────────────────────────────────────────────────────┘

三層が組み合わさると:
  ユーザー
    ↕ [AG-UI] ストリーミング表示・承認フロー
  Orchestratorエージェント
    ↕ [A2A]  サブタスクを専門エージェントに委譲
  Sub-Agent A / Sub-Agent B
    ↕ [MCP]  外部API・データベースを操作

エンタープライズ版の追加機能

$27M調達とともに発表されたCopilotKit Enterprise Intelligence(セルフホスト版)の主な追加機能:

- セルフホスト対応(データがCopilotKit外に出ない)
- カスタム認証(SAML SSO・OIDC)
- ロールベースのエージェントアクセス制御(RBAC)
- エンタープライズSLA・サポート

落とし穴・注意点

エッジ環境でのWebSocket制限

注意: エッジ関数(Cloudflare Workers・Vercel Edge)での使用

SSEは問題なく動作するが、WebSocketはエッジ環境で
接続時間制限(デフォルト30秒)に引っかかることがある。

対策:
  1. SSEを優先的に使用する
  2. WebSocketを使う場合はDurable Objectsまたは
     WebSocket専用のオリジンサーバーを経由する

ヒューマン・イン・ザ・ループのタイムアウト設計

import asyncio

# ❌ タイムアウトなしの承認待ち(エージェントが永久に停止する)
user_response = await wait_for_interrupt(config, prompt="承認しますか?")

# ✅ タイムアウト付き承認待ち
try:
    user_response = await asyncio.wait_for(
        wait_for_interrupt(config, prompt="承認しますか?"),
        timeout=300,  # 5分でタイムアウト
    )
except asyncio.TimeoutError:
    user_response = {"approved": False, "reason": "timeout"}

state_deltaイベントの頻度に注意

大量のstate_deltaイベントを高頻度で送信すると
ブラウザのメモリ使用量が増加する。

目安: 1秒に10回以上のstate_deltaは避け、
     バッチ更新か間引きを検討する。

注意点・未確認事項

  • AG-UIの標準化状況: W3CやIETFへの標準提案は未発表。現時点でのデファクト標準はCopilotKitの実装であり、仕様が変更される可能性がある。
  • A2AとAG-UIの境界: 複雑なマルチエージェントシステムでは役割が重複するケースがある。「UIとのやり取りが必要な場合はAG-UI」という原則が示されているが、実装上の判断が必要。
  • CopilotKit Enterprise Intelligenceの価格: 2026年5月7日時点で未発表。

参考リンク

注記: 本稿は2026年5月5〜7日時点の公開情報に基づく。AG-UIは活発に開発中のため、APIは変更される可能性がある。実装は公式ドキュメントの最新版を参照すること。