61|回答带引用:让每句话都能追溯证据
在上一篇我们把公司的规章制度切成了带标签的碎片Chunk存进了向量数据库。现在用户在聊天框里提问了“晚上几点下班打车能报销”后台系统瞬间忙碌起来它通过向量检索捞出了 3 个最相关的 Chunk。接下来如果直接把这 3 个 Chunk 和用户的问题扔给大模型大模型可能会回答“晚上 9 点以后打车能报销 150 元。”这个回答对吗对。但在企业里这个回答是不及格的。因为如果大模型产生了幻觉比如其实是 10 点以后它记错了用户凭着这句话去报销被拒就会来找你算账。工业级的 RAG 必须做到每一句结论都必须带上可点击的引用链接Citation。这不仅是防幻觉的武器更是产品经理“甩锅”的护身符。1. 核心挑战怎么让大模型乖乖打角标大模型生成的是纯文本它不知道怎么在文本里塞一个超链接。我们需要用一种极其强硬的System Prompt系统提示词把它变成一个严谨的“学术论文撰写机器”。步骤一在后台拼接“带编号的参考资料”在发给大模型之前你的代码必须先把检索到的 3 个 Chunk 包装成这样[参考资料 1] 来源https://company.com/docs/reimburse_2024.pdf (第12页) 内容员工在晚上 21:00 之后下班可凭发票报销打车费。单次上限 150 元。 [参考资料 2] 来源https://company.com/docs/reimburse_2024.pdf (第15页) 内容周末加班满 4 小时可报销往返打车费。步骤二用 Prompt 戴上紧箍咒把上面的资料附带上并对大模型下达死命令System Prompt 示例你是一个严谨的企业知识库助手。请根据提供的 [参考资料] 回答用户问题。强制规则你的回答必须 100% 基于参考资料严禁使用内部知识编造。如果资料中没有答案必须回复“抱歉知识库中未找到相关规定”。你陈述的每一个事实都必须在句末标注对应的资料编号例如[1]或[1][2]。2. 进阶实战结构化输出JSON拯救解析大模型确实输出了带有[1]的文本。但前端怎么把这个[1]变成一个能点击跳转的蓝色超链接呢如果靠写正则去匹配[1]很容易被大模型偶尔生成的乱码搞崩溃。终极解法强制大模型输出 JSON 格式的“证据链”。本篇产出带引用的回答接口与格式规范我们利用大模型的Structured Output结构化输出能力要求它严格按照以下 JSON 格式返回答案{answer:员工在晚上 21:00 之后下班可以凭发票报销打车费单次上限为 150 元[1]。如果是周末加班满 4 小时也可以报销往返打车费[2]。,citations:[{ref_id:[1],source_name:2024公司报销制度 (第12页),source_url:https://company.com/docs/reimburse_2024.pdf#page12},{ref_id:[2],source_name:2024公司报销制度 (第15页),source_url:https://company.com/docs/reimburse_2024.pdf#page15}],is_refused:false}前端如何处理这个 JSON前端拿到这个 JSON 后简直不要太爽先把answer显示在聊天框里。扫描answer里的[1]。去citations数组里找到ref_id [1]的对象。把[1]替换成a hrefhttps://...2024公司报销制度/a的超链接。这样用户点击答案里的引用就能直接跳到 PDF 的第 12 页3. 兜底策略拒答Refusal的艺术注意上面 JSON 里的is_refused字段。如果用户问“老板的年薪是多少” 检索系统捞回来的资料全是不相关的废话。此时大模型应该把is_refused设置为true并在answer里回复“抱歉没有找到相关规定。”前端一旦读到is_refused true就可以在界面上展示一个“反馈按钮”“没找到答案点击转交人工客服”。这才是真正闭环的工业级产品体验。总结与复盘在企业 RAG 中没有引用的回答就是耍流氓。它剥夺了用户的核实权。通过Prompt 的强约束 结构化输出JSON我们可以让大模型稳定地产出带有来源链接和角标的答案。这不仅降低了幻觉还极大地提升了前端渲染的灵活性让你的 AI 助手看起来像一个专业的“学术研究员”。下一步路线提示功能开发完了你觉得它回答得很准。但老板问你“咱们这套知识库回答正确率到底有多少”你总不能说“我感觉有 90% 吧”。我们需要用数据说话。下一篇也是项目 B 的收官战我们将实操《评测与优化命中率与引用正确性回归》。