RAG 的 Prompt 与答案边界:只根据上下文回答,不知道就说不知道
围绕PromptTemplate、RetrievalQA、return_source_documents 以及 stuff、map_reduce、refine 的示例,讲清 RAG 生成阶段如何约束答案、保留引用,并在证据不足时拒答。
相关工具
RAG 的 Prompt 不是让模型更会聊,而是让它守住材料
很多人写 RAG Prompt 时,会习惯性地要求模型“专业、详细、逻辑清晰”。这些要求没有错,但它们不是 RAG 最先要解决的问题。RAG 的核心不是让模型自由发挥,而是把模型的回答收在检索到的上下文里。
相关概念 里的 `PromptTemplate` 示例很直接:使用检索到的上下文回答最后的问题;如果不知道答案,就说不知道,不要试图编造。这个模板不花哨,但它抓住了 RAG 生成阶段最重要的边界:答案必须来自 `context`。
如果检索阶段已经把 `source_documents` 取回来了,Prompt 的任务就是让模型读这些材料、回答用户问题,并在材料不足时停下来。它不是知识竞赛,也不是让模型凭训练记忆补全缺口。

一个可用的 RAG Prompt 至少要有任务边界、context、question 和回答规则,并配合 source_documents 保留证据。
context 和 question 要分清楚
相关的模板把 `{context}` 和 `{question}` 分开。这个结构很重要。`context` 是检索系统提供的材料,`question` 是用户真正要问的问题。两者混在一起,模型就更容易把用户话里的假设当成事实。
比如用户问“这门课会学习 Python 吗”,系统先检索课程资料,再把资料放进 context。模型应该从 context 中找证据,而不是因为问题里出现 Python 就顺着回答。示例里也通过 `RetrievalQA` 把检索器、Prompt 和 LLM 串起来,让问题先进入检索,再进入生成。
实际应用中,Prompt 可以写得朴素一点:根据上下文回答问题;如果上下文没有答案,就说不知道;不要使用上下文之外的信息;必要时列出来源。越接近这个骨架,越容易排查问题。
不知道就说不知道,是产品规则,不是语气问题
“不知道就说不知道”听起来像一句普通提示,其实是 RAG 产品的安全边界。知识库里没有材料时,系统不应该靠模型常识补一个看似合理的答案。尤其是制度、合同、接口、医疗、财务这类资料,编一个答案比拒答更危险。
相关的英文示例里也出现过类似情况:上下文没有给出清楚答案时,模型回答 context 不能提供明确答案。这比强行总结要好。RAG 的可信度不是来自每次都能答,而是来自知道什么时候不能答。
拒答也不应该只写成一句冷冰冰的“不知道”。可以告诉用户:当前资料里没有找到明确依据;建议补充文档、缩小问题范围,或者查看某个来源是否未被索引。这样既守住边界,也能帮助用户下一步处理。

检索到相关上下文时生成回答并引用来源;没有相关上下文时,系统应明确回答不知道,而不是补写。
return_source_documents 让答案能被核对
资料 在构建 `RetrievalQA.from_chain_type` 时设置了 `return_source_documents=True`。这个参数不只是方便调试,也直接影响用户信任。没有来源的回答,即使内容正确,用户也很难判断它是不是从自己的资料里来的。
上线后的 RAG 页面,最好把引用做成默认能力。回答下面展示文档名、页码、片段摘要,用户点击后能回到原文。对内部系统来说,这比一段漂亮的自然语言更重要。用户不是只想听一个答案,还想知道答案能不能被核对。
引用也会反过来约束模型。如果答案里的说法找不到对应 source_documents,就应该被标记出来。评估时可以单独检查“答案是否有引用”“引用是否支撑答案”“页码是否正确”。
stuff 适合先跑通基线
文中先用默认的 `stuff` 创建 QA 链。`stuff` 的方式最直接:把检索到的文档一起放进上下文,一次交给模型回答。资料不多、chunk 不长、k 不大时,它通常是最容易理解、也最容易调试的方案。
它的好处是链路短。答案不好时,你只需要看两件事:source_documents 对不对,Prompt 有没有守住边界。如果这两件事都没问题,再考虑模型或参数。对大多数教程型、FAQ 型、内部轻量知识库,先用 stuff 建基线很合适。
但 stuff 也有上限。上下文太长时,模型会被塞进太多材料;证据分散在很多文档时,一次性拼进去也可能超过窗口。这个时候才需要考虑其他 chain_type。
map_reduce 和 refine 不是越复杂越好
资料 对 `map_reduce` 和 `refine` 也做了实验。`map_reduce` 会先让每个文档生成中间答案,再合并;`refine` 会基于前一个答案逐步读新文档并修正。它们看起来更高级,但并不意味着一定更好。
`map_reduce` 适合文档很多、需要先分块处理的情况。问题是中间答案可能已经丢掉细节,最终合并时也可能变得笼统。`refine` 适合证据分散、需要逐步补充的场景,但速度和成本都会上去,输出稳定性也要单独评估。
选择 chain_type 时,不要把复杂当成默认。先用 stuff 跑出可解释的基线,再用同一批问题比较 map_reduce 和 refine。比较时不要只看答案顺不顺,还要看引用是否准确、有没有遗漏关键 source_documents。

stuff 一次送入上下文,map_reduce 分段回答再合并,refine 逐步修正答案。选择前先看文档长度和证据分布。
Prompt 要配合检索结果,而不是替检索背锅
如果 source_documents 里没有正确材料,Prompt 很难把答案变准。它最多能让模型更谨慎一点,比如拒答或提示资料不足。真正要解决缺材料的问题,还是要回到切分、embedding、k、MMR、metadata filter 和向量库。
反过来,如果检索结果已经对了,Prompt 也不能太松。它要明确告诉模型只用上下文,不要补充上下文之外的内容;如果引用不足,不要把推测写成结论;如果用户的问题超出资料范围,要说明资料没有覆盖。
RAG 的稳定来自两边配合:检索阶段给对材料,生成阶段守住材料。只调 Prompt,不看检索;只调检索,不约束回答,都会留下漏洞。
一个可用的答案模板应该保留三件事
第一,直接回答用户问题,不要先写一大段背景。第二,说明依据来自哪些 source_documents,最好带 source 和 page。第三,材料不足时明确说不知道,并告诉用户缺什么材料。
比如课程问答里,用户问“这门课会学习 Python 吗”,如果上下文里明确提到 Python,就回答会,并给出来源页;如果上下文只讲课程目标,没有出现 Python,就不要靠常识猜。这个判断看起来简单,却是 RAG 和普通聊天最大的差别。
写 Prompt 时可以少一点修辞,多一点约束。模型不缺表达能力,缺的是边界。把边界写清楚,答案才更像资料系统,而不是随口聊天。
常见问题
RAG Prompt 最重要的一句话是什么?
只根据上下文回答;如果上下文没有答案,就说不知道,不要编造。这正是 示例中 PromptTemplate 的核心意思。
为什么要返回 source_documents?
因为 source_documents 能让答案被核对,也方便调试。用户可以看到答案来自哪份文档、哪一页,使用者也能判断检索是否正确。
chain_type 默认应该选哪一个?
多数轻量 RAG 先用 stuff。它链路短、容易调试;只有上下文太长或证据分散时,再测试 map_reduce 或 refine。