尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
AI 回复比用户提问还早?一个空字符串引发的“时间倒流“
我今天在查 AI 聊天记录时看到一条很离谱的数据assistant message 的 created_at 竟然比触发它的 user message 还早。AI 难道会时间倒流当然不是。最后查下来根因居然是一个空字符串id 。项目背景:考虑需要快速开发,以及方便定位问题和拓展新需求,所以该项目的技术栈选用的是 Next.js Vercel AI SDK Supabase。服务端持久化消息:用户消息在流式响应前存,assistant 消息在 onEnd 回调里用 upsert(..., { onConflict: id }) 存。UI 一切正常,数据库里却是另一个世界。问题一:数据库里 assistant 消息的 id 是空字符串我在 Postgres 表结构中将 id 设置的属性为 primary key。PRIMARY KEY 隐含 NOT NULL,我以为空值必然被拦住。但是居然出现了毫无报错,但是 assistant 的 id 为空字符串的情况。后面我才发现,PRIMARY KEY 能拦住 NULL但拦不住 。在 Postgres 眼里 是一个合法字符串。想要真正的保护得加 CHECK 约束:ALTER TABLE chat_messages ADD CONSTRAINT id_not_empty CHECK (length(trim(id)) 0);问题二:为什么会出现 id 为空字符串的情况根因:AI SDK 只有在你传了 generateMessageId 时才会生成服务端消息 id。不传的话,SDK 内部直接兜底:messageId: messageId ! null ? messageId : 于是持久化回调里拿到的 responseMessage.id 就是 。start chunk 只有在你传了 generateMessageId 时才会带 messageId。不传的话,服务端这份 id 永远不会被覆盖。问题三:时间倒流的真相每一轮 assistant 消息的 id 都是 ,onConflict: id 的 upsert 让它们全部撞到同一行。用一个时间线看会更明显:事件写入 id数据库动作created_at第一次 assistant 回复INSERT 一行10:00第二次 user 提问msg_user_2INSERT 新行10:05第二次 assistant 回复命中同一个主键,UPDATE 旧行仍然是所以并不是 assistant 真的早于 user,而是第二次 assistant 回复没有创建新记录,而是覆盖了第一次那条 id 的旧记录。created_at 记录的是这行数据第一次 INSERT 的时间,不会因为后面的 UPDATE 自动变成当前时间。这就是时间倒流的真相:表面上是时间戳异常,本质上是所有 assistant 回复都被 upsert 到同一行,历史回复一直在被静默覆盖。这不是展示问题,而是数据丢失。那我们应该怎么办?核心修复是让服务端明确生成 assistant message id。比如:import { createIdGenerator } from ai; createAgentUIStreamResponse({ agent, uiMessages: messages, generateMessageId: createIdGenerator({ prefix: msg, size: 16, }), onEnd: async ({ responseMessage }) { await saveMessage(responseMessage); }, });这样 AI SDK 在创建 assistant response message 时,会给它一个稳定的服务端 id,onEnd 里拿到的 responseMessage.id 就不再是 。如果涉及续写已有 assistant 消息,再配合 originalMessages 使用,让 SDK 正确判断这次是在延续旧消息,还是创建一条新的 assistant 消息。数据库层也要加一道兜底,不要让空字符串 id 再混进来:ALTER TABLE chat_messages ADD CONSTRAINT id_not_empty CHECK (length(trim(id)) 0);前者解决源头,后者防止同类坏数据再次入库。最后这个 bug 反而验证了我当初的选型。我选 Vercel AI SDK 就是看中它够薄——出问题时可以直接读源码。这次从症状 → 持久化回调 → SDK 内部 → 根因,只花了几分钟。换成重型黑盒 Agent 框架,同样的问题可能要查比较久。几条教训:用 AI SDK 做服务端持久化,一定要传 generateMessageId(续写场景再加 originalMessages)PRIMARY KEY / NOT NULL ≠ 非空字符串我正在公开构建一个 AI 会议总结应用,这是开发记录的第一篇,后续会持续分享踩坑实录和从 demo 走向生产的全过程。欢迎关注,一起见证它上线。
RELATED

相关推荐

框架表示法 vs 产生式规则:5 个维度对比结构化与因果式知识建模

框架表示法 vs 产生式规则:5 个维度对比结构化与因果式知识建模

框架表示法与产生式规则:5个维度解析结构化与因果式知识建模的抉择在构建专家系统或业务规则引擎时,知识表示方法的选择往往决定了系统的表达能力、推理效率和维护成本。框架表示法与产生式规则作为两种经典的知识建模范式,分别代表了结构化思…

📅 2026/10/14 10:12:44
pandas多维聚合生产实践:滚动计算、列名扁平化与自定义函数规范

pandas多维聚合生产实践:滚动计算、列名扁平化与自定义函数规范

1. 项目概述:为什么多维聚合不是“加个groupby”就能搞定的事我在银行数据平台组干了八年,从最早用SQL写几十行嵌套子查询做客户分层,到后来带团队重构整个风险指标计算引擎,踩过的坑比写的代码还多。今天聊的这个主题——“多维聚…

📅 2026/10/7 12:54:28
NI HIL自动化测试05-测试环境的搭建

NI HIL自动化测试05-测试环境的搭建

软件的下载 NI 还是很Open的,下载相关软件很简单,不像dSPACE管理的那么严格,下个软件都那么费劲。登录NI官网,找到 包管理器下载完成后打开,直接在这里下载或者卸载相关的软件软件的安装软件安装顺序 Labview--…

📅 2026/11/10 0:03:12
MORE NEWS

更多资讯

📰

工地太忙根本没时间复习考试 高级电工证带国徽值不值得考

工地太忙根本没时间复习考试 高级电工证带国徽值不值得考 你是不是也经常在工地上忙得连吃饭的时间都没有,更别说抽空复习电工考试了?这事儿我懂,我以前也带过十几个项目,几乎每次都被问到“高级电工证带国徽值不值得考”。现在回头想想,这证书确实不是…

📰

水暖工证是特种作业吗在哪考最靠谱别踩坑

水暖工证是特种作业吗在哪考最靠谱别踩坑 怕考不过白交培训费?别急,水暖工证是不是特种作业,得看国家规定。很多人以为水暖工就是安装管道、处理暖气的普通工种,实际上根据《特种作业人员安全技术培训考核管理规定》,水暖工并不属于特种作业范畴,但这并…

📰

高压电工证图纸符号真实经历:学历不高怕报不上名?手把手教你解决

高压电工证图纸符号真实经历:学历不高怕报不上名?手把手教你解决 你是不是也经常听人说,搞特种作业证,学历不够、经验不够,就别想进这行了?我当年就是个高中毕业,连个正式电工证都没有,靠的是 真实经历…

📰

成都低压电工证怎么考?外省证能转吗?上岗必备的干货来了

成都低压电工证怎么考?外省证能转吗?上岗必备的干货来了 之前考的证在外省能不能转过来?这几乎是每个打算考低压电工证的人心里最关心的问题。尤其是在成都这类一线城市,电工岗位需求大,证书成了“上岗必备”的门槛。但如果你之前在别处考过证,心里可能…

📰

电工证年审学习资料千万别踩坑,这些报名避坑指南你必须知道

电工证年审学习资料千万别踩坑,这些报名避坑指南你必须知道 你是不是也遇到过这种情况?不知道去哪报名,怕被中介坑,结果花了冤枉钱还拿不到证? 电工证年审学习资料…

📰

青海电工证报名别踩坑,2026河南最新政策你必须知道

青海电工证报名别踩坑,2026河南最新政策你必须知道 证书过期了不知道怎么复审延期?这是很多特种作业人员的共同烦恼。尤其是像你这样的濮阳公路工程从业者,电工证一旦过期,不仅影响日常工作,还可能面临项目暂停、罚款甚至停工整顿的风险。2026年…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬