- 后端
- 前端
- CRM
- 人工智能
- AI Agent
【免费下载链接】crm
Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.
Checkpoint 是 ai-elements 组件库提供的一个轻量级对话组件,用于在会话历史中标记关键节点,并允许用户将聊天一键恢复至之前的任意状态。本文基于本仓库内置的 ai-elements 技能文档(checkpoint.md),完整讲解其安装、组合方式、Props 契约,并结合 AI SDK 的useChat状态管理,给出手动打点、自动打点与分支对话三种实战方案,帮助你在 Agent 类应用中落地"回到过去"的对话体验。
背景:Checkpoint 组件是什么
Checkpoint是一个用于标记对话历史节点、并把聊天恢复到先前状态的基础组件。它借鉴了 VSCode Copilot 的 checkpoint 功能:用户可以在对话中途"立一个路标",之后随时点击恢复,把会话拉回到该节点,同时通过清晰的视觉分隔线区分不同会话片段。
在本仓库中,ai-elements 以 skill 的形式随项目一起分发(见 skills-lock.json 中的vercel/ai-elements条目),其组件文档、示例脚本与技能说明分别位于:
- .agents/skills/ai-elements/references/checkpoint.md:Checkpoint 组件参考文档(本文主体);
- .agents/skills/ai-elements/scripts/checkpoint.tsx:可直接运行的完整示例;
- .agents/skills/ai-elements/SKILL.md:ai-elements 技能总览,包含安装前提、CLI 用法与排障指南。
安装
在项目根目录(package.json所在目录)执行:
npx ai-elements@latest add checkpoint按 SKILL.md 的说明,组件默认安装到@/components/ai-elements/目录(具体取决于你的 shadcn 组件配置)。CLI 会把组件源码直接写入你的代码库,而不是隐藏在某个库中,因此安装后可以像项目自身组件一样直接导入、修改样式或扩展逻辑。如果项目使用 pnpm 或 bun,请将命令替换为pnpm dlx ai-elements@latest add checkpoint或bunx --bun ai-elements@latest add checkpoint。
安装前提:
- Node.js 18 及以上;
- 已安装 AI SDK 的 Next.js 项目(组件示例依赖
@ai-sdk/react的useChat); - 项目中已配置 shadcn/ui(未安装时 CLI 会自动安装)。
特性概览
按文档,Checkpoint 组件具备以下开箱即用的能力:
- 简洁的 flex 布局,由图标(icon)、触发按钮(trigger)和分隔线(separator)三部分组成;
- 视觉分隔线:在对话流中制造清晰的"断点"观感,将不同会话段分开;
- 可点击的恢复按钮:一键回滚到指定检查点;
- 可自定义图标:默认使用 lucide-react 的
BookmarkIcon; - 键盘可达与无障碍支持:具备正确的 ARIA 标签;
- 响应式设计:适配不同屏幕尺寸;
- 无缝的明暗主题集成:与 shadcn/ui 的主题系统保持一致。
与 AI SDK 集成:完整示例
文档给出了一套可直接照搬的聊天界面实现:在消息流中渲染检查点标记,点击 "Restore checkpoint" 时把useChat的消息数组截断到检查点位置,同时清理该点之后的所有检查点。将下面组件添加到前端:
"use client"; import { useState, Fragment } from "react"; import { useChat } from "@ai-sdk/react"; import { Checkpoint, CheckpointIcon, CheckpointTrigger, } from "@/components/ai-elements/checkpoint"; import { Message, MessageContent, MessageResponse, } from "@/components/ai-elements/message"; import { Conversation, ConversationContent, } from "@/components/ai-elements/conversation"; type CheckpointType = { id: string; messageIndex: number; timestamp: Date; messageCount: number; }; const CheckpointDemo = () => { const { messages, setMessages } = useChat(); const [checkpoints, setCheckpoints] = useState<CheckpointType[]>([]); const createCheckpoint = (messageIndex: number) => { const checkpoint: CheckpointType = { id: nanoid(), messageIndex, timestamp: new Date(), messageCount: messageIndex + 1, }; setCheckpoints([...checkpoints, checkpoint]); }; const restoreToCheckpoint = (messageIndex: number) => { // Restore messages to checkpoint state setMessages(messages.slice(0, messageIndex + 1)); // Remove checkpoints after this point setCheckpoints(checkpoints.filter((cp) => cp.messageIndex <= messageIndex)); }; return ( <div className="max-w-4xl mx-auto p-6 relative size-full rounded-lg border h-[600px]"> <Conversation> <ConversationContent> {messages.map((message, index) => { const checkpoint = checkpoints.find( (cp) => cp.messageIndex === index ); return ( <Fragment key={message.id}> <Message from={message.role}> <MessageContent> <MessageResponse>{message.content}</MessageResponse> </MessageContent> </Message> {checkpoint && ( <Checkpoint> <CheckpointIcon /> <CheckpointTrigger onClick={() => restoreToCheckpoint(checkpoint.messageIndex) } > Restore checkpoint </CheckpointTrigger> </Checkpoint> )} </Fragment> ); })} </ConversationContent> </Conversation> </div> ); }; export default CheckpointDemo;实现要点拆解:
- 状态建模:
CheckpointType用messageIndex关联到具体某条消息,messageCount记录检查点处的消息总数,timestamp用于展示或排序; - 渲染位置:检查点不是独立列表,而是通过
checkpoints.find((cp) => cp.messageIndex === index)在消息流内联渲染——这决定了它在视觉上精确"卡"在两条消息之间; - 恢复逻辑:核心只有两行——
setMessages(messages.slice(0, messageIndex + 1))截断消息;setCheckpoints(checkpoints.filter(...))丢弃检查点之后的全部检查点。恢复后若再发送新消息,本质上就是在该节点派生出一条新的对话分支; - 组合关系:
Checkpoint内部默认会渲染图标、触发器,并在末尾自动追加分隔线(Separator),因此你不需要手动添加任何分隔元素。
仓库中的示例脚本 .agents/skills/ai-elements/scripts/checkpoint.tsx 给出了同样的组合方式:它用memo+useCallback把单个检查点封装成CheckpointItem,并通过checkpoints.find((cp) => cp.messageCount === index + 1)(注意这里用的是 1 基的messageCount)定位检查点,onRestore回调执行setMessages(initialMessages.slice(0, messageCount))完成回滚。这种将检查点封装成独立 memo 子组件、并通过回调上抛恢复事件的做法,适合消息列表较长、需要避免整体重渲染的聊天场景。
三种典型使用场景
手动检查点
允许用户在自己认为重要的对话节点手动创建检查点,例如在关键结论、代码方案定稿处按下按钮:
<Button onClick={() => createCheckpoint(messages.length - 1)}> Create Checkpoint </Button>这里传入的是messages.length - 1,即当前最后一条消息的下标,确保检查点正好标记在对话末尾。
自动检查点
在达到显著的对话里程碑后自动创建检查点,例如每 5 条消息打一个点,无需用户干预:
useEffect(() => { // Create checkpoint every 5 messages if (messages.length > 0 && messages.length % 5 === 0) { createCheckpoint(messages.length - 1); } }, [messages.length]);依赖数组为messages.length,因此每次消息数变化都会触发判断;% 5 === 0的取模条件让检查点自动落在第 5、10、15… 条消息之后。
分支对话
利用检查点实现对话分支:用户可以在同一会话里先保存当前分支(例如保存为草稿或快照),再回退到检查点探索另一条路径,之后还能恢复原分支继续:
const restoreAndBranch = (messageIndex: number) => { // Save current branch const currentBranch = messages.slice(messageIndex + 1); saveBranch(currentBranch); // Restore to checkpoint restoreToCheckpoint(messageIndex); };saveBranch是你的业务实现(如存入本地状态、IndexedDB 或服务端),restoreToCheckpoint复用前面定义的截断逻辑。这一模式很适合 AI 助手类产品的"多方案对比":让用户先看到 A 方案的推导过程,回退检查点后尝试 B 方案,两种路径互不干扰。
Props 参考
<Checkpoint />
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 检查点图标与触发器组件;末尾会自动附带一个 Separator 分隔线 |
...props | React.HTMLAttributes<HTMLDivElement> | - | 其余属性透传给根 div |
children默认自动追加 Separator,这是组件保持"清晰会话断点"观感的关键设计——你只需提供图标与触发器,分隔线由组件自身完成。
<CheckpointIcon />
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 自定义图标内容;未提供时默认渲染 lucide-react 的 BookmarkIcon |
...props | LucideProps | - | 其余属性透传给 BookmarkIcon 组件 |
想换成其他图标(如HistoryIcon、FlagIcon),直接以 children 传入即可;如果只想调整尺寸或颜色,也可以通过...props覆盖。
<CheckpointTrigger />
| Prop | 类型 | 默认值 | 说明 |
|---|---|---|---|
children | React.ReactNode | - | 触发器按钮内展示的文本或内容 |
tooltip | string | - | 悬停时显示的提示文字 |
variant | string | - | 按钮变体样式 |
size | string | - | 按钮尺寸 |
...props | React.ComponentProps<typeof Button> | - | 其余属性透传给底层 shadcn/ui Button 组件 |
variant与size直接复用 shadcn/ui Button 的取值(如ghost、outline、sm等),这意味着触发器按钮的样式、点击处理(onClick)、禁用状态等都可以按 shadcn/ui Button 的惯例配置。仓库示例中即通过tooltip="Restores workspace and chat to this point"为恢复按钮补充了悬停说明。
接入本项目的实践建议
本仓库(Comp AI CRM,Agent 优先的开源 CRM)在apps/app/components/crm/下实现了大量 Agent 对话界面(例如 agent-conversations.tsx 中基于@tanstack/react-query+ tRPC 的会话列表与消息管理)。在接入 Checkpoint 时,可以针对这类真实聊天应用做如下适配:
- 消息来源替换:ai-elements 文档示例基于
useChat的messages/setMessages,而 CRM 场景的消息可能来自服务端持久化(如conversations路由返回的数据),恢复逻辑可改为"截断本地消息列表 + 调用后端接口删除后续消息",核心的slice思想不变; - 检查点持久化:
CheckpointType的id、timestamp字段使其天然可序列化,可将检查点连同messageIndex存入库表,实现"刷新页面后仍可恢复"; - 恢复粒度:示例按"消息"粒度回滚,若业务需要精确到单条消息内的部分内容(如撤销某次工具调用结果),可以扩展
CheckpointType,在messageIndex之外增加partIndex或内容哈希字段。
需要注意的是,本仓库当前并未在apps/app源码中直接使用ai-elements组件(Checkpoint相关内容仅存在于 .agents/skills/ai-elements/ 的文档与示例中),因此上述"接入建议"属于基于文档与仓库现状的可行方案推演,而非对现有代码行为的描述。
小结
Checkpoint 用极小的 API 面(三个组件 + 若干透传 Props)解决了 AI 聊天产品中"会话回滚"这一高价值需求:Checkpoint负责布局与分隔线,CheckpointIcon提供可替换的图标,CheckpointTrigger复用 shadcn/ui Button 承担交互。配合useChat的setMessages截断与checkpoints.filter清理,即可在十余行代码内实现手动打点、自动打点与分支探索三种能力。其"代码进入你的代码库"的安装方式,也让你能随时按业务需求改造分隔样式、恢复逻辑甚至扩展持久化能力。
- 后端
- 前端
- CRM
- 人工智能
- AI Agent
【免费下载链接】crm
Comp AI CRM is an open source, CRM designed for AI agents. Agentic-first CRM.
相关推荐
Higress AI 历史对话插件(ai-history)配置与实践指南
Higress AI 历史对话插件(ai history)配置与实践指南 本篇技术指南围绕 Higress 内置的 AI 历史对话插件(ai history)
API网关后端云原生LLM 网关人工智能MCP 服务Gemini CLI 会话管理实战:恢复、浏览、分叉与回滚你的对话历史
Gemini CLI 会话管理实战:恢复、浏览、分叉与回滚你的对话历史 本篇指南聚焦 Gemini CLI 的会话(session)管理机制:如何从断点恢复上次
人工智能AI Agent交互助手CLIMCP ClientsOpenPlayground对话历史管理全攻略:轻松找回每一段AI对话
OpenPlayground对话历史管理全攻略:轻松找回每一段AI对话 还在为找不到之前的AI对话记录而烦恼吗?OpenPlayground内置的对话历史功能让
AI 应用大模型交互助手
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考