做RAG项目的人,多半都听老手念叨过一句话:检索抓得准,生成就成功了一大半。这话对,但也不全对。我见过太多团队把向量检索、重排调得漂漂亮亮,Top5召回结果非常准,结果大模型一张嘴就翻车——要么把文档里没出现的信息编得有模有样,要么把多份来源里互相矛盾的内容硬揉成一团,要么干脆无视检索上下文,凭自己的训练记忆自说自话。问题出在哪?十有八九,出在提示词上。
RAG的提示词和普通问答的提示词完全是两回事。它不是简单地把“你是助手”和“根据上下文回答”拼在一起就完事儿。在RAG链路里,提示词至少要承担三重任务:明确告诉模型哪些内容是可信依据、哪些内容可以忽略,约束模型在引用与不编造之间找平衡,同时还要支持多轮对话、处理检索噪声。这篇是【ACP-LLM】RAG系列的第一篇,专门拆解RAG场景下的提示词设计。我会先讲清楚RAG提示词为什么难写,再给出一套可以直接抄的模板结构,最后把我实践中踩过的坑和排查方法全部摊开来说。
如果你是正在做知识库问答、企业文档助手、智能客服这类应用的开发者,或者刚接触RAG想系统性入门的人,这篇文章应该能帮你节省大量试错时间。
1. 先搞清楚RAG提示词到底站在链路的哪个位置
1.1 一次完整问答里,提示词其实有多个入口
很多做RAG的新手对提示词的理解非常单一:认为提示词就是最后发给大模型的那些指令。但如果你把一个标准的RAG问答流程完整走一遍,就会发现提示词至少出现在三个关键位置。
第一个位置是查询改写阶段。用户原始提问可能很口语化,比如“那个方案的成本是多少?”“这个和上一个问题有什么关系?”,这种问题直接拿去向量检索,召回效果往往很差。所以通常要先用一个LLM调用,把用户问题改写成适合检索的正式查询,这一步本身就是一次提示词工程。第二个位置是检索后的上下文组织阶段。你可能召回了5到8个文本块,有的相关、有的不相关、有的互相重叠,你需要决定如何把这些内容拼进提示词,要不要让模型先压缩一下,这些都是通过提示词来控制的。第三个位置才是最终生成阶段,也就是大多数人说的主提示词,包含了系统角色、上下文内容、用户问题这几个部分。
这三个入口任何一个没设计好,整个RAG的效果都会打折扣。我在实际项目里见过不少案例,检索链路完全没动,只是优化了查询改写阶段的提示词,整体准确率就提升了十几个百分点。所以提示词在RAG里不是一个点,而是一条线。
1.2 为什么不能用普通Prompt的思维去做RAG提示词
普通问答的提示词,核心是引导模型调用自身能力去回答,比如“你是一位资深律师,请从专业角度分析……”。模型拥有大量自由度,知识来自参数记忆。
RAG提示词完全不同,它的核心诉求是让模型当一个可信的信息搬运工,而不是一个知识渊博的专家。你严格要求它只依据给定文档回答,这就产生了一个根本性冲突:大模型天生习惯凭记忆补全信息,你要用提示词强行压制这种习惯,让它把上游检索到的内容当作唯一信息来源。
这种差异带来几个直接的提示词设计原则。
第一,上下文不是越多越好。普通Prompt你可以把背景信息、示例、约束条件一股脑塞进去。RAG里塞太多无关块,模型会更倾向于从最显眼但不是最相关的内容里找答案,甚至被噪声内容带偏。第二,指令必须明确区分“依据”和“干扰”。普通Prompt里你不需要告诉模型“忽略某部分内容”,RAG提示词里你要主动声明,检索结果中可能含有无关内容,模型需要批判性地使用。第三,要对模型的“自信”设防。普通问答中有时我们希望模型果断给出判断,但RAG场景里,你必须让模型在信息不足时坦然说不知道,而不是硬答。
一句话总结:普通Prompt是在释放模型的能力,RAG提示词是在给模型的能力上锁,同时只留一扇通往检索结果的窗户。理解了这点,后面的模板设计才立得住。
2. RAG提示词核心组件逐个拆解
2.1 系统级指令:角色、边界与任务范围
整个RAG提示词里,系统级指令(System Prompt)是压舱石,它决定了模型整体以什么姿态处理任务。我常用的写法包含四个要素:角色定义、知识边界声明、任务目标说明、全局约束。
角色定义不能太虚。说“你是一个知识库助手”太宽泛,我更推荐结合业务定位去描述,比如“你是某企业内部制度知识库的问答助手,负责基于已上传的制度文档解答员工疑问”。这样模型会对回答风格有基本预期。
知识边界声明是RAG提示词里最不能少的一句。典型写法是“你只能使用用户提供的检索文档中的信息来回答,不能使用你自己的先验知识”。实测下来,这句话对抑制幻觉有明显帮助,但有一定概率会让模型过度保守,连常识性的补充都不敢说。所以我会在后面加一个豁免条款:“在检索文档信息不足时,你可以基于常识进行一般性补充,但必须明确标注此部分不属于检索文档内容。”
任务目标说明是告诉模型“你面对的是什么样的调用场景”。比如“用户通过对话查询知识库,你的目标是给用户一个简洁、准确、可追溯的答案”,这能让模型主动控制回答篇幅,不会长篇大论把检索到的内容原样倒出来。
全局约束部分通常放输出格式要求。比如“回答中涉及文档内容时,请在句末使用[来源编号]标注对应文档块”“如果检索文档与问题无关,直接回答‘未找到相关信息’,不要尝试猜测”。这些约束在后续小节里会展开细说。
2.2 上下文注入:组块格式与来源标注的写法
RAG提示词里,检索结果的注入方式会直接影响模型的引用精度。很多人在这一步偷懒,把检索到的文本块用普通换行隔开,直接拼进提示词,结果模型根本分不清哪些内容是哪来的,也不会正确引用。
我给一个在实践中非常好用的格式化方案。把检索结果定义为多个“文档块”,每一块用固定标签包裹:
<document> <source>doc_id_001</source> <chunk_id>chunk_003</chunk_id> <content> 这里是检索到的文本块内容... </content> </document> <document> <source>doc_id_002</source> <chunk_id>chunk_010</chunk_id> <content> 这里是另一个检索到的文本块内容... </content> </document>然后在系统提示词中告诉模型:“以上内容中,每个 代表一个检索到的文本片段,
这种结构的价值是双向的。对模型来说,它有了清晰的信息颗粒度,知道引用时该绑定到哪个具体来源;对下游应用来说,用户点击引用标记时,你能准确跳转到对应的原文位置,这在企业知识库场景里几乎是刚需。
这里有一个关键技巧:检索结果在提示词中的摆放顺序会显著影响模型的关注权重。模型对上下文早期的内容往往分配更多注意力。实测下来,把相关性最高的文档块放在所有文档块的最前面,比随机排序能小幅提升答案准确率。如果你的检索排序本身已经做得不错,注意保持这个顺序不变;如果检索分数不可靠,那宁可先做一轮重排或压缩。
2.3 防幻觉设计:置信度表达与拒绝回答机制
RAG最让人头疼的问题就是模型编造文档里不存在的信息。提示词是普通开发者对抗幻觉的第一道防线。
我的做法是在系统提示词中加入压力测试式约束,大意是:“如果检索文档中没有任何内容可以回答问题,你必须明确回复‘知识库中未找到相关信息’,并在后面用一句话说明你的尝试过程,例如‘我检索了与XX相关的文档,但未能找到匹配内容’。绝对禁止编造或猜测答案。”
这个措辞看起来简单,但有两个容易被忽略的细节。一是“说明尝试过程”这个要求,它本质上是在诱导模型进入一个验证性思维模式,降低模型直接“编造”的概率,因为模型要先在逻辑上承认自己找不到,才能给出拒绝回答,而这一步自我声明会触发模型对不确定性的判断。二是“绝对禁止”这种强否定词,中文大模型对强否定词有一定的遵从倾向,虽然不如英文模型稳定,但总体有效。
另外一个很实用的技巧是使用“双通道回答”,比如要求模型在正式回答前先进行内部判断:如果检索内容与问题相关度低于某个阈值,走拒绝流程;否则正常回答。这种让模型先判断再回答的提示词结构,实测比直接要求“根据检索内容回答”的准确率更高,代价是多消耗一些推理时间和Token。
我还会在提示词里固定加入一句:“注意,检索到的文档块可能包含无关或重复内容,应以最相关的部分为准,忽略无关内容。”这句话能明显降低模型被低质量检索块带偏的概率。说白了,RAG系统里检索不可能永远干净,提示词做好“抗污染”设计,比拼命优化检索算法更划算。
3. 实操:从零搭一个能落地的RAG提示词模板
3.1 从业务需求倒推提示词功能清单
直接给模板前,先讲一个原则:提示词不是越多越好,而是要看业务场景需要什么。我拿一个真实做过的“企业制度问答助手”来举例,这个场景的核心需求有三个:支持员工用自然语言查询休假、报销、考勤等制度条款;回答必须给出制度原文依据;多轮对话中能理解“那如果请假超过三天呢”这种指代。
从这三个需求出发,提示词需要的功能点就很清晰了。需要支持查询改写模块,把指代句补全为完整问题;需要强制引用机制,标注来源;还需要在连续对话中保留关键上下文,但避免把上一轮检索结果全部灌入提示词。至于角色设定,则不需要复杂的人设,保持中性、严谨即可。
从需求倒推还有一个好处:你会在设计阶段自然过滤掉不需要的功能。比如如果你的用户只想快速得到是或否的答案,那就不需要让模型输出长篇幅分析;如果不需要跨文档综合,就不用设计复杂的多文档融合逻辑。
3.2 查询改写阶段提示词写法
查询改写是RAG提示词里最容易被省掉的一环。直接用用户原始问题去检索,在多轮对话场景下几乎必然出现召回失败。这里给一个我常用的改写提示词模板:
你是一个面向知识库检索的查询改写助手。你的任务是把用户当前的提问改写成一个适合向量检索的独立查询。 要求: 1. 如果当前问题包含了明确的指代词(如“这个”“那个”“它”“其”),请结合对话历史将其替换为具体名词。 2. 如果当前问题本身已经完整清晰,直接输出原问题。 3. 不要添加对话历史中不存在的信息。 4. 只输出改写后的查询文本,不要有任何解释或前后缀。 5. 改写后的查询应尽量包含领域关键词,便于检索匹配。 对话历史: {chat_history} 当前提问: {user_question}这个模板的核心是第1条和第3条。第1条解决指代消解,第3条防止模型在改写中自己加料,这一点很容易被忽略。有些情况下,模型为了“完善”查询,会把用户没有明说的条件补进去,导致检索结果跑偏。所以“不要添加对话历史中不存在的信息”必须写死。
在一些强结构化领域,我还会在改写指令中追加“关键词扩展”要求,比如“如果查询涉及专业术语缩写,请补充完整名称”。这能提高召回率,但要注意扩展词的准确性,否则反而引入噪声。
3.3 主生成阶段提示词模板与参数配套
这是整个RAG提示词的核心。我给出一个经过多轮调优的中文模板,可以直接复制修改使用:
你是企业知识库智能问答助手。你的职责是基于系统提供的制度文档片段,准确回答员工提出的问题。 严格遵守以下规则: 1. 只能依据上面“检索文档”部分提供的内容进行回答,不得使用自己的先验知识。 2. 如果检索文档与问题无关或不足以回答问题,请回复:“知识库中未找到相关信息”,并简要说明检索方向。 3. 回答中如需引用文档内容,请在句末使用标注格式:[来源编号-片段编号]。 4. 如果多个文档块内容存在冲突,请优先采用出现次数较多、信息更完整的内容,并在回答中说明存在不一致,必要时列举各文档的说法。 5. 回答要简洁、准确,直接给出结论,不要复述检索文档原文,不要输出多余的分析过程。 检索文档: <document> <source>doc_id_001</source> <chunk_id>chunk_003</chunk_id> <content> {检索到的文本块} </content> </document> ... <document> <source>doc_id_002</source> <chunk_id>chunk_010</chunk_id> <content> {检索到的文本块} </content> </document> 对话历史: {chat_history} 用户问题: {user_question}模板写完,要注意配套参数设置。我在RAG任务里通常把temperature设为0.2到0.3之间,既保留一定的表达多样性,又不至于让模型自由发挥到编造答案。top_p设置在0.85左右。有些人会直接把temperature设成0,认为这样最稳定。实测下来,0确实能减少幻觉,但会让回答变得僵硬,尤其在多轮对话中容易重复已有说法。我更推荐0.2,稳而不过度保守。
还有一个重要细节:检索文档部分要放在对话历史之前还是之后?我试过多种排布,最终认为“检索文档—对话历史—用户问题”的顺序效果最好。模型先看到权威依据,再看到历史,最后看到当前提问,整个推理路径最清晰。如果把对话历史放在最前面,模型容易把注意力放在聊天语境上,弱化文档的存在感。
3.4 上下文压缩提示词:解决信息过载
RAG系统召回的结果经常有不相关或重复的情况。原来我直接在主提示词中丢入全部召回块,但在一个检索召回过多、文档块过长的场景下,Prompt总Token会迅速膨胀,既增加成本又影响生成质量。后来我加了一个“上下文压缩”环节,效果很好。
压缩提示词的大致逻辑是:让模型从多个文档块中剔除与用户问题无关的信息,合并重复内容,只保留最关键的部分,并以相同XML格式输出。这一步可以放在生成阶段之前,也可以作为一个后处理步骤。
你是一个检索结果压缩助手。下面是从知识库中检索到的若干文档块,请根据用户问题,从中筛选出最相关的内容进行压缩汇总。 要求: 1. 保留与问题直接相关的信息,剔除无关内容。 2. 对多个文档块中重复的信息进行合并。 3. 压缩后的内容必须保持原始语义,不得修改或用你自己的知识补充。 4. 请保留来源标注信息,压缩后的每个段落仍使用<document>标签包裹。 用户问题: {user_question} 检索文档块: {document_blocks}压缩之后,主提示词里的上下文总量可能减少40%-60%,模型面对的信息噪声大幅下降。不过要留意,压缩本身也是一次LLM调用,会有延迟和成本,适合在检索块数量多或文本块长度大的场景使用。如果检索结果已经非常干净,或者系统对首字延迟要求极高,就不要做压缩,直接用。
4. 常见问题与排查技巧实录
4.1 模型总是无视检索结果,凭记忆回答
这是我被问得最多的问题。现象是检索结果里明明有标准答案,模型却用自己的理解回答,甚至直接编造。排查方向有三个。
第一,看检索块是否放在提示词过靠后的位置。有些框架默认把上下文放在系统提示词之后,看似没问题,但如果上下文很长,模型读到后面注意力已经下降,检索内容反而成了“背景噪音”。解决方法是把检索块前移到紧挨系统提示词的位置,并加上“检索文档”的显式标识。
第二,看上下文块是否过长。当单个文档块超过模型上下文窗口的30%,模型对局部信息的感知会钝化。建议按语义分块,把块长度控制在200到500个Token内。
第三,检查提示词里是否给了模型“跳出”的借口。比如角色设定中带有“你是一位资深HR专家”类似表述,模型会不由自主地启用领域知识,降低对检索内容的依赖。RAG场景的角色描述应中性化,可改为“你是知识库问答系统的输出模块”。
4.2 多文档块内容冲突,模型强行和稀泥
企业制度文档经常出现新版本替代旧版本、A部门与B部门规定不一致等情况。模型默认倾向于在多个冲突观点之间“融合”,输出一个看似中立但实际两边都不对的说法。
我推荐的提示词写法是第3节模板中的第4条:要求模型优先选择信息更完整、出现次数更多的内容,同时要在回答中明确提示“当前检索文档中存在不一致说法”。这样模型收到如实呈现矛盾的指令后,就不太可能强行和稀泥。如果业务上允许,还可以再加一条“如果冲突无法调和,请分别列出各来源说法,让用户自行判断”。
这里有个需要用提示词配合后端的点:平时要做好版本号管理。如果文档块中有明显的更新时间字段,可以在提示词中加入“优先使用较新版本”的规则。但这需要保证分块时把版本号一并保留了。
4.3 提示词Token超限,正文内容被截断
这是工程上必然会遇到的问题。提示词里塞了历史对话、检索块、系统指令,一个大点的知识库问答很容易超过模型的上下文窗口限制。把上下文窗口扩大是一个办法,但成本会直线上升,而且过长的上下文反而会引入新的注意力噪声。
我的实践做法是三层递进。第一层,检索块只保留最相关的TopK,K值从5可以压到3,很多场景下准确率不降反升。第二层,对话历史做滑窗,只保留最近两轮。第三层,启用压缩提示词,也就是第3.4节的内容,先让模型把检索结果缩掉一半再进入主提示词。
另外提醒一句,设定Token上限时不能卡在模型窗口的最大值,要为生成留出至少30%的余量,否则可能出现生成一半被系统截断的情况。这属于工程红线,踩过的人都知道。
4.4 提示词效果到底怎么评估
RAG提示词的改动如果没有量化评估,约等于在盲调。我给一个轻量级评估思路:准备一个30-50条问题的小测试集,按业务重点分成几类,比如“简单事实问答”“多跳推理”“指代追问”“无法回答类”。每一轮修改提示词后,跑一遍测试集,用三个方面打分:答案准确率、引用命中率、拒答正确率。
下面是我常用的一个小记录表:
| 问题类型 | 用例数 | 准确率 | 引用命中率 | 拒答正确率 | 备注 |
|---|---|---|---|---|---|
| 简单事实问答 | 15 | 93% | 87% | - | 模板v3后引用下降 |
| 指代追问 | 10 | 80% | 90% | - | 改写模块修复指代后提升 |
| 无关问题 | 10 | - | - | 70% | 拒答偏高,考虑放宽边界 |
评估完不能只看总体准确率。引用命中率单独看很重要,它是判断模型是否真正“跟着文档走”的关键指标。如果准确率还不错但引用经常张冠李戴,说明提示词对来源标注的约束不够,需要像2.2节那样强化格式绑定。
5. 从提示词看向RAG系统的整体调优
5.1 提示词不是万能的:边界在哪
做提示词做得越久,越要承认一个事实:提示词能解决的,只有“模型会用检索结果”的问题;如果检索本身召回的就是一堆错内容,提示词写得再精妙也无济于事。
但提示词可以作为检索质量的一面镜子。当模型频繁触发“知识库中未找到相关信息”的拒答分支,大概率是查询改写或召回环节出了问题;当模型总是引用同一个文档块,大概率是检索排序对某类内容存在偏置。用好拒答率和引用分布这两个信号,你能快速定位到RAG链路中真正需要优化的环节。
所以我的建议是,做RAG不要一头扎进提示词调优里出不来。提示词的合理投入周期是两周左右,达到稳定效果后,应该把精力逐步移向分块策略、Embedding选型、重排模型这些同样影响最终效果的方向。
5.2 关于【ACP-LLM】这个系列的安排
这篇文章是【ACP-LLM】系列中关于RAG提示词的入门篇。后续我会继续整理RAG相关的更多主题,包括分块策略的细节与评估、查询改写进阶、多路召回与重排实践,以及RAG系统整体评测方案。每个主题都会保持本篇的风格,少讲空泛的原理,多讲可复用的方案和踩坑记录。
如果你正在做RAG应用,欢迎对照本篇的模板先跑一个最小可行性版本,再根据你的场景去调整。提示词这个东西,看起来是写几行字的事,但里面藏着大量需要实测才知道的经验。
最后再分享一个我个人的习惯:每次修改RAG提示词,我都会把改动版本、评估分数、甚至当时模型的输出样本截图一起归档。这样当线上效果出现波动时,我可以快速回溯是提示词改动引入的,还是上游数据或检索变化导致的。这个习惯帮我省了非常多的排查时间,也推荐你试试。