RAG 检索只要 Top-K 拿不到全部答案就得翻车
很多 RAG 管道在处理“列举类”问题时其实在悄悄失效,因为绝大多数人的逻辑是寻找那个最相关的片段,但列举类问题的答案往往散落在所有相关的段落里。如果你问“这份报告里提到了哪些风险点”,模型如果只盯着检索分最高的一两个片段,结果必然是遗漏。
下一篇
免费用户也能无限次发文字消息了,OpenAI 这波操作有点意思 →
我之前在跑一个企业文档分析项目时就踩了这个坑。原本以为调高 Top-K 就能解决,结果发现 LLM 在面对大量上下文时,很容易产生“中间丢失”现象,或者直接偷懒只总结前两个点。这种问题不能靠调参,得在 pipeline 形状上做文章,也就是所谓的 Loop Engineering。
具体的实操思路是把单次检索改为循环迭代。简单说就是:检索 -> 提取答案 -> 将已提取内容作为负反馈/过滤条件再次检索 -> 直到没有新内容出现。
一个基础的伪代码逻辑大概是这样:
def loop_retrieval(query, documents):
found_answers = []
current_query = query
while True:
# 这里的检索需要能够排除掉已经找到的信息
passage = vector_db.search(current_query, filter_out=found_answers)
if not passage or is_redundant(passage, found_answers):
break
# 提取该片段中的具体列举项
new_info = llm.extract(passage, query)
found_answers.append(new_info)
# 更新 query,告诉模型我已经知道这些了,请找剩下的
current_query = f"Based on {found_answers}, what other points regarding {query} are mentioned?"
return found_answers在这种工作流里,关键点在于 current_query 的动态更新。你得强迫模型意识到它已经拿到了部分答案,现在需要寻找的是“增量”信息。
另外一个细节是,如果文档量极大,这种循环会显著增加 Token 消耗和响应延迟。建议在循环中加入一个最大迭代次数(比如 3-5 次)或者设置一个相似度阈值,一旦检索回来的片段与已获得答案的余弦相似度过高,就直接跳出循环,否则很容易陷入死循环或者在重复信息里打转。