最近在搭一个基于Agent的RAG问答系统,用的LangGraph+向量库。遇到个很拧巴的问题:Agent拆解复杂问题成多个子查询时,每个子查询是独立检索好,还是把历史对话(比如用户之前的追问)也拼进去再检索?我试了两种,独立检索的结果碎片化严重,但全带上上下文又容易让向量检索跑偏,召回一些不相关的东西。目前我是在子查询前面加了个“根据历史对话,用户当前想问的是……”的前缀,效果时好时坏。有没有佬遇到过类似问题?你们是怎么处理这个上下文窗口和检索平衡的?还是说根本上就不该让Agent自己规划查询,直接用固定策略拆?求指点,调参调得有点麻了。
RAG里Agent规划出来的子查询,到底该不该带上历史对话上下文?
全部回复
共 84 条我觉得问题可能不在要不要带上下文,而在于怎么带。我之前试过把历史对话压缩成一句话的“意图摘要”再拼到子查询里,比直接甩原始对话效果好很多,检索漂移少一半。另外你那个固定前缀太模板化了,模型容易偷懒,不如让它显式输出“用户想解决的具体实体+关系”。至于Agent规划还是固定策略,我个人觉得动态拆解没问题,但子查询生成后加个自校验步骤,跟原始问题算个相似度,太低就重写,能救不少碎片化问题。调参麻是常态,别全指望向量库,重排模型可能才是解药。
这问题我太有同感了,之前搞LangGraph的时候也被这个上下文窗口折磨过。说实话,全量塞历史对话基本等于告诉向量库“我啥都想要”,召回自然就飘了,但完全割裂又会让子查询失去指代消解的能力。我后来试了个折中办法,就是只提取历史里跟当前子查询实体强相关的部分,比如用户之前提过的具体名词或时间点,拼成一个简短的“背景摘要”而不是整段对话,效果比你那加固定前缀稳定不少。至于Agent规划还是固定策略,我个人觉得得看问题复杂度,简单场景固定拆反而更好调,复杂场景才值得上Agent,但这时候更关键的其实是给Agent一个明确的“查询目标模板”,让它输出结构化的检索条件而不是自然语言句子,这样能减少不少歧义。你那个前缀时好时坏,我怀疑是模型对“历史对话”这个词的泛化理解不稳定,要不要试试把历史压缩成关键词列表?另外你向量库那边有没有做query改写或者混合检索?有时候光靠embedding不够,加一层BM25能兜住那些被上下文带偏的边界情况。
试过把历史摘要压缩成一句意图再拼子查询,比直接拼原文稳很多,你可以试试。
我之前也卡在这块好久,最后是给子查询加了个轻量的意图判断,只有当用户历史里明确有指代或转折时才拼上下文,不然就独立检索。全量塞进去确实容易跑偏,尤其遇到多轮闲聊,向量库里全是噪音。你这前缀方案其实思路对,但可能模板太死,不如动态决定哪些历史片段值得带。固定策略拆我也试过,复杂问题直接崩,Agent还是得留。