assistant-ui Runtime 架构与后端集成
Runtime 抽象层:将 UI 原语与后端解耦,支持 Vercel AI SDK、LangGraph、自定义 SSE 等多种后端切换。
#type / concept
#status / evergreen
#tech / dev / frontend
#tech / ai
[!info] related notes
- 概览: assistant-ui 概览
- Thread & Message: Thread & Message 原语
- SSE 消费: 前端 SSE 消费
- Vercel AI SDK: Vercel AI SDK 聊天运行时
assistant-ui Runtime 架构与后端集成
Runtime 是 assistant-ui 的核心抽象层。所有 UI 原语通过 Runtime 获取状态和触发操作,与后端实现完全解耦。
架构概览
┌──────────────────────────────────┐
│ UI 原语 (Thread, Message...) │
│ ↕ AssistantRuntimeProvider │
├──────────────────────────────────┤
│ Runtime 接口 │ ← 统一的运行时抽象
├──────────────────────────────────┤
│ useChatRuntime (Vercel AI SDK) │
│ useLangGraphRuntime │
│ useDataStreamRuntime │
│ useLocalRuntime (自定义) │
└──────────────────────────────────┘
AssistantRuntimeProvider
所有原语必须包裹在 Provider 内:
// app/layout.tsx 或页面组件
import { AssistantRuntimeProvider } from "@assistant-ui/react";
export default function ChatLayout({ children }) {
const runtime = useChatRuntime({ api: "/api/chat" });
return (
<AssistantRuntimeProvider runtime={runtime}>
{children}
</AssistantRuntimeProvider>
);
}
Vercel AI SDK 集成
最常用的运行时,连接 Next.js API Route:
// app/chat/page.tsx
import { useChatRuntime } from "@assistant-ui/react-ai-sdk";
function ChatPage() {
const runtime = useChatRuntime({
api: "/api/chat",
// 可选配置
maxSteps: 5, // 最大工具调用轮次
onFinish: (message) => {
console.log("生成完成:", message);
},
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>
);
}
// app/api/chat/route.ts
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
export async function POST(req: Request) {
const { messages } = await req.json();
const result = streamText({
model: openai("gpt-4o"),
messages,
tools: {
search: {
description: "搜索知识库",
parameters: z.object({ query: z.string() }),
execute: async ({ query }) => {
return await searchKnowledgeBase(query);
},
},
},
});
return result.toDataStreamResponse();
}
LangGraph 集成
用于需要复杂 Agent 工作流的场景:
// app/chat/page.tsx
import { useLangGraphRuntime } from "@assistant-ui/react-langgraph";
function AgentChatPage() {
const runtime = useLangGraphRuntime({
apiUrl: "/api/langgraph",
// LangGraph 特定配置
assistantId: "agent",
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>
);
}
自定义数据流 Runtime
连接任意 SSE / WebSocket 后端:
// app/chat/page.tsx
import { useDataStreamRuntime } from "@assistant-ui/react-data-stream";
function CustomBackendChat() {
const runtime = useDataStreamRuntime({
// 自定义数据流适配
adapter: {
sendMessage: async (messages) => {
const response = await fetch("/api/chat", {
method: "POST",
body: JSON.stringify({ messages }),
});
return response.body; // 返回 ReadableStream
},
},
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>
);
}
useLocalRuntime
完全本地的运行时,用于自定义后端或测试:
// 自定义运行时实现
import { useLocalRuntime } from "@assistant-ui/react";
function CustomChat() {
const runtime = useLocalRuntime({
// 自定义消息处理
run: async (messages) => {
const response = await myCustomAPI(messages);
return {
content: [{ type: "text", text: response }],
};
},
});
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread />
</AssistantRuntimeProvider>
);
}
运行时配置对比
| 运行时 | 包名 | 适用场景 |
|---|---|---|
useChatRuntime | @assistant-ui/react-ai-sdk | Next.js + Vercel AI SDK |
useLangGraphRuntime | @assistant-ui/react-langgraph | LangGraph Agent 工作流 |
useLangChainRuntime | @assistant-ui/react-langchain | LangChain 链式调用 |
useDataStreamRuntime | @assistant-ui/react-data-stream | 自定义 SSE/数据流 |
useLocalRuntime | @assistant-ui/react | 完全自定义后端 |
useAgUiRuntime | @assistant-ui/react-ag-ui | AG-UI 协议 |
useGoogleAdkRuntime | @assistant-ui/react-google-adk | Google ADK |
Tool UI 注册
工具调用结果需要注册对应的 UI 组件:
// components/ToolUI.tsx
import { makeAssistantToolUI } from "@assistant-ui/react";
// 搜索工具 UI
export const SearchToolUI = makeAssistantToolUI<{
query: string;
results: Array<{ title: string; url: string }>;
}>({
toolName: "search",
render: ({ args, result, status }) => {
if (status.type === "running") {
return <div className="animate-pulse">搜索中: {args.query}...</div>;
}
return (
<div className="rounded-lg border p-3">
<h4 className="font-medium">搜索: {args.query}</h4>
<ul className="mt-2 space-y-1">
{result?.results?.map((r, i) => (
<li key={i}>
<a href={r.url} className="text-blue-500 hover:underline">
{r.title}
</a>
</li>
))}
</ul>
</div>
);
},
});
// 注册 Tool UI — 放在 AssistantRuntimeProvider 内部
<AssistantRuntimeProvider runtime={runtime}>
<SearchToolUI />
<Thread />
</AssistantRuntimeProvider>
运行时切换模式
// 通过配置切换后端,UI 代码完全不变
function ChatWithBackend({ backend }: { backend: "openai" | "langgraph" | "custom" }) {
const runtime = backend === "openai"
? useChatRuntime({ api: "/api/chat" })
: backend === "langgraph"
? useLangGraphRuntime({ apiUrl: "/api/langgraph" })
: useLocalRuntime({ run: customHandler });
return (
<AssistantRuntimeProvider runtime={runtime}>
<Thread /> {/* 同一套 UI,不同后端 */}
</AssistantRuntimeProvider>
);
}
优雅之处:
- Runtime 抽象层让 UI 和后端完全解耦——同一套原语可连接任意后端
makeAssistantToolUI将工具调用渲染为声明式 React 组件,参数类型自动推断- 从 Vercel AI SDK 迁移到 LangGraph 只需替换
useChatRuntime→useLangGraphRuntime