主题
生成:Prompt 设计、标注出处、查不到时拒答
本章要回答的问题:检索对了,模型为什么还会答错?怎么让它标出处?什么时候该拒答?
前七节把“资料找对”做到了。这一节是最后一公里:把 5 条资料交给模型,让它答得对、指得准、不知道就说不知道。
三件事里,标出处最难。答案对不对,人一眼能看出来;引用准不准,得逐条回查。而用户真正会拿来追责的,是引用。
提示词只做三件事
本书的提示词很短,约束只有三条:
- 只能依据资料,不得使用资料以外的知识;
- 每个结论后用
[编号]标来源,编号必须来自给定的 5 条; - 资料不足时只输出「资料中没有相关信息」。
模型是 Qwen3.5-9B-Q4_K_M,温度 0,最多 384 token。资料按 [1](doc clause)正文 拼装,5 条约 1300–1800 token。
实测:带资料与不带资料
同一批 80 题,两种模式各跑一遍:
- RAG:给 top-5 资料,按上面的提示词;
- 闭卷:不给资料,让模型凭参数知识回答。
自动判定用“标准答案的 2-gram 覆盖率 ≥ 0.6 且数字全部对上”,结果如下(实测,见 benchmarks/rag/generation.csv):
| 题型 | RAG 正确率 | 闭卷正确率 |
|---|---|---|
| 直接摘取(20) | 0.95 | 0.80 |
| 条件判断(20) | 0.75 | 0.50 |
| 跨段组合(20) | 0.35 | 0.00 |
| 拒答(20) | 20/20 拒答 | 19/20 拒答 |
| 合计(60 道可答题) | 0.683 | 0.433 |
闭卷在法规题上并不差:Qwen3.5-9B 见过《安全生产法》,直接摘取能到 0.80。这正是“模型好像已经知道”的错觉来源。 但一到跨段组合,闭卷是 0——它没有两条条款同时在场,也指不出出处。
自动判定会低估,人工复核才是准的
自动判定对“换个说法”和“中文数字 vs 阿拉伯数字”很苛刻。比如标准答案写“十万元”,模型写“10万元”,数字比对就失败。
所以本书对 60 道可答题逐条人工复核,标签写在 benchmarks/rag/generation-audit.csv:
| 题型 | 自动判定 | 人工严格正确 | 人工含部分正确 |
|---|---|---|---|
| 直接摘取 | 0.95 | 20/20 | 20/20 |
| 条件判断 | 0.75 | 19/20 | 19/20 |
| 跨段组合 | 0.35 | 11/20 | 14.5/20 |
| 合计 | 0.683 | 50/60(83.3%) | 53.5/60(89.2%) |
(人工标签见 benchmarks/rag/generation-audit.csv;部分正确按 0.5 计。)
两个数字差 15 个百分点。结论不是“自动判定没用”,而是:自动判定适合做回归哨兵,不适合做对外结论。 每次改提示词或换模型,先用自动判定看有没有大退步;要写进报告的数字,抽样人工复核。
跨段题仍然最差:11/20 严格正确。典型错误是只答了一半、或者把两条条款的罚款金额串了。这是 5.6 的 joint recall 在生成端的体现——检索没把两条都找齐,模型只能答一半。
出处:命中容易,精准很难
自动统计(generation.csv):
- 引用命中率 0.95:95% 的可答题,至少有一个引用指向 gold 块;
- 平均引用 3.03 条:60 道题共引用了 182 次;
- 引用精确率 0.363(66/182,微平均;按题平均 0.471):引用的条目里,只有约三分之一是真正相关的;
- 9/60 道题把 5 条资料全引了。
(引用精确率由 generation.jsonl 的 cited 与 gold 块对照算出;微平均是相关引用数 ÷ 总引用数,口径见 benchmarks/rag/README.md。)
这说明“能标出处”和“标得准”是两件事。提示词只说“每个结论后标来源”,模型最省事的做法是把所有资料都列上;用户回查时,约三分之二的引用对不上,信任就没了。
带引用的生成已经有系统的评估方法(引用 [2]),但前提是引用能被逐条核验。工程上的改法不是加一句“只标相关的”,而是约束输出结构:让模型输出 JSON,每个结论带 quote(原文片段)与 source,再用程序校验 quote 是否真的出现在对应块里。对不上就丢弃该引用,或整题降级为“资料不足”。
拒答:模型太“乐于助人”
RAG 模式下 20 道拒答题全部拒答,可答题没有一道误拒。这个结果很好,但不是自动来的(6.1 的二次审计发现 R12 其实可由专利回答,v2 已退役;它原本被算在 20 道拒答里,按更严的口径应记 1 次误拒,所以 v1 的 20/20 应读作 19 道真拒答 + 1 道误拒):
- 提示词明确给了固定句式「资料中没有相关信息」,比“请判断是否能回答”更容易执行;
- 温度 0,减少了“自由发挥”;
- 拒答题本身很“明显”(问营收、问员工电话),不是边界情况。
闭卷模式漏了 1 道(R12,注塑工艺参数):模型没有拒答,而是给了一段“参数因材料而异、需试模确定”的通用建议。 这段话不算错,但它不是资料里的内容,也没有出处——在私有知识库里,这就是编造。
真实的拒答边界比这 20 题难:资料里有一半答案、问题需要跨文档、条款已废止但文本还在。RGB 这类中文 RAG 基准专门测了噪声鲁棒性与拒答(引用 [3]),结论也一样:模型倾向于“给点什么”,而不是承认不知道。拒答率是一个产品参数,不是模型能力: 定得太松,用户拿到似是而非的答案;定得太紧,能答的也不答。要用业务上的“错答代价 / 拒答代价”来定阈值。
为什么生成端要单独评估
检索指标(Recall@k、nDCG)只说明“资料在不在”,不说明“模型会不会用”。同一批 top-5,换提示词、换模型、换温度,答案质量都会变。
生成端至少要报四个数:
- 答案正确率:分题型,人工复核;
- 引用命中率:至少一个引用对;
- 引用精确率:引用的条目里有多少真的相关;
- 拒答率与误拒率:该拒的拒了没有,不该拒的有没有拒。
只报第一个,会漏掉“答案对但引用全错”的情况;只报引用,会漏掉“引用对但答案编了”的情况。
工程含义
引用要用结构约束,不要用自然语言请求。 JSON + quote + 程序校验,比“请只引用相关段落”可靠得多。
拒答要有固定句式。 模型对“资料不足时请拒答”这种抽象指令执行不稳;给一个必须逐字输出的句子,判定和统计都简单。
答案正确率必须人工抽样。 自动指标会被改写和数字格式骗;本书 60 题自动 0.683、人工 0.833,差 15 个点。
把“部分正确”单列。 跨段题最容易只答一半;二值判定会把“答了一半”和“完全胡说”混在一起,掩盖真正的问题。
动手实验
动手实验:把引用改成 JSON + 原文片段
假设:要求模型输出 {"answer": ..., "citations": [{"source": 2, "quote": "..."}]} 并用程序校验 quote 是否出现在对应块后,引用精确率能从 0.363 升到 0.7 以上,答案正确率基本不变。
做法:改 generate.py 的提示词与解析逻辑;对 quote 做子串匹配(去空白),不匹配的引用直接丢弃;在 80 题上重跑,人工复核 20 条。
记录:引用命中率、精确率、答案正确率、拒答率、平均输出 token。注意:JSON 输出会增加 token 与解析失败率,失败时要降级为纯文本,不要让整题报错。
参考资料
- Lewis, P. et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks(RAG 的生成端公式,NeurIPS 2020). https://arxiv.org/abs/2005.11401
- Gao, T. et al. Enabling Large Language Models to Generate Text with Citations(带引用的生成与自动评估,EMNLP 2023). https://arxiv.org/abs/2305.14627
- Chen, J. et al. Benchmarking Large Language Models in Retrieval-Augmented Generation(RGB:噪声鲁棒性、拒答与信息整合). https://arxiv.org/abs/2309.01431
- Liu, N. F. et al. Lost in the Middle: How Language Models Use Long Contexts(上下文位置对生成的影响). https://arxiv.org/abs/2307.03172