最近在搭一个客服场景的AI Agent,用RAG做知识库检索。遇到一个头疼的问题:多轮对话里,用户会问“那它价格呢?”或者“具体怎么用啊”,这种指代性很强的问题。我现在是把整个历史对话拼到query里重新检索,但结果经常跑偏——模型会去匹配历史里的“老问题”,而不是当前真正的意图。试过只截取最后两轮,但有些上下文又不够。想请教一下各位大佬,你们在Agent+RAG里是怎么处理多轮历史信息的?是直接丢给LLM去改写,还是有什么更好的策略?求具体方案或踩坑经验。
RAG+Agent做多轮对话时,历史记录怎么塞才不会让检索变“智障”?
全部回复
共 150 条试试让LLM先做query改写,把指代消解成独立问题再检索,历史只保留跟当前意图相关的片段,别全塞进去。
试试让LLM先把指代消解成独立query再检索,历史只留事实别留问题,效果会稳很多。
我踩过一模一样的坑,后来直接放弃拼历史,改成让LLM先做一步query改写,把指代和省略补全成独立问题再去检索。你可以试试用流式对话里的最近两轮加一个“当前轮”组合提示词,让模型输出“用户真正想问的完整问题”,效果比硬拼好很多。
另外历史不是越多越好,建议给每轮对话打个时间或主题标签,检索时只取跟当前query语义最相关的片段,再让LLM决定要不要追加上下文。你可以给改写加个置信度阈值,不确定时就回退到只拼当前轮,至少不会跑偏太远。
我最近也在搞这个,踩过一样的坑。后来改成先用LLM把当前问题和历史对话压缩成一个独立的“查询意图”,再拿这个去检索,效果好很多。你可以试试把历史里跟当前问题相关的实体和条件提取出来,拼成一句完整的话,别一股脑全塞进去。另外历史轮次真不是越多越好,我试过带权重的滑动窗口,最近几轮给高分,老早的只保留关键信息,这样检索漂移少多了。
我们团队之前也踩过这个坑,后来改成把历史对话先让LLM做一轮query改写,只抽当前问题对应的实体和意图,再拿改写后的query去检索,效果比直接拼历史稳很多。另外建议给历史轮次加个权重,比如最近两轮全量保留,更早的只保留关键实体,这样既不会丢上下文,也不会让老问题干扰检索。你可以试试看,改写的prompt里明确要求“忽略已解决的主题”。
先让LLM把历史对话压缩成当前问题相关的背景,再拿这个去检索,效果会稳很多。
我之前也踩过这坑,光拼历史query确实容易跑偏。后来改成先把历史对话压缩成“用户当前诉求摘要”,再和当前问题拼一起去检索,效果好很多。另外建议把指代消解单独拎出来做个步骤,直接用LLM判断哪些历史信息是当前真正需要的,别全塞进去。
还有个思路是给历史片段加时间权重,近几轮的高亮,远的就只保留实体和意图,这样检索时不会一锅烩。不过具体还得看你知识库的粒度,如果文档太长,摘要反而会丢关键细节,多试试不同方案吧。
先让LLM把指代补全成独立query再检索,历史只用来改写别直接塞。
我也踩过这个坑,全塞历史确实容易把检索带偏。后来改成先用小模型做query改写,把“那它价格呢”补全成“XX产品的价格是多少”,再拿改写后的query去检索,效果稳很多。另外历史也不是全丢,一般保留最近3-5轮加一个摘要就够了,太老的对话对当前检索基本是噪音。你们那边有没有试过让LLM先判断这轮要不要检索?有些寒暄或追问其实不用走RAG。
这个问题我踩过坑,说下我的做法。直接拼历史确实容易让检索跑偏,因为embedding会把历史里的关键词权重拉高,当前query反而被稀释了。我现在是用LLM做query改写,但只让它参考最近一两轮,把“那它价格呢”这种指代消解成完整的独立问题,再去检索。关键是改写那步的prompt要限制它别过度发挥,不然会把用户没问的东西也塞进去。另外我会在检索时给当前query更高的权重,历史只作为改写素材而不是直接参与匹配。还有个思路是做意图路由,先判断这轮是不是依赖上下文,如果是就改写,不是就直接检索。截取两轮确实不够,我一般保留最近三轮加一个摘要,摘要用LLM压缩成一句话背景。这样既不会丢上下文,也不会让老问题干扰检索。