最近在做一个人事政策问答的RAG项目,用的是智谱的API,向量检索top5召回后拼进prompt让模型回答。我试了好几种写法:有的把检索内容放前面,有的放后面,有的加上“请仅根据以下资料回答”,有的让模型先判断相关性再回答。结果发现效果极不稳定,同一套prompt换个问法就翻车,比如问“年假怎么算”能答对,问“我入职两年能休几天”就开始胡诌了。也试过给few-shot示例,但不同岗位问题差异太大,示例反而会带偏。想请教一下各位,RAG场景下的prompt到底有没有一个比较通用的结构?还是说要针对问题类型动态切换prompt模板?另外RAG输出不稳定的问题,是prompt的锅还是检索的锅?真心被搞晕了。
Prompt工程在RAG场景下到底该怎么调?调了半天效果还是飘忽不定
全部回复
共 44 条“入职两年能休几天”这种问法翻车,大概率是检索没把年假计算规则那块召回来,top5里可能全是政策概述。建议先把检索命中率测一下,拿几个bad case看看context里到底有没有答案,没有的话调prompt也白搭。prompt这边可以试试让模型先复述资料再回答,能压一压它自己发挥的冲动。few-shot确实不适合这种场景,问题太散,不如在system里把“资料没有就说不知道”卡死。
你这个情况太典型了,我去年做员工手册问答时几乎一模一样。说白了RAG输出飘,十有八九锅在检索上,prompt只能算放大器。你问“年假怎么算”能答对,是因为召回的chunk里大概率直接有那条规则;问“入职两年能休几天”就胡诌,很可能是top5里压根没把工龄对应的档位表捞回来,或者捞回来的是残缺片段,模型只能硬编。所以先别急着换prompt模板,拿十几个bad case去翻检索结果,看该出现的条文到底在不在context里,这一步能省你一半精力。至于prompt结构,我现在的习惯是把指令放最前面、检索内容用明确分隔符包起来、最后再重复一遍关键约束,比如“若资料未提及,直接说不知道”,比只写一次“仅根据资料回答”稳不少。动态切模板这事我觉得值得做,但别一上来就搞很多套,先按“事实查询”和“计算推理”分两类试试,后者可以在prompt里要求模型先列出用到的条款再算,能压住不少瞎编。few-shot在RAG里确实容易带偏,尤其你这种岗位差异大的场景,不如换成给一两个“资料不足时如何拒答”的示例,反而更通用。
我之前也踩过这个坑,说点个人经验吧。你描述的那种“问年假能答对、问入职两年休几天就胡诌”的情况,我第一反应不是prompt的问题,而是检索那块可能就已经歪了。因为“入职两年能休几天”这种口语化问法,向量检索很容易召回一些泛泛的考勤制度段落,而不是具体按工龄分档的那条,模型拿不到关键数字,只能自己编。你可以先单独把每次的召回内容打出来看看,如果top5里压根没有工龄对应天数的原文,那再怎么调prompt都是白搭。至于prompt结构,我觉得没有万能模板,但有几个点比较稳:把检索内容用明确的分隔符包起来,指令放在检索内容之后而不是之前,并且要求模型在资料不足时直接说“未找到依据”而不是硬答。few-shot确实要慎用,岗位差异大的场景下示例容易变成干扰项,不如把精力花在query改写和检索召回上。RAG的不稳定,十有八九是检索和生成之间的锅甩来甩去,建议你先做个小的评估集,把召回命中率和最终答案正确率分开统计,这样才知道该往哪使劲。
我做过类似的HR问答,感觉你这个问题大概率是检索的锅。问“入职两年能休几天”这种带推理的问题,top5召回的可能全是年假制度原文,但模型得自己算工龄对应天数,检索里没这个映射关系就容易瞎编。你试试在prompt里加一句“如果资料中没直接给出答案,请说明需要哪些信息”,比逼着它硬答会稳一些。另外few-shot示例别用不同岗位的,挑同类型问题的两三个就行,太多反而干扰。