20
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

知らないと損かも!AG-UIを入門しよう!

Posted at

前書き

昨年からAI関連のプロトコルが一気に増えてきました。

MCPことModel Context Protocolは、Anthropic社が2024年11月に発表し、今年の2月後半から急速に注目を集めています。

続いてA2A(Agent-to-Agentプロトコル)は、2025年4月9日頃にGoogleが発表し、対応サービスも徐々に増加してきています。

そして最新の動きとして、2025年5月12日にはAG-UI(Agent-User Interaction Protocol)プロトコルも登場しました、今回はそれをキャッチアップしていきましょう。

68220ca67d57591307be02ef_MCP_-AG-UI-Diagram_1.jpg

AG-UIの何が嬉しい?

AG-UIの最大の魅力は、Agentとユーザーを繋ぐUIの標準化です。
これにより、開発者はAgentのフロントエンド実装に労力を費やさずに済みます。

これまでのAgent開発では、リアルタイムストリーミングやツール連携、状態管理などのフロントエンド実装が大きな負担でした。Streamlitは選択肢の一つでしたが、表現力に限界がありました。

AG-UIなら、LangGraph、Mastra、Amazon Bedrock Agent(対応予定)など異なるバックエンド技術でも同一のUI環境で利用可能です。単一のHTTPエンドポイントとJSONイベントストリームによる、シンプルで強力なプロトコルがこれを実現しています。
また、AG-UIはA2A(Agent-to-Agentプロトコル)やMCP(Model Context Protocol)と競合せず、これらと組み合わせることでより強力なAIシステムが構築できます。

442547013-215227a5-9698-4d97-a46e-8904b95bfa08.png

ハンズオン

成果物

AG-UI + Mastra + ReactでMCP搭載のエージェントアプリを構築します
5A0E65FF-CF47-421E-88BB-BC2942997F7A.jpeg

Mastraエージェントの初期化

npx create-mastra@latest

初期化プロセスで色々聞かれますが、全部任意で大丈夫です。

Need to install the following packages:
create-mastra@0.3.3
Ok to proceed? (y) 

┌  Mastra Create
│
◇  What do you want to name your project?
│  copilotkit-app
│
◇  Project created
│
◇  npm dependencies installed
│
◇  mastra installed
│
◇  Dependencies installed
│
◇  .gitignore added
│
└  Project created successfully

┌  Mastra Init
│
◇  Where should we create the Mastra files? (default: src/)
│  src/
│
◇  Choose components to install:
│  Agents, Workflows
│
◇  Add tools?
│  Yes
│
◇  Select default provider:
│  OpenAI
│
◇  Enter your openai API key?
│  Enter API key
│
◇  Enter your API key:
│  you-key
│
◇  Add example
│  Yes
│
◇  Make your AI IDE into a Mastra expert? (installs Mastra docs MCP server)
│  Windsurf
│
│  
│  Windsurf is already installed, skipping.
│
◇  
│
◇   ───────────────────────────────────────╮
│                                          │
│                                          │
│        Mastra initialized successfully!  │
│                                          │
│                                          │
├──────────────────────────────────────────╯

デフォルトではamazon bedrockプロバイダーインストールできないですが、使いたい方は下記の記事をご参照ください

その他必要のライブラリーをインストール

npm install @mastra/mcp@latest @copilotkit/runtime @mastra/client-js

エージェントの実装

src/mastra/agents/index.ts
import { openai } from '@ai-sdk/openai';
import { Agent } from '@mastra/core/agent';
import { Memory } from '@mastra/memory';
import { LibSQLStore } from '@mastra/libsql';
import { MCPClient } from '@mastra/mcp';

const mcp = new MCPClient({
  servers: {
    tavily: {
      command: "npx",
      args: ["-y", "tavily-mcp@0.1.4"],
      env: {
        TAVILY_API_KEY: "you-key",
      },
    },
  },
});

export const searchAgent = new Agent({
  name: 'Search Agent',
  instructions: `
      You are a helpful search assistant that provides search results.

      Your primary function is to help users get search results. When responding:
      - Always ask for a topic if none is provided
      - If the topic name isn’t in English, please translate it
      - If giving a topic with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
      - Keep responses concise but informative

      Use the searchTool to fetch current search data.
      日本語で返信してください。
`,
  model: openai('gpt-4o'),
  tools: await mcp.getTools(),
  memory: new Memory({
    storage: new LibSQLStore({
      url: 'file:../mastra.db',
    }),
    options: {
      lastMessages: 10,
      semanticRecall: false,
      threads: {
        generateTitle: false,
      },
    },
  }),
});
  

mastraのメインファイルを修正します。

src/mastra/index.ts
import { Mastra } from "@mastra/core/mastra";
import { createLogger } from "@mastra/core/logger";
import { LibSQLStore } from "@mastra/libsql";
import { registerApiRoute } from "@mastra/core/server";
import {
  CopilotRuntime,
  copilotRuntimeNodeHttpEndpoint,
  ExperimentalEmptyAdapter,
} from "@copilotkit/runtime";
import { MastraClient } from "@mastra/client-js";
import { searchAgent } from "./agents";
 
const serviceAdapter = new ExperimentalEmptyAdapter();
 
export const mastra = new Mastra({
  agents: { weatherAgent, searchAgent },
  storage: new LibSQLStore({
    url: ":memory:",
  }),
  logger: createLogger({
    name: "Mastra",
    level: "info",
  }),
  server: {
    apiRoutes: [
      registerApiRoute("/copilotkit", {
        method: 'POST',
        handler: async (c) => {
          // N.B. Current integration leverages MastraClient to fetch AGUI.
          // Future versions will support fetching AGUI from mastra context.
          const client = new MastraClient({
            baseUrl: "http://localhost:4111",
          });
 
          const runtime = new CopilotRuntime({
            agents: await client.getAGUI({ resourceId: "searchAgent" }),
          });
 
          const handler = copilotRuntimeNodeHttpEndpoint({
            endpoint: "/copilotkit",
            runtime,
            serviceAdapter,
          });
 
          return handler.handle(c.req.raw, {});
        },
      }),
    ],
  },
});

クライアントの実装

Viteを使ってReactのプロジェクトを初期化します。

% npm create vite@latest

> npx
> create-vite

│
◇  Project name:
│  client
│
◆  Select a framework:
│  ○ Vanilla
│  ○ Vue
│  ● React
│  ○ Preact
│  ○ Lit
│  ○ Svelte
│  ○ Solid
│  ○ Qwik
│  ○ Angular
│  ○ Marko
│  ○ Others
└

依存関係をインストールします。

npm install @copilotkit/react-core @copilotkit/react-ui

クライアントを修正する

src/client/App.tsx
import { CopilotChat } from "@copilotkit/react-ui";
import { CopilotKit } from "@copilotkit/react-core";
import "@copilotkit/react-ui/styles.css";

function App() {

  return (
      <CopilotKit
        runtimeUrl="http://localhost:4111/copilotkit"
        agent="searchAgent"
      >
        <CopilotChat
          labels={{
            title: "Your Assistant",
            initial: "Hi! 👋 How can I assist you today?",
          }}
        />
      </CopilotKit>
    );
}

export default App

このままではエラーが出るので、Mastraサーバーにcorsの設定を追加します。

src/mastra/index.ts
import { Mastra } from "@mastra/core/mastra";
import { createLogger } from "@mastra/core/logger";
import { LibSQLStore } from "@mastra/libsql";
import { registerApiRoute } from "@mastra/core/server";
import {
  CopilotRuntime,
  copilotRuntimeNodeHttpEndpoint,
  ExperimentalEmptyAdapter,
} from "@copilotkit/runtime";
import { MastraClient } from "@mastra/client-js";
import { searchAgent } from "./agents";
 
const serviceAdapter = new ExperimentalEmptyAdapter();
 
export const mastra = new Mastra({
  agents: { weatherAgent, searchAgent },
  storage: new LibSQLStore({
    url: ":memory:",
  }),
  logger: createLogger({
    name: "Mastra",
    level: "info",
  }),
  server: {
+    cors: {
+      origin: ["http://localhost:5173"], //クライアントのPORTに合わせて
+      allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"],
+      allowHeaders: ['Content-Type', 'Authorization', 'x-copilotkit-runtime-client-gql-version'],
+      credentials: true,
+    },
    apiRoutes: [
      registerApiRoute("/copilotkit", {
        method: 'POST',
        handler: async (c) => {
          // N.B. Current integration leverages MastraClient to fetch AGUI.
          // Future versions will support fetching AGUI from mastra context.
          const client = new MastraClient({
            baseUrl: "http://localhost:4111",
          });
 
          const runtime = new CopilotRuntime({
            agents: await client.getAGUI({ resourceId: "searchAgent" }),
          });
 
          const handler = copilotRuntimeNodeHttpEndpoint({
            endpoint: "/copilotkit",
            runtime,
            serviceAdapter,
          });
 
          return handler.handle(c.req.raw, {});
        },
      }),
    ],
  },
});

クライアントとMastraサーバーの両方を立ち上げれば、機能していることが確認できます。

F69DE409-4867-4F19-8EBF-75F7E343270B.jpeg

その他使い方

AG-UIを実装するためのcopilotkitというSDKも公開されており、
ユースケースに合わせて好きなクライアントコンポーネントを選んで利用できます。

現状、Amazon Bedrock Agentがまだ対応していないのは残念ですが、LangGraphMastraが同じクライアントで動作できるだけでも大きなメリットと言えるでしょう。

009234A1-1A0E-4EB1-AE70-2D64F34EC362.jpeg

CopilotPopup

例えばサービスの右下に配置するチャットボットは、CopilotPopupを使えば一瞬で実装できます。

client/src/App.tsx
import { CopilotChat, CopilotPopup } from "@copilotkit/react-ui";
import { CopilotKit } from "@copilotkit/react-core";
import "@copilotkit/react-ui/styles.css";

function App() {

  return (
      <CopilotKit
        runtimeUrl="http://localhost:4111/copilotkit"
        agent="searchAgent"
      >
        <CopilotChat
          labels={{
            title: "Your Assistant",
            initial: "Hi! 👋 How can I assist you today?",
          }}
        />
+        <CopilotPopup
+          instructions={"You are assisting the user as best as you can. Answer in the best way possible given the data you have."}
+          labels={{
+            title: "Popup Assistant",
+            initial: "Need any help?",
+          }}
+        />
      </CopilotKit>
    );
}

export default App

B6F4F16C-D8A5-4467-9161-254A66EBD6D7.jpeg

tailwindcssと一緒に使える

SDKに構築済みのコンポネートのデザインを修正する際に、tailwindcssも使えます。

client % npm install tailwindcss @tailwindcss/vite
vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
+import tailwindcss from '@tailwindcss/vite'

// https://vite.dev/config/
export default defineConfig({
+  plugins: [react(), tailwindcss()],
})
index.css
+ @import "tailwindcss";
client/src/App.tsx
import {type HeaderProps, useChatContext, CopilotSidebar } from "@copilotkit/react-ui";
import { BookOpenIcon } from "@heroicons/react/24/outline";
import { CopilotKit } from "@copilotkit/react-core";
import "@copilotkit/react-ui/styles.css";

function Header({}: HeaderProps) {
  const { setOpen, icons, labels } = useChatContext();
 
  return (
    <div className="flex justify-between items-center p-4 bg-blue-500 text-white">
      <div className="w-24">
        <a href="/">
          <BookOpenIcon className="w-6 h-6 bg-white" />
        </a>
      </div>
      <div className="text-lg">{labels.title}</div>
      <div className="w-24 flex justify-end">
        <button onClick={() => setOpen(false)} aria-label="Close" type="button">
          {icons.headerCloseIcon}
        </button>
      </div>
    </div>
  );
};

function App() {

  return (
      <CopilotKit
      runtimeUrl="http://localhost:4111/copilotkit"
      agent="searchAgent">
        <CopilotSidebar Header={Header} />
      </CopilotKit>
    );
}

export default App

E1B31A6C-9B09-4534-8DDE-D96FEAB2CF67.jpeg

最後に

実際にクライアントからエージェントを呼び出す際、MCPサーバーを使用すると何回か失敗することがありました。原因はまだ特定できていませんが、今後の発展に期待したいと思います。

また、AG-UIとA2Aの発展次第では、クライアントとサーバーの実装を明確に分離することで、よりスケーラブルなシステム構築が可能になるかもしれません。

参考資料

20
12
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
20
12

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?