知识点思维导图
35 个知识节点
RAG(15) - RAG 回答生成与引用来源
读完后,你应能完成以下任务:
- 绘制“RAG(21) - RAG 回答生成与引用来源 / grounding:答案必须长在资料上”的关键对象与数据流,解释“grounding 的意思是「把模型的回答锚定在检索到的资料上」,不许自由发挥。”,并用源码位置、日志或 Trace 标注证据。
- 为“RAG(21) - RAG 回答生成与引用来源 / citation:出处必须来自实际用到的资料”设计正常与异常输入,验证“citation(引用来源)是企业知识库的信任基础——用户能点开看「这答案出自《售后政策》第 X 条」,”,输出首个偏差位置与回归测试结果。
- 实现“RAG(21) - RAG 回答生成与引用来源 / 拒答:证据不足时老实说不知道”的最小代码或配置,检验“这是 RAG 可信度的底线,也最容易被忽略。”,输出命令、结果与 Diff,并说明不适用边界。
一句话目标:读完你能讲清回答生成这一步要保证哪三件事(grounding、citation、拒答),并能说出引用来源为什么必须从命中的 chunk 生成。
一、与进阶篇的分工
本篇保留为回答生成基础:重点讲 grounding、citation 和拒答。 进阶项目请读 91《企业级知识库项目》, 那里会把引用扩展到页码、表格、图片区域、音视频时间戳等多模态证据定位。
二、RAG 回答生成与引用来源的真实应用场景
检索把最相关的几段资料找出来了,接下来该模型回答了。 这一步看着简单——把资料和问题一起丢给模型,让它答就行。 但三个地方稍不注意就出事:
- 模型拿到资料,却没基于资料答,而是掺了自己「记忆」里的东西,甚至编。
- 答案给了,但用户不知道这话从哪来的,没法核实,企业不敢信。
- 检索其实没找到靠谱资料,模型却硬凑一个答案出来。
回答生成这一步的全部工作, 就是把这三件事按住:基于资料答(grounding)、标明出处(citation)、证据不足就拒答。
三、grounding:答案必须长在资料上
grounding 的意思是「把模型的回答锚定在检索到的资料上」,不许自由发挥。 实现上靠两手:
一是 prompt 里把话说死。给模型的指令大致是:
你是知识库助手。只能根据下面提供的资料回答,
资料里没有的内容不要编,不确定就说不知道。
【资料】
1. (来自 after-sale.md) 退货邮费由买家承担,质量问题导致的退货由卖家承担邮费。
2. (来自 after-sale.md) 商品签收后 7 天内可申请无理由退货。
【问题】退货邮费谁出?
二是只把检索到的资料放进去,不给模型额外发挥的空间。 资料越聚焦,模型越不容易跑偏。 grounding 做得好,模型的回答就是对资料的转述和组织,而不是凭空创作。
四、citation:出处必须来自实际用到的资料
citation(引用来源)是企业知识库的信任基础——用户能点开看「这答案出自《售后政策》第 X 条」, 才敢用。
关键原则:citation 必须从实际命中的 chunk 的元数据生成, 不能让模型自己「报」一个来源。 如果让模型自己说出处, 它可能答的是 A 资料, 却随口标了个 B 来源, 甚至编一个不存在的文件名。 正确做法是:检索时每个 chunk 都带着 source 元数据, 回答时这些 source 直接就是 citation, 和模型说什么无关。
这样才能保证「答案」和「来源」是绑定的,经得起核对。
五、拒答:证据不足时老实说不知道
这是 RAG 可信度的底线,也最容易被忽略。两种情况都要拒答:
- 检索完全为空:知识库里没有相关资料,直接说「没找到,无法回答」。
- 检索到了但证据太弱:最相关的那条得分也很低,说明只是蹭到几个字,并不真的回答问题。这时也该拒答,而不是拿弱证据硬凑。
两种拒答的话术最好分开,让用户(和你自己排查时)能区分「库里没有」和「库里有但不够」。
六、工程上真正会踩的坑(本篇独有)
- 让模型自报来源。模型会张冠李戴甚至编文件名。citation 只能从实际传入的 chunk 元数据生成,这是硬规矩。
- prompt 里不强调「只能基于资料」。模型默认会调用自己的「知识」补充,结果资料和模型记忆混在一起,分不清哪句有据、哪句是编的。grounding 的指令必须写死。
- 没有弱证据拒答,只有空结果拒答。检索蹭到几个词、得分很低也算「命中」,模型就拿这点弱证据硬答。要设一个相关性阈值
min_score,低于它也拒答。 - 拒答阈值拍脑袋定。
min_score太高会把本该答的也拒了(误拒),太低又放进一堆弱证据。这个值要结合评测集调,不能凭感觉,这正是下一篇的事。
七、一句话面试答法
RAG 回答生成要保证什么? 三件事:grounding——用 prompt 把模型锁死在检索到的资料上,不许自由发挥;citation——引用来源必须从实际命中的 chunk 元数据生成,不能让模型自报,否则会张冠李戴;拒答——检索为空或最高相关性低于阈值时,老实说不知道,绝不拿弱证据硬凑。能编造的知识库企业不敢用,所以拒答和引用比「答得漂亮」更重要。
八、动手实践:25 RAG 回答生成与引用来源
把检索结果变成有据可查、可拒答的回答, 演示三件事合一:grounding(基于资料答)、citation(引用实际用到的 chunk)、拒答(无资料或证据弱时不硬凑)。
8.1 在线运行
直接使用本文“可运行源码”中的沙盒执行; 源码、复制内容和实际运行入口保持一致。
零依赖,纯标准库。
8.2 预期输出
问:退货邮费谁出
回答:退货邮费由买家承担,质量问题导致的退货由卖家承担邮费。
引用:[after-sale.md] 退货邮费由买家承担,质量问题导致的退货由卖家承担邮费。
引用:[after-sale.md] 商品签收后 7 天内可申请无理由退货,需保持包装完整。
--------------------------------------------------------
问:邮费贵不贵
拒答:找到的资料相关性不足,不足以可靠回答,建议补充更明确的资料。
引用:无
--------------------------------------------------------
问:你们门店在哪里
拒答:知识库里没有找到相关资料,无法回答这个问题。
引用:无
--------------------------------------------------------
三条路径:第一问强命中,正常回答并带上来源; 第二问只蹭到「邮费」一个片段,证据分低于阈值,拒答; 第三问完全没资料,也拒答。 两种拒答的文案不同——区分「没找到」和「找到了但不够可靠」。
8.3 代码↔概念对应
| 概念 | 在 main.py 哪里 |
|---|---|
| 检索 chunk(带来源) | retrieve |
| 基于资料生成(grounding) | generate_answer 正常分支用 hits[0]['text'] |
| citation 来自实际命中的 chunk | generate_answer 里 citations |
| 无资料拒答 | generate_answer 里 if not hits |
| 弱证据拒答(分数阈值) | generate_answer 里 hits[0]['score'] < min_score |
8.4 说明
真实项目里正常分支会把命中的 chunk 拼进 prompt 发给模型,
让模型用自然语言基于资料作答;
citation 仍然从实际传入的 chunk 元数据生成,不能让模型自己「编」一个来源。
拒答阈值(min_score)需要结合评测集调,太高会误拒、太低会放进弱证据。
这部分调优见下一篇 26。
8.5 可运行源码:RAG 回答生成与引用来源
下方代码就是在线沙盒实际执行的完整源码。
main.py
九、总结
- grounding:答案必须长在资料上:grounding 的意思是「把模型的回答锚定在检索到的资料上」,不许自由发挥。
- citation:出处必须来自实际用到的资料:citation(引用来源)是企业知识库的信任基础——用户能点开看「这答案出自《售后政策》第 X 条」,
- 拒答:证据不足时老实说不知道:这是 RAG 可信度的底线,也最容易被忽略。
- 工程上真正会踩的坑(本篇独有):citation 只能从实际传入的 chunk 元数据生成,这是硬规矩。
- 一句话面试答法:citation——引用来源必须从实际命中的 chunk 元数据生成,不能让模型自报,否则会张冠李戴;
学完自测
选择所有正确答案;提交后逐项核对判断依据。