最近在搭一个简单的RAG问答系统,用的是LangChain + OpenAI的embedding,检索效果还行,但LLM回答时总喜欢“自由发挥”——比如用户问“张三在2023年说过什么政策”,明明库里只有一段原文,它非要自己总结一遍,甚至加些原文没有的细节。我试过在system prompt里写“请严格引用原文回答”,但效果不稳定,有时候还是乱编。想请教下大家,有没有什么prompt技巧或者模板能让模型更“死板”一点?另外,如果原文很长,是不是应该把检索到的片段分段塞进prompt,还是直接整段放进去比较好?感谢!
RAG系统里prompt怎么写才能让LLM更准确引用原文?
全部回复
共 141 条我之前也遇到过这问题,后来在prompt里加了个例子,就是给一段原文和对应的标准回答,模型立马老实多了。另外建议把检索片段按相关性排好序,用清晰的标记隔开,比如【片段1】这种,再明确告诉它只能从这些标记里选内容。长原文的话分段塞效果更好,整段塞容易让模型抓不住重点。
试试在prompt里加一句“只输出原文中出现的词句”,长文本就分段塞,整段容易让模型抓不住重点。
试试在prompt里加一句“若原文无对应信息就直说不知道”,再把检索片段按段落拆开带编号,让模型先引用再解释。
我之前也踩过这个坑,后来发现光靠system prompt压不住,得把原文直接塞进user message里,并且明确告诉它“只能从下面这段文字里找答案,找不到就说不知道”,效果会稳很多。分段塞的话我试过,比整段好,但别分太碎,不然模型容易抓错重点,而且每段前面加个编号,让它在回答里引用编号,能明显减少编造。另外你可以在prompt里加一句“不要使用原文没有的细节”,比干巴巴的“严格引用”管用。
我最近也在搞类似的RAG,发现光靠system prompt压不太住,核心问题其实是模型把“引用”和“总结”混为一谈了。你可以试试把检索到的原文直接拆成带编号的小段落,然后在prompt里明确告诉它“回答必须包含[1][2]这样的标记,并且每个标记后面只能跟原文原句”。这样等于把引用变成了硬性格式约束,比单纯说“请引用”有效得多。另外关于长原文,我建议别整段塞,因为token一多注意力就容易分散,反而更容易自由发挥——分段后每段加个简短标题,让模型先定位再摘录,效果会稳很多。还有个土办法,就是故意在prompt里加一句“如果原文没有直接答案,请回答‘未找到相关信息’”,能逼它少编造。不过说实话,prompt只是兜底,你最好还是检查一下embedding的chunk大小和重叠率,有时候检索回来的片段本身就不够精准,模型再听话也白搭。
我最近也在搞类似的东西,发现光靠prompt压“自由发挥”真不太够,更管用的是把检索片段直接切成小块,每块前面标上“编号+原文引用”,然后明确要求模型回答时只能用“引用[编号]”的方式输出,不许自己转述。你试过把system prompt里“请严格引用”改成“如果回答内容不在给定片段中,请直接说不知道”吗?这个对减少幻觉挺有效的,至少模型会更容易承认自己没找到。另外原文长的话建议分段塞,但别一次性塞太多,我一般控制在每段不超过300字,并且按相关度排序,这样模型更愿意去钻细节。还有个土办法,就是在user prompt里把问题重复一遍,再告诉它“下面这些是唯一可信信息源”,效果比只放system里强不少。不过说到底,prompt只是兜底,真正稳的还是得靠检索质量,要是top1没命中,再怎么prompt它也得编。你用的embedding模型是哪个?我觉得换一个更适配你领域数据的embedding可能比调prompt收益更大。
试试在user prompt里把原文用引号包起来,再加一句“只能引用引号内内容”,比system指令管用多了。
遇到过同样的问题,尤其是用gpt-3.5的时候特别明显。后来我试了个土办法,把检索到的原文按段落拆开,每段前面加个编号,然后prompt里明确要求“回答时只能引用编号段落里的原句,不能改写,不能拼接”。效果比单纯写“严格引用”好很多,但代价是回答经常很生硬,像在念稿子。另外,原文太长确实得分段,但别分太碎,我试过把一段完整的话拆成两句塞进不同位置,模型反而容易混乱,甚至自己脑补出逻辑关系。还有个坑是,如果原文里有多个类似句子,模型可能挑错引用的目标,这时候就得在prompt里加个“优先引用与问题关键词重合度最高的原句”这种指令。说到底,prompt只是软约束,真想让它死板,可能还得靠后处理——比如用相似度匹配强制从检索结果里抽句子拼答案,而不是完全信任LLM的生成。你用的是哪种模型?有些模型对指令的服从度差异挺大的,调prompt前可能得先换模型试试。
试试在prompt里加一句“若原文无对应内容,直接说不知道”,再配合分段塞入,效果能稳不少。
我之前也踩过这个坑,后来发现光靠system prompt压不住,得在user prompt里把原文用xml标签包起来,再明确告诉模型“只能引用里的内容,禁止改写”。另外分段塞比整段塞效果好,但别切太碎,每段保持一个完整语义块,不然模型容易抓瞎。还有就是temperature调低一点,0.1左右,能肉眼可见减少自由发挥。
试试在prompt里加一句“只输出原文中存在的表述,不要推测或改写”,同时把检索片段按来源拆开标注,确实比整段塞进去稳。
我之前也踩过这个坑,后来在prompt里加了个硬性要求:必须用原文的连续片段作答,不能改写,如果找不到就明确说“未检索到”。你可以试试把system prompt改成“你是引用机器,只能输出检索片段中的原话,禁止概括”,再把检索到的原文用特殊标记包起来,比如在每段前后加【原文开始】【原文结束】,模型会老实很多。至于长文本,建议按语义切块后每个块单独给一个编号,在prompt里让模型先选编号再引用,比整段塞进去准确率高不少,不然它容易漏掉关键信息。
试过在prompt里加“只输出原文相关内容,禁止解释”,效果比单纯说引用好不少,你可以试试。长文本分段塞进去确实更稳,整段容易让模型抓不住重点。
我最近也踩过这个坑,后来发现光靠system prompt压不住,得在user prompt里把引用源切分成小块,每块前面标上编号,然后明确要求模型“只能引用编号片段里的原话,禁止改写”。另外温度调低到0.1以下会有奇效,基本能杜绝自由发挥。至于长文本,分段塞进去确实比整段好,模型对开头和结尾的内容更敏感,中间容易漏。
我之前也踩过这个坑,后来发现光靠system prompt不够,还得在user prompt里把原文用引号包起来,再明确告诉模型“只准用引号里的内容,不许自己改写”。另外如果原文太长,分段塞进去确实比整段好用,但每段前面最好加个编号,不然模型容易搞混引用来源。你试过给检索片段加个“原文”和“问题”的明确分隔符吗?我这么改完,幻觉少了很多。
试试在prompt里加一句“如果原文没有就直接说不知道”,顺便把检索片段拆成带编号的小段喂进去,效果会稳很多。
我之前也踩过这个坑,后来发现光靠system prompt压不住,得在user prompt里把原文用特殊符号框起来,比如“以下是原文:<<<...>>>”,然后明确要求“只能引用符号内的句子,禁止改写”。另外分段塞确实比整段好,但别切太碎,按语义块切,不然模型容易丢失上下文。还有个土办法,就是让模型先输出“引用原文:”再给结论,这样它不得不先抓原句。
试试在prompt里加一句“若原文无对应内容,直接回答未找到”,比单纯要求引用管用。长文本还是分段喂吧,整段塞容易让模型抓错重点。
试试在prompt里把原文用引号包起来,再加一句“只能从引号内提取信息”,我这么改之后幻觉少多了。
我之前也踩过这个坑,后来发现光在system prompt里强调没用,得把原文直接放进user prompt里,并且明确要求“只能从下面这段文字中提取信息,禁止使用外部知识”。另外分段塞比整段塞效果好,因为太长的话模型容易抓不住重点,而且我会在每段前面加个编号,让它在回答里标注对应编号,这样引用准确率会高不少。