背景
当前引用链路有两个结构性问题,已有的 bug(学格式、张冠李戴、翻译模式空引用、双语截断)都是它们的下游症状:
- 引用是"按内容模糊反查",不是"按身份精确引用"。 模型输出引文文本,
applyAssistantPostProcessing.ts 的 resolveQuoteTarget 用 includes/前缀匹配反猜目标消息。文本匹配天然脆弱(翻译、双语、复述、截断都会失败),失败后兜底到"最近一条用户消息"。同时它只搜 role === 'user',AI 引用自己的话会被静默地错挂到用户最近一条消息上。
- 模型输出格式与它在历史里看到的格式不一致(round-trip 不保真)。 模型写
[[QUOTE:]],落库后历史被 chatPrompts.ts buildMessageHistory 渲染成自然语言头 [xx引用了xx说的「…」,并回复了 ↓]——模型在 few-shot 层面只见过后者,会模仿它输出错误格式。
第 2 点的解析端兜底已落地(见关联 commit:自然语言格式也被识别为合法引用并剥离),但那是止血,不是治本。
方案:按 ID 引用
- 历史渲染:最近 N 条(30–50 条即可,引用几乎总指向近期消息)在现有
[时间戳] 头部旁加短 ID,如 [#482]。
- 输出格式:系统提示改为
[[QUOTE: #482]],语义变为"引用任意一条历史消息"——天然支持 AI 引用自己/图片/转账等。
- 解析:三级降级——① ID 精确查表(确定性)→ ② 现有文本模糊匹配(兼容旧输出,本次落地的自然语言兜底也在这层)→ ③ 最近一条用户消息。
- 历史渲染(AI 侧):AI 自己的引用消息原样回放
[[QUOTE: #N]]\n正文,round-trip 保真,模仿变成自我强化。用户侧保留自然语言渲染不动(那是 1db5ea5 针对"模型只看引用不看回复"的注意力修复,且用户引用存在「你之前说的/自己说的」消歧需求,见 6d0054d)。
- 顺手项:引用块点击跳转原消息——基建全部现成(
replyTo.id 已存、DOM 有 chat-msg-${id}、handleJumpToMessageInChat 已被搜索跳转使用),ID 方案落地后 replyTo.id 变可靠,把 MessageItem.tsx 引用块的 onClick 接上即可;注意原消息被删除时优雅降级。
代价 / 注意点
- Token 开销:每条历史多 ~4 token,只给近期消息编号可控制在数百 token。
- 模型会回声
[#N] 头部标签(现有 sanitize 剥 4 种时间戳格式即是先例),需在 sanitize 加确定性剥离 [#\d+]。
- QUOTE 相关正则散在 4 处需同步:
utils/applyAssistantPostProcessing.ts、utils/sanitize.ts、utils/chatParser.ts、components/chat/MessageItem.tsx;utils/sanitize.ts 被打进 instant-push worker bundles,改完需 pnpm build:workers 重打包。
- 双语/翻译边界 case 有既有测试锁定(
chatPrompts.quote.test.ts、quoteReplyBilingual.test.ts),改渲染格式时需扩测。
- 迁移成本为零:
replyTo 本就存 id,存量数据与旧格式输出经 ②③ 层继续兼容。
背景
当前引用链路有两个结构性问题,已有的 bug(学格式、张冠李戴、翻译模式空引用、双语截断)都是它们的下游症状:
applyAssistantPostProcessing.ts的resolveQuoteTarget用includes/前缀匹配反猜目标消息。文本匹配天然脆弱(翻译、双语、复述、截断都会失败),失败后兜底到"最近一条用户消息"。同时它只搜role === 'user',AI 引用自己的话会被静默地错挂到用户最近一条消息上。[[QUOTE:]],落库后历史被chatPrompts.ts buildMessageHistory渲染成自然语言头[xx引用了xx说的「…」,并回复了 ↓]——模型在 few-shot 层面只见过后者,会模仿它输出错误格式。第 2 点的解析端兜底已落地(见关联 commit:自然语言格式也被识别为合法引用并剥离),但那是止血,不是治本。
方案:按 ID 引用
[时间戳]头部旁加短 ID,如[#482]。[[QUOTE: #482]],语义变为"引用任意一条历史消息"——天然支持 AI 引用自己/图片/转账等。[[QUOTE: #N]]\n正文,round-trip 保真,模仿变成自我强化。用户侧保留自然语言渲染不动(那是 1db5ea5 针对"模型只看引用不看回复"的注意力修复,且用户引用存在「你之前说的/自己说的」消歧需求,见 6d0054d)。replyTo.id已存、DOM 有chat-msg-${id}、handleJumpToMessageInChat已被搜索跳转使用),ID 方案落地后replyTo.id变可靠,把MessageItem.tsx引用块的 onClick 接上即可;注意原消息被删除时优雅降级。代价 / 注意点
[#N]头部标签(现有 sanitize 剥 4 种时间戳格式即是先例),需在 sanitize 加确定性剥离[#\d+]。utils/applyAssistantPostProcessing.ts、utils/sanitize.ts、utils/chatParser.ts、components/chat/MessageItem.tsx;utils/sanitize.ts被打进 instant-push worker bundles,改完需pnpm build:workers重打包。chatPrompts.quote.test.ts、quoteReplyBilingual.test.ts),改渲染格式时需扩测。replyTo本就存id,存量数据与旧格式输出经 ②③ 层继续兼容。