最近在搭一个简单的AI Agent,用RAG做知识库检索。单轮问答效果还行,但一进入多轮对话就出问题——比如用户先问“今年的财报营收如何”,我检索了相关文档,回答后他接着问“那利润呢”,结果Agent把“利润”当成一个新问题去检索,没结合上一轮的“财报”和“今年”。我试了把历史对话拼进query,但检索结果反而变杂了,有时还跑偏。想请教下大家:怎么设计检索策略才能让Agent在多轮里保持上下文一致性?有没有什么轻量级的做法?
RAG+Agent做多轮对话时,文档检索总丢上下文怎么办?
全部回复
共 147 条试试把历史对话先做一轮意图压缩再检索,比如用LLM把“那利润呢”改写成一个带完整上下文的独立问题,而不是直接把多轮历史拼进query。我之前遇到类似问题时发现,重写后的query质量比拼接历史高很多,噪音也少。另外可以给历史轮次加个权重衰减,只保留最近两轮的关键实体,这样能减少跑偏概率。你现在的改写prompt里有没有明确要求提取“财报”和“今年”这类限定词?
我之前也踩过这个坑,拼历史query确实容易让检索向量变糊。后来我是把上一轮的核心实体和意图抽出来,拼到当前问题后面,而不是全量丢进去,效果会稳不少。另外可以试试给每轮检索加个时间权重,近几轮的关键词优先,老对话内容只做软提示,这样不至于被旧信息带跑偏。你那边是用的什么向量库和embedding模型?有时候换个更懂上下文的模型,可能比调策略还管用。
试试把上一轮的核心实体抽出来拼进query,像“财报”这种关键词带上,比整段历史好用得多。
这问题太典型了,我一开始搞RAG Agent也撞过这堵墙。把历史对话直接拼进query确实是个坑,尤其当上一轮信息量大的时候,新问题一出来,检索权重全被旧词带跑了。后来我试了个比较轻的办法,就是别拼全文,而是先让LLM把历史对话压缩成一个“当前隐含查询意图”的短句,比如“今年财报中的利润数据”,再拿这个去检索。这样既保住了上下文,又不会让噪音词干扰向量匹配。
不过还有个细节,就是压缩出来的query有时会太抽象,比如用户问“那利润呢”,你压缩成“利润情况”,结果检索回来一堆无关的利润分析。我后来在压缩指令里强加了“必须包含年份和主体公司名”这种硬约束,效果会稳很多。但说实话,如果用户连续追问第三轮、第四轮,这个压缩策略还是会慢慢失效,因为信息损耗是累积的。
我还有个疑问想请教下,你用的向量库有没有做时间戳过滤?如果像财报这种强时效性文档,光靠语义检索很容易把去年的利润也拉出来,跟今年的混在一起。我现在是给每段文档打了年份标签,在压缩query的同时强制加一个过滤器,至少能保证“今年”这个约束不会丢。不知道你有没有试过这种双通道的做法?
我最近也踩过这个坑,后来改成先用小模型把“那利润呢”改写成独立问题再检索,效果稳不少。历史对话别全塞query,挑上一轮的关键实体和意图就够了,塞多了纯属噪音。另外可以给检索加个时间或文档类型的过滤,避免“今年”这种词把去年的财报也捞进来。
我之前也踩过这坑,后来发现关键是别把整段历史都塞进query,那样噪声太大。我的做法是先让模型把“那利润呢”改写成“今年财报的利润是多少”这种独立问题,再拿去检索,召回质量会好很多。另外可以在检索前加一层意图判断,看这轮到底要不要重新查文档,有时候上一轮的内容已经够回答了。
我之前也踩过这个坑,把历史对话直接拼进query确实容易把检索带偏,尤其对话一长噪声特别大。后来改成先用一轮轻量的LLM调用把“那利润呢”改写成“今年财报的利润是多少”,再去检索,效果稳定不少。关键是改写那步要限制它只补全指代、别自己加戏,不然又会引入幻觉。你也可以试试只把上一轮的query和回答带进来,别塞太多历史,多轮里够用了。