3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

assistant-ui と AG-UI でAIエージェントのフロントエンドを作ってみる

3
Posted at

はじめに

以前、Amazon Bedrock AgentCoreのPayments機能を使った決済エージェントのアプリを作りました。

その際、フロントエンドには「assistant-ui」というライブラリを使い、エージェントとのやり取りにはAG-UIを使用しました。

assistant-uiは、AIチャットボットのようなUIを簡単に作れるフロントエンドライブラリです。
AG-UIはフロントエンドとAIエージェントを繋ぐためのプロトコルです。

今回は、assistant-uiのAG-UIを使って、AG-UI対応エージェントをどのようにして作ったかの話をします。

1. AG-UIの概要

AIエージェントの構成をどうするか

AIエージェントを作るとき、「ユーザーとエージェントが会話しながらタスクを進める」「途中でユーザーの承認を挟む」といった要件がある場合は、フロントエンドを用意する必要があります。

agent_architecture.png

アンビエントエージェントという選択肢
バックグラウンドで自律的に動く「アンビエントエージェント」のように、ユーザーとGUI上の対話が不要なパターンもあります。

フロントエンドとAIエージェントを繋ぐ時の課題

フロントエンドとAIエージェントの間では、リアルタイムのストリーミングでやりとりするのが主流です。(ChatGPTなどでよく見る、徐々にエージェントの応答が表示されるあれです)

こうしたリアルタイムなストリーミング通信は、SSE(Server-Sent Events)やWebSocketで実現できます。

SSEのイベントイメージ
data: "今日の天気は"

data: "晴れです"

↓ SSEについては以下の記事が参考になります

ただ、SSEは「dataにデータを載せて流す」というところまではできますが、dataの中身としてどんな種類のイベントをどんな形式で流すかは、実装次第になります。(表現力に難あり)

そうなってくると、以下のような課題が発生します。

課題 内容
SSEイベントの
設計・デバッグが大変
AIエージェントはテキスト以外にもツールの実行結果やHuman-in-the-loopなど様々な情報を返します。
SSEは、これらの情報が同じdataとして流れてくるため、テキスト以外のイベントが混在すると区別するのがしんどくなります。
エージェントSDKの
差し替えなど変化に弱い
SSE通信部分の実装はエージェントSDKごとに必要です。
フロントエンド側もSDKごとに個別のSSEを解釈する処理を用意する必要があり、SDKを差し替えるとなると作り直しの可能性があります。

AG-UIを使ってエージェントとのやり取りを共通化しよう

AG-UI(Agent-User Interaction Protocol)は、AIエージェントとフロントエンドとの接続を標準化したイベントベースのプロトコルです。

AG-UIはSSEやWebSocketといった既存の通信方式の上で動作し、前述したような複雑なやり取りを、標準化されたイベントとして定義してくれています。

AG-UIイベントのイメージ
data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg_1", "delta": "今日の天気は"}

data: {"type": "TEXT_MESSAGE_CONTENT", "messageId": "msg_1", "delta": "晴れです"}

data: {"type": "TOOL_CALL_RESULT", "toolCallId": "call_1", "content": "..."}

フロントエンドは、SSEで流れてくるAG-UIイベントを解釈するだけで、エージェントの動きを柔軟にUIへ反映できます。

AIエージェントをAG-UIに対応させるには

AIエージェント側に求められるのは、実行中の出来事(応答テキスト・ツール呼び出しなど)をAG-UIイベントとしてフロントエンドに返すことです。

こうしたAG-UI対応エージェントを作るには、公式から提供されている「AG-UIアダプタ」を使用します。
フロントエンド側でもAG-UIイベントを受け取るための「AG-UIクライアント」を使用します。

ag-ui-architecture.png

「AG-UIアダプタ」は、使用するエージェントごとに用意されています。
作ったエージェントでは、AWSが提供するStrands Agentsを利用しており、専用のアダプタを使用しました。

ag-ui-_strands_architecture.png

AG-UIに対応したフロントエンドの選択肢

エージェント側をAG-UIに対応させたら、次はフロントエンドです。

正直、AG-UIに対応したフロントエンドライブラリは多くありません。Webアプリに対応しているものだと、現状は以下の2つが選択肢になるかと思います。

今回はこの中からassistant-uiを選びました。

ライブラリ 特徴
CopilotKit チャットUI・状態管理・Generative UIを事前構築済みUIとして提供
リッチなフロントエンドを作れるが、仕組みはちょっと複雑
assistant-ui ReactのチャットUIライブラリ
部品を組み合わせて、サクッと綺麗なUIを構築するスタイル

2. assistant-uiの概要

assistant-uiはAIチャットを作れるUIライブラリ

assistant-uiは、ChatGPTのような綺麗なチャットUIをReactアプリに組み込むためのライブラリです。
既存のテーマを使わず、自分のアプリに合わせて一からUIを組み立てることもできます。

中身的には、コアランタイム、プロトコルレイヤー、フレームワークアダプタの3層から構成されています。

assistant-ui.png

assistant-uiはAG-UIにも対応している

assistant-uiは、AG-UIにも対応しており、フレームワークアダプタとして「react-ag-ui」を提供します。
react-ag-uiのuseAgUiRuntimeフックでAG-UIイベントをassistant-uiが読める形に変換します。

エージェントと直接やりとりするのは、AG-UI公式が提供するAG-UIクライアントです。
@ag-ui/clientのHttpAgentでエージェントへの接続を定義して、エージェントとフロントエンドをAG-UIで繋ぎます。

assistant-ui_ag-ui.png

3. 今回の実装内容について

ここからは、実際に作った内容を見ていきます。

全体アーキテクチャ

作ったアプリの全体構成は以下になります。AWS上にエージェントやフロントエンドを構築しています。

aws_architecture1.png

今回は図の左にある「フロントエンド」の実装について説明します。
インフラやエージェント周りは以下を参照ください。

フロントエンドのフォルダ構成

フロントエンド(Next.js)の主なフォルダ構成は以下です。

src/app配下の構成
src/app
├── api
│   ├── agent
│   │   └── route.ts                  ← AgentCore RuntimeへのSSEプロキシ
│   └── paid/weather
│       └── route.ts                  ← x402対応のモックAPI(前回記事を参照)
├── components
│   ├── AssistantRuntimeProvider.tsx  ← ランタイム接続
│   ├── AgentThread.tsx               ← Thread UI
│   ├── AgentAssistant.tsx            ← 上記2つをまとめて配置するコンポーネント
│   ├── paymentToolkit.ts             ← Human-in-the-loopツールの定義
│   └── payment-tools/                ← ツールが表示するUIコンポーネント
├── providers/                        ← Amplify認証・Privyのプロバイダー
├── layout.tsx
└── page.tsx

フロントエンドのパッケージインストール

assistant-uiのAG-UIランタイムを使うには、以下の3パッケージをインストールします。

パッケージ 役割
@assistant-ui/react assistant-ui本体。プロバイダーやUIプリミティブを提供する
@assistant-ui/react-ag-ui AG-UIランタイムアダプター。useAgUiRuntimeを提供する
@ag-ui/client AG-UIクライアント。HttpAgentを提供する

AIエージェントとの接続(HttpAgent + useAgUiRuntime)

エージェントへの接続を設定して、assistant-uiのプロバイダーに渡します。
HttpAgentで接続先を指定し、useAgUiRuntimeでランタイムに変換します。

部品 役割
HttpAgent AG-UIエンドポイントにHTTP接続する
useAgUiRuntime AG-UIイベントをassistant-uiのランタイムに変換する
AssistantRuntimeProvider ランタイムをUIコンポーネント群に配る
ThreadPrimitive / ComposerPrimitive 実際のチャットUIを描画する

AG-UIが提供するHttpAgentを使って、接続先のエージェントを設定します。

今回はNext.jsのAPI Routeを使ってAPIを作っているので、そのAPIをurlに指定します。

fetchでリクエスト送信処理を差し替えられるので、Cognitoのjwtとユーザー情報をカスタムヘッダーに付与しています。

./src/app/components/AssistantRuntimeProvider.tsx
  const agent = useMemo(
    () =>
      new HttpAgent({
        url: "/api/agent",
        fetch: async (url, requestInit) => {
          const session = await fetchAuthSession();
          const accessToken = session.tokens?.accessToken?.toString();
          const userSub =
            session.tokens?.idToken?.payload.sub ??
            session.tokens?.accessToken?.payload.sub;
          const userEmail = session.tokens?.idToken?.payload.email;
          const headers = new Headers(requestInit?.headers);

          if (accessToken) {
            headers.set("Authorization", `Bearer ${accessToken}`);
          }

          if (typeof userSub === "string") {
            headers.set(AGENTCORE_USER_SUB_HEADER, userSub);
          }

          if (typeof userEmail === "string") {
            headers.set(AGENTCORE_USER_EMAIL_HEADER, userEmail);
          }

          return window.fetch(url, {
            ...requestInit,
            headers,
          });
        },
      }),
    [],
  );

次に、assistant-uiが提供するuseAgUiRuntimeを使ってランタイムを作ります。
このとき、先ほど作成したagentを渡します。

これでAG-UIイベントをassistant-uiが解釈できる形に変換します。

./src/app/components/AssistantRuntimeProvider.tsx
const runtime = useAgUiRuntime({ agent, showThinking: false });

あとは、assistant-uiが提供するAssistantRuntimeProviderのruntimeに渡すだけです。

./src/app/components/AssistantRuntimeProvider.tsx
  return (
    <AssistantRuntimeProvider aui={aui} runtime={runtime}>
      {children}
    </AssistantRuntimeProvider>
  );

最終的に以下のような形になりました。

接続先となるAPI Routeの実装は以下のとおりです。

受け取ったリクエストをAgentCore Runtimeの呼び出しURLに転送し、返ってきたSSEストリームをそのままフロントエンドへ流します。

まとめ

今回は、AG-UI対応のエージェントのフロントエンドにassistant-uiを使ってみました。

CopilotKitは、バックエンド層が必要ですが、AG-UIやGenerative UIの共通プロトコル(A2UIなど)の最新を追従しているので、多機能なフロントエンドを構築できます。

一方、assistant-uiは、バックエンドが必須ではないため、バックエンドを挟まずにフロントエンドをサクッと作りたい場合に向いていると思います。

比較観点 CopilotKit assistant-ui 評価理由
機能面 ○ △ CopilotKitは主要機能を標準で提供している。
assistant-uiは一部(会話処理の中断・再開、複数の会話管理)が試験提供にとどまる
Generative UIの対応力 ○ △ CopilotKitはA2UI、MCP Appsなど共通仕様に対応。assistant-uiはMCP Appsに対応しているが、A2UIには未対応(独自実装はある)
導入しやすさ △ ○ CopilotKitは基本的にバックエンドを挟む必要がある。assistant-uiはフロントからAgentへ直接接続できる
AG-UI追従度 ○ △ CopilotKitはAG-UI公式が1st-partyとして扱っている。
assistant-uiは独自の公式AdapterでAG-UIに対応している。対応イベントの詳細は公式ドキュメントを参照

○:標準機能として対応
△:一部未対応、または試験提供中
×:未対応

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?