RAG 检索结果太多时怎么做去重和上下文预算
来源:17golang原创
时间:2026-09-07 07:10:20 207浏览 收藏
RAG 检索结果太多时,先别急着把 top_k 调小。更稳的做法是把“召回更多候选”“去掉重复片段”“重新排序”和“装入上下文”拆开处理:先用较大的候选池保证覆盖,再按来源与文本指纹去重,最后在扣除系统指令、用户问题和回答空间后,用剩余预算选择证据。这样既能减少模型看到的重复内容,也不会因为硬截断丢掉关键事实。
- 去重解决“同一事实出现多次”,重排解决“相关片段顺序不对”,预算装箱解决“证据太长”。
- 候选片段应同时记录来源、分数、覆盖主题和估算成本,不能只按相似度排序。
- 预算要先给系统指令、用户问题和回答留位置,再把余量分配给证据片段。
候选方案怎么分工:去重、重排和预算不是一件事
把检索结果直接送给模型,常见问题不是“结果不够”,而是前十条里有四条来自同一段文档,剩下的片段又把同一个结论换了说法。扩大召回只能提高覆盖,不能自动消除冗余;重排器能判断相关性,却不一定知道两个片段其实在重复;上下文压缩则是在内容已经选定后减少字数,不能替代候选筛选。
实用的候选池可以按四层理解:
| 方案 | 主要解决 | 适合场景 | 代价 |
|---|---|---|---|
| 扩大召回 | 漏掉相关证据 | 问题表述多、召回不稳定 | 重复片段增多 |
| 去重或 MMR | 内容同质化 | 文档切块重叠、同源内容多 | 可能牺牲一点单条最高分 |
| 重排器 | 相关性顺序 | 关键词命中但语义不准 | 增加一次模型或计算成本 |
| 上下文压缩 | 片段过长 | 证据本身有大量背景话术 | 压缩过度会丢限定条件 |

先去重再排序,避免同源片段占满前排
去重不要只比较整段字符串。切块通常有重叠窗口,同一事实可能只差一两句;可以先做空白、大小写和标点归一化,再以来源标识加文本指纹识别完全重复,最后用相似度阈值处理近重复。近重复组里保留分数更高、来源更稳定、包含限定条件更完整的片段,其他片段的主题标签仍可用于计算覆盖度。
下面的示例展示“去重与预算装箱”这两个动作如何分开。rough_tokens 只是工程估算,正式系统应替换为目标模型对应的 tokenizer;不要把字符数当成跨语言通用的精确 token 数。
def pack_context(hits, budget):
seen = set()
picked = []
used = 0
# 先按相关性排序,实际项目可在这里接入重排分数
for hit in sorted(hits, key=lambda item: item["score"], reverse=True):
text = " ".join(hit["text"].split())
fingerprint = (hit["source_id"], text[:120])
# 同源同前缀视为重复候选;近重复需换成相似度指纹
if fingerprint in seen:
continue
seen.add(fingerprint)
# 这是预算估算,不等同于目标模型的真实 token 计数
rough_tokens = max(1, len(text) // 2)
if used + rough_tokens > budget:
continue
picked.append({"source_id": hit["source_id"], "text": text})
used += rough_tokens
return picked
这个片段故意没有把“最高分”当成唯一标准。生产实现还应给不同来源设质量权重,并给每个主题设置最低覆盖数;否则一个写得很长的热门文档,仍可能挤掉回答另一半问题所需的证据。
上下文预算怎么定:先留回答空间,再装高价值片段
预算公式可以先写成:证据预算 = 模型输入上限 - 系统指令 - 用户问题 - 历史对话 - 回答预留 - 安全余量。输入上限和输出上限要以实际部署模型的官方规格为准;不同模型、工具调用和消息格式会改变可用空间。OpenAI 的模型与 API 文档可作为核对入口,不能把别的模型的数字直接套过来。
装箱时建议采用“相关性分数 + 新增覆盖度 - token 成本”的简单收益值。每加入一个片段,就重新计算它带来的新实体、字段或结论;如果只是重复已经覆盖的事实,即使分数很高,也应该让位给能补齐答案的片段。超过预算时优先删掉重复度高、来源弱、没有新增限定条件的候选。

用四个指标判断是检索问题还是预算问题
不要只看最终回答是否“像是正确的”。在离线样本上同时记录:候选去重率、主题覆盖率、预算截断率和引用完整度。去重率很低但覆盖率也低,通常是召回或切块问题;覆盖率足够而截断率很高,优先调整片段长度、压缩策略或证据预算;片段都在上下文里但引用遗漏,说明排序、来源元数据或回答约束仍有问题。
- 去重率:被合并或丢弃的重复候选占比,过低说明候选池可能冗余。
- 覆盖率:参考答案所需事实被至少一个片段覆盖的比例。
- 截断率:因预算不足未进入最终上下文的高价值片段比例。
- 引用完整度:回答中的关键结论能否回指到保留的来源片段。
最容易踩的坑是固定写死 top_k=5。知识库更新、问题长度和模型上下文变化后,这个数字没有稳定含义。更可靠的配置是保留较小的最终证据集,同时把候选数、预算、去重阈值和截断原因写入日志,方便回放同一个问题。
常见问题
去重后只剩两条结果,是不是阈值太严格?
不一定。先检查原候选是否本来就来自同一文档或同一事实,再看主题覆盖率;如果覆盖率仍完整,少量结果反而更健康。
重排器能不能直接替代去重?
不能。重排器优化顺序,去重器减少重复;两者可以串联,也可以让重排分数参与近重复组内的代表选择。
上下文越大,回答一定越好吗?
不一定。过多相似片段会稀释重点并挤压回答空间。应以覆盖率、引用完整度和截断率一起调参,而不是只追求更大的输入。
参考入口:OpenAI API 文档用于核对模型与输入限制;Hugging Face Blog用于观察检索、重排和上下文压缩的工程讨论。本文的示例、阈值和组合策略均为原创说明,不对应某个厂商的默认实现。
-
284 收藏
-
387 收藏
-
328 收藏
-
426 收藏
-
147 收藏
-
108 收藏
-
441 收藏
-
299 收藏
-
426 收藏
-
335 收藏
-
473 收藏
-
科技周边 · 人工智能 | 1天前 | 人工智能 · LangChain · rag · RAG 文档分块 RecursiveCharacterTextSplitter chunk_size chunk_overlap192 收藏
-
237 收藏
-
501 收藏
-
科技周边 · 人工智能 | 1天前 | python · 人工智能 · transformers · 流式输出 · SSE Transformers TextIteratorStreamer 流式生成472 收藏
-
384 收藏
-
273 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习