マルチエージェントでは、メインエージェントがツール呼び出しを介してサブエージェントを呼び出します。
assistant-ui は MessagePartPrimitive.Messages プリミティブを使用して、これらのネストされた会話をレンダリングします。
assistant-ui 入門 – ガイド : マルチエージェント
作成 : Masashi Okumura (@classcat.com)
作成日時 : 03/25/2026
バージョン : assistant-ui@0.0.85
* 本記事は assistant-ui.com/docs の以下のページを参考にしています :
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
◆ お問合せ : 下記までお願いします。
- クラスキャット セールス・インフォメーション
- sales-info@classcat.com
- ClassCatJP

assistant-ui 入門 – ガイド : マルチエージェント
ツール呼び出し UI 内にサブエージェントの会話をレンダリングします。
マルチエージェント・アーキテクチャでは、メインエージェントがツール呼び出しを介してサブエージェントを呼び出します。各サブエージェントは、それ自身の会話 (ユーザ/アシスタント・メッセージ, ツール呼び出し, 等) を生成する場合があります。assistant-ui は MessagePartPrimitive.Messages プリミティブを使用してこれらのネストされた会話のレンダリングをサポートします。
概要
ツール呼び出しが messages フィールド (ToolCallMessagePart.messages) を含む場合、それはサブエージェントの会話履歴を表しています。MessagePartPrimitive.Messages は現在のツール呼び出しパートからこのフィールドを読み取り、ネストされたスレッドとしてレンダリングします。
主な挙動 :
- スコープの継承 – 親ツールの UI 登録は、サブエージェントのメッセージでも利用可能です。トップレベルで登録された makeAssistantToolUI は、サブエージェントの会話内でも動作します。
- 再帰的 (Recursive) — サブエージェントのメッセージは、ネストされたメッセージを含むツール呼び出しを含めることができます。MessagePartPrimitive.Messages を再度使用するだけです。
- 読み取り専用 (Read-only) — サブエージェントのメッセージは読み取り専用コンテキスト内でレンダリングされます。編集、分岐、作成 (composing) はできません。
クイックスタート
- サブエージェント用にツール UI を登録する
import { makeAssistantToolUI, MessagePartPrimitive, } from "@assistant-ui/react"; const ResearchAgentToolUI = makeAssistantToolUI({ toolName: "invoke_researcher", render: ({ args, status }) => ( <div className="my-2 rounded-lg border p-4"> <div className="mb-2 text-sm font-medium text-gray-500"> Researcher Agent {status.type === "running" && "(working...)"} </div> <MessagePartPrimitive.Messages> {({ message }) => { if (message.role === "user") return <MyUserMessage />; return <MyAssistantMessage />; }} </MessagePartPrimitive.Messages> </div> ), }); - バックエンドからのメッセージを供給する
バックエンドはツール呼び出しの結果について messages フィールドに値を設定します。例えば、AI SDK では :tools: { invoke_researcher: tool({ description: "Invoke the researcher sub-agent", parameters: z.object({ query: z.string() }), execute: async ({ query }) => { const subAgentMessages = await runResearcherAgent(query); return { answer: subAgentMessages.at(-1)?.content, // The messages field is picked up by assistant-ui messages: subAgentMessages, }; }, }), },ℹ️ The exact mechanism for populating messages depends on your backend framework. The key requirement is that the tool result’s corresponding ToolCallMessagePart includes a messages array of ThreadMessage objects.
- ツール UI コンポーネントを登録する
function App() { return ( <AssistantRuntimeProvider runtime={runtime}> <Thread /> <ResearchAgentToolUI /> </AssistantRuntimeProvider> ); }
以上
