拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

用 AI Elements 构建 AI 原生聊天界面:ZCode 中的组件库集成与实践指南

用 AI Elements 构建 AI 原生聊天界面:ZCode 中的组件库集成与实践指南
  • 人工智能
  • 大模型
  • 代码智能体
  • AI Agent
  • 桌面应用
  • 后端
  • 前端
  • CLI

【免费下载链接】ZCode

ZCode 是 AI 编程工作台,提供桌面应用、浏览器界面和终端 Agent。本仓库包含客户端、后端服务、共享 UI,以及 Agent CLI 与运行时源码。

项目地址:https://gitcode.com/zai-org/ZCode
点击查看免费下载

AI Elements 是一套构建在 shadcn/ui 之上的组件库与自定义注册表,为 AI 原生应用提供会话(Conversation)、消息(Message)、提示输入(PromptInput)、工具调用展示(Tool)、推理过程(Reasoning)等开箱即用的 React 组件。本文以智谱 AI 的 ZCode 仓库中内置的 ai-elements 技能文档为主体,结合仓库内 @zcode/ui 包 中实际的本地化集成源码,系统讲解 AI Elements 的环境准备、CLI 安装、组件组合用法、可扩展性与定制方式,以及常见故障排查方法。读完本文,你将掌握如何在 ZCode 这类 AI 编程工作台中快速搭建具备消息流、流式输出、工具调用、附件上传与模型选择能力的完整聊天界面。

AI Elements 是什么

AI Elements 是一个组件库与自定义注册表(custom registry),构建在 shadcn/ui 之上,目标是把 AI 原生应用中最常复用的界面能力(会话列表、消息气泡、输入框、工具执行面板等)以「组件即源码」的方式交付给开发者。

它与传统 UI 组件库最大的区别在于分发模型:组件不是打包进 npm 依赖的黑盒,而是通过 CLI 将组件代码直接下载并整合进你的项目目录(默认位于@/components/ai-elements/,也可跟随 shadcn 配置的 components 目录)。这意味着:

  • 组件代码成为你代码库的一部分,可以像自己写的代码一样直接阅读、修改与定制;
  • 使用方式与普通 React 组件完全一致,无需额外的 Provider 或全局初始化;
  • 每个组件尽可能透传原生的 HTML 属性(例如Message继承HTMLAttributes<HTMLDivElement>),扩展成本极低。

在 ZCode 仓库中,这一套组件已经被完整地本地化整合进了@zcode/ui包,目录为 packages/ui/src/components/ai-elements/,包含conversation.tsx、message.tsx、tool.tsx、prompt-input.tsx、reasoning.tsx、attachments.tsx、code-block.tsx、terminal.tsx等 40 余个本地实现文件。每个文件头部都带有明确的来源与授权声明(Derived from vercel/ai-elements,Copyright 2023 Vercel, Inc.,Apache-2.0,由 ZCode 做本地集成与格式化适配),完整许可与来源信息可查看仓库根目录的 THIRD-PARTY-NOTICES.md。

在 ZCode 中的本地化集成方式

从仓库源码可以确认,ZCode 的 UI 层已经深度采用了 AI Elements:

  • packages/ui/components.json 在registries字段中注册了"@ai-elements": "https://ai-sdk.dev/elements/api/registry/{name}.json",说明该包直接复用了 AI Elements 的官方 registry,shadcnCLI 可以按名拉取对应组件;
  • packages/ui/package.json 的exports中显式导出了"./message"指向./src/components/ai-elements/message.tsx,依赖中同时包含ai(AI SDK v6)、use-stick-to-bottom、streamdown、shiki等与 AI Elements 配套的运行时库;
  • 业务侧已经在真实使用这些组件,例如 ToolCallBlocks/ToolCallBody.tsx 从 ai-elements 导入了CodeBlock、MessageResponse、ToolInput、ToolOutput来渲染工具调用结果。

从本地实现看,ZCode 还做了一些适配性改造:以 conversation.tsx 为例,它在Conversation上固定了initial="instant"与resize="instant",以避免任务切换时「恢复历史消息 → 自动吸底」被 smooth 动画叠加成拖沓的缓动;导入方式统一改为带.js后缀的相对 ESM 导入,规避 NodeNext 下 package.jsonexports未导出深层路径导致的解析问题。这些都是将上游组件真正落地到生产级 AI 工作台时的典型工程实践。

环境准备(Prerequisites)

在安装 AI Elements 之前,需要确认项目满足以下条件:

依赖要求说明
Node.js18 或更高版本运行安装 CLI 与构建前端的基础运行时
Next.js 项目已初始化组件面向 React 生态,示例以 Next.js App Router 展示
AI SDK已安装提供useChat、streamText、UIMessage等核心 API,组件围绕其数据结构设计
shadcn/ui已安装(或由安装命令自动安装)AI Elements 构建在 shadcn/ui 之上,未安装时执行任一安装命令会自动补齐

此外,官方建议使用 AI Gateway 并在env.local中配置AI_GATEWAY_API_KEY,这样可以在不逐个配置各家模型厂商 API Key 的情况下统一调用模型,便于快速实验。

安装组件

安装 AI Elements 组件有两种等价方式,效果一致:将所选组件的代码与所需依赖写入项目。

方式一:AI Elements CLI(推荐)

npx ai-elements@latest add <component-name>

例如安装会话与消息组件:

npx ai-elements@latest add conversation npx ai-elements@latest add message npx ai-elements@latest add prompt-input

重要:所有 CLI 命令都应使用项目packageManager对应的包运行器,即npx ai-elements@latest、pnpm dlx ai-elements@latest或bunx --bun ai-elements@latest。示例统一用npx,实际项目中请替换为与你的项目一致的运行器。

方式二:shadcn/ui CLI

如果项目已经采用 shadcn 的工作流,可以直接通过 shadcn CLI 安装。前提是在components.json中配置好 AI Elements registry——ZCode 的做法可作参考(见 packages/ui/components.json):

{ "registries": { "@ai-elements": "https://ai-sdk.dev/elements/api/registry/{name}.json" } }

配置完成后,shadcn CLI 即可按名拉取 AI Elements 组件。执行成功后终端会显示文件已添加的确认信息,随后即可在代码中使用该组件。

基础使用:五分钟跑通一个对话组件

组件安装完成后,像使用任何 React 组件一样导入即可。以下是来自技能文档的完整示例(保存为conversation.tsx),它用Message系列组件渲染useChat返回的流式消息:

"use client"; import { Message, MessageContent, MessageResponse } from "@/components/ai-elements/message"; import { useChat } from "@ai-sdk/react"; const Example = () => { const { messages } = useChat(); return ( <> {messages.map(({ role, parts }, index) => ( <Message from={role} key={index}> <MessageContent> {parts.map((part, i) => { switch (part.type) { case "text": return <MessageResponse key={`${role}-${i}`}>{part.text}</MessageResponse>; } })} </MessageContent> </Message> ))} </> ); }; export default Example;

关键点解读:

  • Message from={role}:按消息角色(user/assistant)自动切换左右对齐与样式,用户消息使用次级背景色,助手消息通栏展示;
  • MessageContent:内容容器,负责间距与用户消息的主题色(bg-primary等);
  • MessageResponse:渲染 Markdown 正文,默认支持 GFM、数学公式与「不完整 Markdown 智能解析」;
  • 数据源直接来自 AI SDK 的useChat(),messages数组无需二次转换,组件与 AI SDK 的数据模型天然对齐。

由于组件代码就在你的项目里,你可以打开组件文件查看实现,也可以按自己的需求直接修改。

组件体系一览

技能文档在 references/ 目录 下为每个组件提供了独立文档,并在 scripts/ 目录 提供可直接运行的示例。以下表格汇总了核心组件及其用途(完整列表见 references 目录):

组件用途参考文档
Conversation会话容器,自动吸底滚动、空态、滚动按钮、Markdown 导出conversation.md
Message消息气泡、动作按钮、回复分支、Markdown 渲染message.md
PromptInput输入框 + 附件上传 + 提交按钮 + 模型选择下拉prompt-input.md
Tool可折叠的工具调用详情(参数、状态、输出/错误)tool.md
Reasoning流式推理过程展示,自动展开/收起reasoning.md
Attachments附件展示(网格/行内/列表三种形态)attachments.md
CodeBlock代码块语法高亮、行号、复制按钮code-block.md
Terminal流式终端输出,支持 ANSI 颜色与自动滚动terminal.md
Chain of Thought结构化的分步思考过程展示chain-of-thought.md
Suggestion / Queue / Task 等输入建议、队列状态、任务进度等场景化组件见 references 目录

核心组件实战

Conversation:会话容器

Conversation负责包裹消息列表并自动滚动到底部,同时提供「不在底部时出现的滚动按钮」。核心子组件与能力:

  • ConversationContent:内容区,负责消息间距与内边距;
  • ConversationEmptyState:空会话时的引导态(title、description、icon均可定制);
  • ConversationScrollButton:仅在未处于底部时显示,点击回到最新消息;
  • ConversationDownload:把整个会话导出为 Markdown 文件(默认文件名conversation.md);
  • messagesToMarkdown:导出逻辑的底层工具函数,可自定义消息格式化器,例如只取文本部分拼接:
import { messagesToMarkdown } from "@/components/ai-elements/conversation"; const customMarkdown = messagesToMarkdown( messages, (msg, i) => `[${msg.role}]: ${msg.parts .filter((p) => p.type === "text") .map((p) => p.text) .join("")}`, );

底层实现上,Conversation基于use-stick-to-bottom库(ZCode 本地实现见 conversation.tsx),支持contextRef、instance、render prop 等进阶用法,滚动按钮通过useStickToBottomContext()读取isAtBottom状态并调用scrollToBottom。

一个完整的会话 UI 通常由Conversation+PromptInput组合而成:消息区负责展示,输入区负责提交,二者共享 AI SDK 的useChat状态。

Message:消息展示全家桶

Message组件族覆盖了聊天消息展示的大部分需求:

  • 消息渲染:Message(角色与对齐)、MessageContent(内容容器)、MessageResponse(Markdown 正文);
  • 操作按钮:MessageActions+MessageAction,支持带 tooltip 的 Retry / Copy / Like 等操作,label用于无障碍朗读,未提供 tooltip 时也作为后备文本;
  • 回复分支:MessageBranch、MessageBranchContent、MessageBranchSelector、MessageBranchPrevious、MessageBranchNext、MessageBranchPage,用于在多条候选回复之间切换,defaultBranch指定默认分支(默认 0),onBranchChange监听切换;
  • 工具栏:MessageToolbar,以 space-between 布局把操作按钮与分支选择器放在消息下方一行。

MessageResponse的 Markdown 渲染值得一提,它是流式聊天体验的关键:

  • parseIncompleteMarkdown(默认true):自动修复流式传输中的不完整 Markdown(未闭合的代码块、列表等);
  • remarkPlugins(默认[remarkGfm, remarkMath])与rehypePlugins(默认含rehypeKatex):GFM 表格、任务列表、删除线与数学公式;
  • allowedImagePrefixes/allowedLinkPrefixes:限制图片与链接的 URL 前缀,defaultOrigin处理相对地址,兼顾安全与灵活性;
  • components:传入自定义 React 组件覆盖 Markdown 元素渲染。

在 ZCode 本地实现中(见 message.tsx,约 1670 行),Markdown 渲染链路替换为streamdown全家桶:Streamdown配合@streamdown/cjk、@streamdown/code、@streamdown/math、@streamdown/mermaid插件,语法高亮使用shiki,并额外引入了remark-cjk-friendly-gfm-strikethrough提升中文文本的删除线渲染质量——这是针对中文 AI 工作台场景的典型本地化增强。

PromptInput:带附件的提示输入

PromptInput允许用户携带文件附件向大模型发送消息,内置 textarea、文件上传、提交按钮与模型选择下拉。组件按头部/主体/底部三段式组织:

  • PromptInputHeader:附件展示区;
  • PromptInputBody:自动伸缩的PromptInputTextarea(Enter 提交、Shift+Enter 换行);
  • PromptInputFooter:工具栏PromptInputTools与提交按钮PromptInputSubmit。

工具栏能力包括:

  • PromptInputActionMenu:附件/截图菜单,内含PromptInputActionAddAttachments与PromptInputActionAddScreenshot;
  • PromptInputButton:自定义动作按钮,支持带快捷键提示的 tooltip:
// 简单字符串 tooltip <PromptInputButton tooltip="Search the web"> <GlobeIcon size={16} /> </PromptInputButton> // 带快捷键提示 <PromptInputButton tooltip={{ content: "Search", shortcut: "⌘K" }} /> // 自定义位置 <PromptInputButton tooltip={{ content: "Search", side: "bottom" }} />
  • PromptInputSelect系列:模型下拉(Trigger / Content / Item / Value 全套);
  • 提交按钮PromptInputSubmit:根据status(submitted / streaming / error)自动切换图标;
  • 表单级能力:onSubmit收到PromptInputMessage(含text与files)、accept/multiple/maxFiles/maxFileSize约束、globalDrop全文档拖拽、syncHiddenInput原生表单隐藏输入同步。

PromptInput还提供状态管理 hooks:

const attachments = usePromptInputAttachments(); attachments.files; // 当前附件数组 attachments.add(files); // 添加文件 attachments.remove(id); // 按 ID 移除 attachments.clear(); // 清空 attachments.openFileDialog(); // 打开文件选择对话框

当需要把输入状态提升到组件外部统一控制时,使用可选的PromptInputProvider,配合usePromptInputController/useProviderAttachments/usePromptInputReferencedSources在任意子组件中访问文本输入与附件状态(完整 hooks 与属性表见 prompt-input.md,本地实现见 prompt-input.tsx)。

Tool:工具调用展示

Tool组件专为 AI SDK 的ToolUIPart设计,用可折叠面板展示工具调用的输入、输出、状态与错误:

<Tool defaultOpen={true}> <ToolHeader type="tool-fetch_weather_data" state={weatherTool.state} /> <ToolContent> <ToolInput input={weatherTool.input} /> <ToolOutput output={<MessageResponse>{formatWeatherResult(weatherTool.output)}</MessageResponse>} errorText={weatherTool.errorText} /> </ToolContent> </Tool>
  • ToolHeader:根据state自动渲染状态徽标,getStatusBadge工具函数支持的状态包括input-streaming(Pending)、input-available(Running)、approval-requested(Awaiting Approval)、approval-responded(Responded)、output-available(Completed)、output-error(Error)、output-denied(Denied);
  • ToolInput:以带语法高亮的 JSON 展示参数;
  • ToolOutput:展示执行结果或errorText错误信息,完成/出错状态默认展开以呈现结果;
  • 类型导出ToolPart = ToolUIPart | DynamicToolUIPart覆盖静态与动态两种工具 UI 数据。

后端示例使用 AI SDK 的streamText声明带 Zod 参数校验的工具并toUIMessageStreamResponse()流式返回,前端即可无缝渲染(完整示例见 tool.md,本地实现见 tool.tsx)。

Reasoning:流式推理展示

Reasoning组件展示模型的思考过程,流式期间自动展开、结束后自动收起:

<Reasoning className="w-full" isStreaming={isReasoningStreaming}> <ReasoningTrigger /> <ReasoningContent>{reasoningText}</ReasoningContent> </Reasoning>
  • isStreaming:为 true 时自动打开并显示脉冲动画指示器;
  • ReasoningTrigger:自定义思考文案,可通过getThinkingMessage(isStreaming, duration)定制;
  • ReasoningContent:推理文本经 Streamdown 渲染;
  • useReasoning():子组件访问{ isStreaming, isOpen, setIsOpen, duration }上下文。

适用于 Deepseek R1、Claude extended thinking 等以连续块输出思考内容的模型。如果模型输出的是离散、带标签的步骤(如搜索查询、工具调用),官方建议改用 Chain of Thought 组件做结构化展示。后端启用推理流需要显式开启:result.toUIMessageStreamResponse({ sendReasoning: true })。

Attachments / CodeBlock / Terminal:高频场景组件

  • Attachments:统一展示图片、视频、音频、文档与来源文件。三种形态variant="grid"(消息内缩略图网格)、"inline"(输入区紧凑徽标 + 悬停预览)、"list"(带元数据的文件列表)。辅助函数getMediaCategory(data)返回"image" | "video" | "audio" | "document" | "source" | "unknown",getAttachmentLabel(data)返回展示名。删除按钮onRemove回调与AttachmentRemove组件配套(见 attachments.md)。
  • CodeBlock:Shiki 语法高亮,可选行号,头部可组合CodeBlockTitle(图标 + 文件名)与CodeBlockActions(CodeBlockCopyButton,复制成功态默认 2000ms),支持CodeBlockLanguageSelector多语言切换与dark类深色模式。底层CodeBlockContainer使用contentVisibility做渲染性能优化。
  • Terminal:流式终端输出,ansi-to-react解析 ANSI 颜色(256 色、粗体、斜体、下划线),内置流式光标动画、自动滚动、复制与清空按钮(见 terminal.md)。

可扩展性

AI Elements 的设计原则是组件尽可能透传原生属性。例如Message继承HTMLAttributes<HTMLDivElement>,凡是div支持的属性(className、onClick、id、ARIA 属性等)都可以直接传入;Tool透传Collapsible的 props,ConversationScrollButton透传 shadcn/uiButton的 props。这意味着你可以:

  • 用className叠加 Tailwind 类做样式微调;
  • 用受控 props(如Reasoning的open/onOpenChange)接管内部状态;
  • 用components(MessageResponse)或 render prop(Conversation的children)注入自定义渲染逻辑;
  • 在组件之上包一层业务组件,组合出符合自己产品的交互。

定制化

安装完成后无需额外配置即可使用——组件的 Tailwind 样式与脚本已随安装集成。若需要改样式,直接编辑组件源码即可。以技能文档中的示例为例:去掉MessageContent的圆角,打开components/ai-elements/message.tsx,移除根元素上的rounded-lg类:

export const MessageContent = ({ children, className, ...props }: MessageContentProps) => ( <div className={cn( "flex flex-col gap-2 text-sm text-foreground", "group-[.is-user]:bg-primary group-[.is-user]:text-primary-foreground group-[.is-user]:px-4 group-[.is-user]:py-3", className, )} {...props} > <div className="is-user:dark">{children}</div> </div> );

其中cn(...)会把传入的className与内置类合并,Tailwind 的同名类冲突时以传入为准。由于组件就在项目源码中,任何修改即时生效,这是「组件即源码」模型的核心价值。

故障排查(Troubleshooting)

技能文档针对高频问题给出了明确排查路径:

组件没有样式?确认项目已按 shadcn/ui(Tailwind 4)规范配置——globals.css导入了 Tailwind 并包含 shadcn/ui 基础样式。

运行 CLI 后项目没有任何新增文件?逐项检查:

  1. 当前工作目录是项目根目录(存在package.json的位置);
  2. components.json(shadcn 风格配置)设置正确;
  3. 使用最新版本的 CLI:
npx ai-elements@latest

主题切换失效,应用一直停留在浅色模式?确保应用使用 shadcn/ui 与 AI Elements 期望的同一套data-theme系统。默认实现会在<html>元素上切换data-theme属性,同时tailwind.config.js需使用 class 或 data 选择器。

组件导入报 "module not found"?先确认文件确实存在;若存在,检查tsconfig.json是否配置了@/路径别名:

{ "compilerOptions": { "baseUrl": ".", "paths": { "@/*": ["./*"] } } }

AI 编程助手无法访问 AI Elements 组件?依次验证:配置文件语法是否为合法 JSON、文件路径是否与 AI 工具配置一致、修改后是否重启了编程助手、网络连接是否稳定。

结语

AI Elements 以「组件即源码」的方式,把 AI 聊天界面中最复杂、最容易做丑的部分(流式 Markdown、工具调用状态、附件管理、吸底滚动、模型选择)封装为开箱即用的可组合组件。在 ZCode 仓库中,这套组件已经完成本地化整合(见 packages/ui/src/components/ai-elements/),并针对中文渲染、ESM 解析、任务切换动画等真实工程问题做了适配改造,是研究其源码实现、学习如何在生产级 AI 工作台中落地 AI 聊天 UI 的最佳参考。无论你是要快速搭一个聊天 Demo,还是要构建完整的 AI 助手产品界面,都可以从本仓库的技能文档(SKILL.md)与其 references 组件文档 出发,按需安装、自由定制。

  • 人工智能
  • 大模型
  • 代码智能体
  • AI Agent
  • 桌面应用
  • 后端
  • 前端
  • CLI

【免费下载链接】ZCode

ZCode 是 AI 编程工作台,提供桌面应用、浏览器界面和终端 Agent。本仓库包含客户端、后端服务、共享 UI,以及 Agent CLI 与运行时源码。

项目地址:https://gitcode.com/zai-org/ZCode
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

返回列表