ARTICLE DETAIL

资讯详情

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

ZCode 中的 Sources 组件:为 AI 对话响应构建可折叠引用来源展示

ZCode 中的 Sources 组件:为 AI 对话响应构建可折叠引用来源展示 【免费下载链接】ZCodeZ.ais coding agent harness. Powerful, intelligent, extensible.项目地址https://gitcode.com/gh_mirrors/zco/ZCode点击查看免费下载导读本文以 ZCode 仓库内置的ai-elements技能中Sources组件的完整参考文档.agents/skills/ai-elements/references/sources.md为核心系统讲解如何在基于 AI SDK 的聊天应用中为模型生成的回答展示来源与引用citations。文章将覆盖Sources、SourcesTrigger、SourcesContent、Source四个组件的安装方式、与 Perplexity 等搜索型模型的端到端接入示例、全部 Props 参数表并结合 ZCode 仓库中的实际实现源码packages/ui/src/components/ai-elements/sources.tsx深入剖析其折叠交互原理与自定义渲染方法帮助你快速在自己的 AI 前端中落地引用来源这一关键可信度功能。什么是 Sources 组件Sources是一个允许用户查看生成回答时所使用来源或引用的 UI 组件。在基于检索增强或联网搜索的 AI 应用中模型会引用多个网页、文档作为回答依据Sources组件将这些来源以可折叠collapsible的形式组织起来默认只显示一个触发按钮例如Used N sources用户点击后展开来源列表每条来源以带图标的超链接形式展示。从 ZCode 仓库的实现看packages/ui/src/components/ai-elements/sources.tsx该组件由四个协作的部分组成底层依赖 radix-ui 的Collapsible原语见 packages/ui/src/components/ui/collapsible.tsx组件职责Sources折叠容器根节点内部渲染Collapsible持有展开/收起状态SourcesTrigger折叠触发器显示来源数量默认文案Used N sources与箭头图标SourcesContent折叠内容区承载来源列表带展开/收起动画Source单个来源链接默认渲染书本图标 标题新标签页打开四个组件均支持通过className与展开的 props 深度定制这与该技能目录.agents/skills/ai-elements/SKILL.md所倡导的尽可能透传原生属性的扩展性原则一致。安装在具备以下前置条件的项目中通过 AI Elements CLI 即可一键安装Sources组件Node.js 18 及以上版本已安装 AI SDK 的 Next.js 项目已安装 shadcn/ui未安装时执行安装命令会自动补装。安装命令npx ai-elementslatest add sources如果你的项目使用 pnpm 或 bun 作为包管理器请使用对应的运行器pnpm dlx ai-elementslatest或bunx --bun ai-elementslatest。CLI 会将组件代码及其依赖写入你项目中 shadcn 配置的组件目录默认位置是/components/ai-elements/因此安装完成后代码中引入路径即为import { Source, Sources, SourcesContent, SourcesTrigger } from /components/ai-elements/sources;组件代码会作为你项目源码的一部分落地而非封装在不可见的库里这意味你可以直接打开组件文件查看实现、按需修改样式与逻辑。若引入时报module not found请检查tsconfig.json中是否配置了/路径别名{ compilerOptions: { baseUrl: ., paths: { /*: [./*] } } }与 AI SDK 集成构建一个联网搜索问答示例参考文档给出了一个完整的实战示例对应的示例源码位于 .agents/skills/ai-elements/scripts/sources.tsx使用 Perplexity Sonar 模型构建一个简单的网页搜索 Agent前端展示每次回复引用的来源。前端组件app/page.tsxuse client; import { useChat } from ai-sdk/react; import { Source, Sources, SourcesContent, SourcesTrigger } from /components/ai-elements/sources; import { PromptInput, type PromptInputMessage, PromptInputTextarea, PromptInputSubmit, } from /components/ai-elements/prompt-input; import { Conversation, ConversationContent, ConversationScrollButton, } from /components/ai-elements/conversation; import { Message, MessageContent, MessageResponse } from /components/ai-elements/message; import { useState } from react; import { DefaultChatTransport } from ai; const SourceDemo () { const [input, setInput] useState(); const { messages, sendMessage, status } useChat({ transport: new DefaultChatTransport({ api: /api/sources, }), }); const handleSubmit (message: PromptInputMessage) { if (message.text.trim()) { sendMessage({ text: message.text }); setInput(); } }; return ( div classNamemax-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px] div classNameflex flex-col h-full div classNameflex-1 overflow-auto mb-4 Conversation ConversationContent {messages.map((message) ( div key{message.id} {message.role assistant ( Sources SourcesTrigger count{message.parts.filter((part) part.type source-url).length} / {message.parts.map((part, i) { switch (part.type) { case source-url: return ( SourcesContent key{${message.id}-${i}} Source key{${message.id}-${i}} href{part.url} title{part.url} / /SourcesContent ); } })} /Sources )} Message from{message.role} key{message.id} MessageContent {message.parts.map((part, i) { switch (part.type) { case text: return ( MessageResponse key{${message.id}-${i}} {part.text} /MessageResponse ); default: return null; } })} /MessageContent /Message /div ))} /ConversationContent ConversationScrollButton / /Conversation /div PromptInput onSubmit{handleSubmit} classNamemt-4 w-full max-w-2xl mx-auto relative PromptInputTextarea value{input} placeholderAsk a question and search the... onChange{(e) setInput(e.currentTarget.value)} classNamepr-12 / PromptInputSubmit status{status streaming ? streaming : ready} disabled{!input.trim()} classNameabsolute bottom-1 right-1 / /PromptInput /div /div ); }; export default SourceDemo;这段代码的关键逻辑在于通过useChat拿到流式消息后对message.parts中type source-url的 part 逐一映射为Source链接并用SourcesTrigger的count属性展示来源总数文本 part 则交给MessageResponse渲染。折叠容器Sources仅包裹 assistant 消息用户消息不展示来源。后端路由api/chat/route.tsimport { convertToModelMessages, streamText, UIMessage } from ai; import { perplexity } from ai-sdk/perplexity; // Allow streaming responses up to 30 seconds export const maxDuration 30; export async function POST(req: Request) { const { messages }: { messages: UIMessage[] } await req.json(); const result streamText({ model: perplexity/sonar, system: You are a helpful assistant. Keep your responses short ( 100 words) unless you are asked for more details. ALWAYS USE SEARCH., messages: await convertToModelMessages(messages), }); return result.toUIMessageStreamResponse({ sendSources: true, }); }后端的关键配置是toUIMessageStreamResponse({ sendSources: true })开启后AI SDK 会把模型返回的引用来源编码为source-url类型的 UI message part前端据此驱动Sources组件渲染。系统提示词中的 ALWAYS USE SEARCH 用于引导 Sonar 模型始终执行搜索从而稳定地产出可展示的来源数据。maxDuration 30允许流式响应最长 30 秒。功能特性Sources组件围绕来源展示场景提供了以下能力可折叠组件用户可按需展开/收起回答所使用的来源或引用列表触发器与内容可定制SourcesTrigger与SourcesContent均可传入自定义子元素或样式支持自定义来源Source组件接受任意href与title可渲染任意来源/引证链接响应式设计布局适配移动端折叠动画与交互在窄屏下同样可用简洁现代的样式基于 Tailwind 与 shadcn/ui 主题体系可通过className无缝接入既有主题。自定义渲染SourcesTrigger与Source均支持 children 覆盖默认内容这为深度定制提供了入口。参考文档提供了自定义示例对应源码 .agents/skills/ai-elements/scripts/sources-custom.tsxuse client; import { Source, Sources, SourcesContent, SourcesTrigger } from /components/ai-elements/sources; import { ChevronDownIcon, ExternalLinkIcon } from lucide-react; const sources [ { href: https://stripe.com/docs/api, title: Stripe API Documentation }, { href: https://docs.github.com/en/rest, title: GitHub REST API }, { href: https://docs.aws.amazon.com/sdk-for-javascript/, title: AWS SDK for JavaScript, }, ]; const Example () ( div style{{ height: 110px }} Sources SourcesTrigger count{sources.length} p classNamefont-mediumUsing {sources.length} citations/p ChevronDownIcon classNamesize-4 / /SourcesTrigger SourcesContent {sources.map((source) ( Source href{source.href} key{source.href} {source.title} ExternalLinkIcon classNamesize-4 / /Source ))} /SourcesContent /Sources /div ); export default Example;与默认渲染相比自定义示例做了两处增强一是触发器文案改为 Using N citations 并显式放置下箭头图标二是每条来源追加ExternalLinkIcon外链图标语义上提示用户点击后将离开当前页面。这正是该组件可像自己写的代码一样自由修改的体现。结合源码看自定义的实现原理在 ZCode 的组件实现中packages/ui/src/components/ai-elements/sources.tsxSourcesTrigger使用{children ?? (...)}的写法传入 children 时完全渲染自定义内容未传入时才回退到默认的 Used {count} sources 文案与ChevronDownIconSource同样用{children ?? (...)}在无 children 时回退到BookIcontitle的默认布局。由此可推断任何自定义内容都是通过 children 覆盖实现的而组件自身仅负责折叠状态与基础 a 标签语义互不干扰。Props 参考以下是四个组件的完整 Props 说明与参考文档一致并结合实现源码补充类型来源。Sources /Prop类型默认值说明...propsReact.HTMLAttributesHTMLDivElement-其余所有 props 透传到根 div实现中Sources实际渲染为 radix-ui 的Collapsible根组件见 packages/ui/src/components/ui/collapsible.tsx因此defaultOpen、open、onOpenChange等折叠控制 props 同样可用。SourcesTrigger /Prop类型默认值说明countnumber必填触发器上展示的来源数量...propsReact.ComponentPropstypeof CollapsibleTrigger-其余 props 透传到 CollapsibleTrigger 组件SourcesContent /Prop类型默认值说明...propsReact.HTMLAttributesHTMLDivElement-其余 props 透传到内容容器实现中SourcesContent渲染为CollapsibleContent内置了data-[state]驱动的展开/收起动画类fade-out、slide-in-from-top 等实际生效样式可参考 packages/ui/src/components/ai-elements/sources.tsx。Source /Prop类型默认值说明...propsReact.AnchorHTMLAttributesHTMLAnchorElement-其余 props 透传到 a 元素Source即一个a锚点元素实现中默认带有target_blank与relnoreferrer保证来源在新标签页打开且不泄露来源页信息href、title等标准链接属性均可用。主题与样式注意事项Sources组件继承 shadcn/ui 的>赞分享【免费下载链接】ZCodeZ.ais coding agent harness. Powerful, intelligent, extensible.项目地址https://gitcode.com/gh_mirrors/zco/ZCode点击查看免费下载相关推荐如何用Ventoy打造终极多系统启动U盘告别反复格式化一U盘装遍所有操作系统如何用Ventoy打造终极多系统启动U盘告别反复格式化一U盘装遍所有操作系统 还在为每次重装系统都要重新制作启动盘而烦恼吗还在因为U盘只能存放一个系统镜像操作系统固件开发工具marimo Accordion 组件详解用 mo.accordion 构建可折叠内容区marimo Accordion 组件详解用 mo.accordion 构建可折叠内容区 导读 本文围绕 marimo 的 mo.accordion 布局组件数据科学前端后端AI 应用推荐开源项目React响应式折叠组件React Collapsible推荐开源项目React响应式折叠组件React Collapsible 在构建动态和交互式的网页应用时处理大量信息展示的高效性和条理性至关重要。今天我创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表