给AI回答加引用角标citation:RAG前端实现

RAG 答案最值钱的部分,是「这句话出自哪」。模型说得头头是道,用户却不敢信,直到你在句末挂个可点的角标 ¹,点开是原文出处------信任感立刻不一样。这个 citation 角标前端怎么做,看着小,流式场景下坑不少。聊聊我的实现。

后端给的是什么

先说数据。我们后端返回的是文本 + 一组引用标记,标记用占位符嵌在文本里,类似:

css 复制代码
根据财报,二季度营收同比增长 18%[cite:3]。利润率持平[cite:1]。

外加一个 sources 数组:

typescript 复制代码
interface Source {
  id: number;
  title: string;
  url: string;
  snippet: string; // 命中的原文片段
}

前端的活就是:把 [cite:N] 替换成可交互的角标,点击/悬浮展示对应 source。

解析:别用 dangerouslySetInnerHTML

最省事的写法是字符串替换成 <sup> 再 innerHTML 塞进去,但 RAG 答案里混着用户/模型生成的内容,直接 innerHTML 等于开 XSS 大门。我把文本切成片段数组,交给框架渲染,安全也可控:

ini 复制代码
function parseCitations(text: string): (string | { cite: number })[] {
  const parts: (string | { cite: number })[] = [];
  const re = /[cite:(\d+)]/g;
  let last = 0, m: RegExpExecArray | null;
  while ((m = re.exec(text))) {
    if (m.index > last) parts.push(text.slice(last, m.index));
    parts.push({ cite: Number(m[1]) });
    last = m.index + m[0].length;
  }
  if (last < text.length) parts.push(text.slice(last));
  return parts;
}

渲染时字符串段走正常文本,{ cite } 段渲染成角标组件,绑上对应 source 的悬浮卡。安全边界守住了,心里踏实。

流式下的坑:标记被切两半

这是 RAG + 流式特有的暗雷。[cite:3] 这七个字符,可能被 SSE 拆成两个 chunk:先来 ...增长 18%[cite: ,下一包才来 3]。如果你每个 chunk 都立刻 parse,第一包会把 [cite: 当普通文本渲染出来,闪一下乱码,第二包再变回去------抖得很难看。

解法是攒一个 buffer,只在「不可能切到标记中间」的安全点才渲染。简单粗暴版:buffer 末尾若有未闭合的 [,就先扣住不渲染,等闭合:

kotlin 复制代码
let buf = '';
function onChunk(delta: string) {
  buf += delta;
  // 找最后一个未闭合的 [,从那截断,剩下的留到下一包
  const open = buf.lastIndexOf('[');
  const safe = open !== -1 && !buf.slice(open).includes(']')
    ? buf.slice(0, open)
    : buf;
  render(parseCitations(safe));
}

这个 buffer 截断逻辑我前后改了三版才稳,核心就一句:标记可能跨包,渲染必须等它齐了。没意识到这点的话,线上就是满屏一闪一闪的 [cite:

角标点击:定位 + 高亮原文

点角标得跳到出处。如果出处在同页的来源列表里,平滑滚过去并高亮两秒,比直接弹新窗口体验好:

javascript 复制代码
function onCiteClick(id: number) {
  const el = document.getElementById(`source-${id}`);
  el?.scrollIntoView({ behavior: 'smooth', block: 'center' });
  el?.classList.add('flash');
  setTimeout(() => el?.classList.remove('flash'), 2000);
}

一个没解决干净的

模型偶尔会幻觉出一个 sources 里根本不存在的编号,比如 [cite:9] 但数组里最大才 5。我加了校验,越界的角标降级成普通文本不渲染成可点链接------但这只是遮羞,根上是检索/生成的对齐问题,前端兜不住。我能做的就是别让一个死链摆在用户面前,治标不治本,老实承认。

RAG 的检索和模型那套我没自己搭,挂在讯飞这类 MaaS 上,场景化检索和来源回传它那边给,前端只负责把引用呈现得可信、可点。

你们的 citation 是悬浮卡还是脚注式?流式切包的坑有没有人也踩过?评论区对个答案。

相关推荐
Allen_LVyingbo3 分钟前
医疗人工智能项目全生命周期管理系统:监管知识建模、工程实现与实证评估(下)
大数据·数据库·人工智能·python·自然语言处理·自动化
Forerror20265 分钟前
MAI Gateway(魔芋企业级AI网关)从零讲起:大模型网关解决什么问题?小白也能看懂
人工智能·maigateway·finapi·企业级ai网关·大模型财务管控·企业级大模型治理网关
志栋智能6 分钟前
超自动化巡检如何生成“有灵魂”的运维报告?
大数据·运维·人工智能·架构·自动化
千里码aicood6 分钟前
pyqt基于pytorch的人脸交换系统设计(opencv)
人工智能·pyqt
凌风的跨境分享6 分钟前
Temu运营避坑指南:制造地点信息填写合规要点与批量优化方法
运维·服务器·人工智能
来让爷抱一个9 分钟前
2026 表示工程实战:八帧跳跃不许特征乱漂,百智云精灵图把激活引导写进素材包
人工智能·机器学习
熊猫钓鱼>_>10 分钟前
从拍照到建模:HarmonyOS 7 3DGS端侧重建完整实战指南
人工智能·3d·ai·harmonyos·arkts·鸿蒙·3dgs
TechEdu20260611 分钟前
[人工智能]人工智能硬件与芯片的历史与演进:全球视角下的架构、制造、存储、软件生态与系统经济性
人工智能·ai
单向箔13 分钟前
03|AGENTS.md:给 AI 的项目说明书
人工智能·ai编程
Token掘金室13 分钟前
OpenAI Realtime API语音对话开发入门
人工智能·ai