Documentation Index
Fetch the complete documentation index at: https://langchain-zh.cn/llms.txt
Use this file to discover all available pages before exploring further.
AI Elements 是一个基于 shadcn/ui 的可组合组件库,专为 AI 聊天界面构建。诸如 Conversation、Message、Tool、Reasoning 和 PromptInput 等组件设计为可直接放入任何 React 项目,并通过最少的胶水代码连接到 stream.messages。
工作原理
- 以源文件形式安装组件: AI Elements 通过 CLI 提供,可将组件直接添加到您的项目中(类似于 shadcn/ui 注册表风格)
- 将消息映射到组件: 遍历
stream.messages,将 HumanMessage 实例渲染为用户气泡,将 AIMessage 实例渲染为助手回复
- 组合更丰富的 UI: 将工具调用包裹在
<Tool> 中,推理内容包裹在 <Reasoning> 中,所有内容包裹在 <Conversation> 中以管理滚动
通过 CLI 安装 AI Elements 组件。它们将以可编辑的源文件形式添加到您的项目中:
npm install @langchain/react @ai-elements/react
npx ai-elements@latest add conversation message prompt-input tool reasoning suggestion
连接 useStream
直接从 stream.messages 渲染 AI Elements 组件。每个 LangChain BaseMessage 都映射到一个组件:
import { useStream } from "@langchain/react";
import { HumanMessage, AIMessage } from "@langchain/core/messages";
import {
Conversation,
ConversationContent,
ConversationScrollButton,
} from "@/components/ai-elements/conversation";
import {
Message,
MessageContent,
MessageResponse,
} from "@/components/ai-elements/message";
import {
Tool,
ToolHeader,
ToolContent,
ToolInput,
ToolOutput,
} from "@/components/ai-elements/tool";
import {
Reasoning,
ReasoningTrigger,
ReasoningContent,
} from "@/components/ai-elements/reasoning";
import {
PromptInput,
PromptInputBody,
PromptInputTextarea,
PromptInputFooter,
PromptInputSubmit,
} from "@/components/ai-elements/prompt-input";
export function Chat() {
const stream = useStream({
apiUrl: "http://localhost:2024",
assistantId: "agent",
});
return (
<div className="flex flex-col h-dvh">
<Conversation className="flex-1">
<ConversationContent>
{stream.messages.map((msg, i) => {
if (HumanMessage.isInstance(msg)) {
return (
<Message key={i} from="user">
<MessageContent>{msg.content as string}</MessageContent>
</Message>
);
}
if (AIMessage.isInstance(msg)) {
return (
<div key={i}>
{/* 推理块(当模型发出思考令牌时显示) */}
<Reasoning>
<ReasoningTrigger />
<ReasoningContent>{getReasoningText(msg)}</ReasoningContent>
</Reasoning>
{/* 内联工具调用,带有输入/输出显示 */}
{getToolCalls(msg).map((tc) => (
<Tool key={tc.id} defaultOpen>
<ToolHeader type={`tool-${tc.name}`} state={tc.state} />
<ToolContent>
<ToolInput input={tc.args} />
{tc.output && (
<ToolOutput output={tc.output} errorText={undefined} />
)}
</ToolContent>
</Tool>
))}
{/* 流式文本回复 */}
<Message from="assistant">
<MessageContent>
<MessageResponse>{getTextContent(msg)}</MessageResponse>
</MessageContent>
</Message>
</div>
);
}
})}
</ConversationContent>
<ConversationScrollButton />
</Conversation>
<PromptInput
onSubmit={({ text }) =>
stream.submit({ messages: [{ type: "human", content: text }] })
}
>
<PromptInputBody>
<PromptInputTextarea placeholder="问我点什么..." />
</PromptInputBody>
<PromptInputFooter>
<PromptInputSubmit
status={stream.isLoading ? "streaming" : "ready"}
/>
</PromptInputFooter>
</PromptInput>
</div>
);
}
最佳实践
- 自由编辑源文件: 组件以项目内文件形式提供,而非外部包依赖,因此您可以修改任何内容而无需分叉
- 使用
MessageResponse 处理流式传输: 它能正确处理流式传输的部分令牌;避免在流式传输期间直接渲染原始的 msg.content
- 使用
Conversation 包裹: Conversation 组件管理滚动行为,确保新消息自动滚动到视图中
- 使用
isInstance 进行类型守卫: 使用 HumanMessage.isInstance(msg) 和 AIMessage.isInstance(msg) 而不是检查 msg.getType(),以获得正确的 TypeScript 类型收窄