当前位置:首页 > 文章列表 > 科技周边 > 人工智能 > 向量检索召回很多但答案变差:按来源分组保留上下文多样性

向量检索召回很多但答案变差:按来源分组保留上下文多样性

来源:17golang原创 2026-08-29 13:03:07 0浏览 收藏

知识库问答里有一种很隐蔽的退化:召回数量从 5 调到 20,检索指标看起来更宽了,回答却开始围着同一份长文打转。常见原因不是向量模型突然失效,而是相似片段集中来自同一个 source_id,有限的上下文窗口被重复证据占满。

先按来源分组,再在每组中保留最高分片段,最后用一个明确的 top_k 配额合并候选,通常比盲目增大召回数更容易保住上下文多样性。

要点速览

  • top_k 只是最终送入模型的数量,不等于有效证据的来源数量。
  • 用 source_id 分组能先识别“同文档霸榜”,再决定每组最多保留几段。
  • similarity_score 仍负责组内排序,分组不会把低相关片段硬塞进上下文。
  • 小实验应同时比较来源数、分数分布和最终回答引用,不能只看召回条数。

先复现同一来源占满 top_k 的问题

假设检索器返回 8 个候选片段,分数已经按降序排列。前 5 段来自一份部署手册,后 3 段来自两个故障复盘。若直接取前 5 段,模型看到的是同一来源的连续解释,另两份材料即使包含反例,也进不了上下文。

candidates = [
    {"chunk_id": "a-01", "source_id": "deploy-guide", "similarity_score": 0.92},
    {"chunk_id": "a-02", "source_id": "deploy-guide", "similarity_score": 0.91},
    {"chunk_id": "a-03", "source_id": "deploy-guide", "similarity_score": 0.90},
    {"chunk_id": "a-04", "source_id": "deploy-guide", "similarity_score": 0.89},
    {"chunk_id": "b-01", "source_id": "incident-2024-11", "similarity_score": 0.88},
    {"chunk_id": "c-01", "source_id": "runbook-cache", "similarity_score": 0.87},
]

top_k = 5
selected = candidates[:top_k]
print(len(selected), {item["source_id"] for item in selected})

这段代码会打印 5 和只有一个来源。它没有违反相似度排序规则,却把“证据覆盖面”交给了文档长度和切块方式。这里先别急着调大 top_k,因为上下文窗口、重复内容和模型注意力都会一起增加。

用 source_id 分组,再做组内保留

一个可控的后处理步骤是先执行 group_by_source。它不重新计算向量,只把候选片段按真实来源归档;每一组内部仍按 similarity_score 从高到低排列。

from collections import defaultdict

def group_by_source(candidates):
    groups = defaultdict(list)
    for item in candidates:
        groups[item["source_id"]].append(item)
    for items in groups.values():
        items.sort(key=lambda item: item["similarity_score"], reverse=True)
    return groups

groups = group_by_source(candidates)
selected = []
per_source = 2
for source_id, items in groups.items():
    selected.extend(items[:per_source])
selected.sort(key=lambda item: item["similarity_score"], reverse=True)
selected = selected[:top_k]
print([item["source_id"] for item in selected])

这里的关键节点是 source_id、group_by_source 和 selected:候选先进入来源组,组内截断后再汇总,最终仍受 top_k 限制。示例输出会包含 deploy-guide、incident-2024-11 和 runbook-cache,但并不保证三个来源各占相同数量。

向量候选片段经过 group_by_source 按 source_id 分组,再汇总到 selected 的数据流示意图

图中只画这段实验真正存在的关系:候选片段进入 group_by_source,按 source_id 分组,再汇入 selected。如果某来源只有一个高相关片段,它不会为了凑数被复制。

如何决定每个来源的配额

固定的 per_source = 2 适合做第一版实验,但线上通常需要把候选数量、来源数和上下文预算一起看。可以先设一个较宽的召回池,再用每组上限控制重复度:

  • 知识库来源很多时,每组保留 1 段,优先观察覆盖面;
  • 来源较少但每份文档内部有互补章节时,每组保留 2 段或 3 段;
  • 某些来源是权威规范时,可以给它额外配额,但要在配置中显式记录,不要让长文天然获得特权。

别把“来源多”直接当成“答案可靠”。如果一组的最高 similarity_score 都明显低于另一组,仍应先检查查询改写、切块粒度和元数据过滤。分组是控制证据结构的手段,不是相关性校验的替代品。

similarity_score 组内排序后按来源配额汇入 top_k 的上下文选择示意图

运行检查:别只打印最终片段

小实验至少输出三个结果:最终片段数、来源集合、每个来源的分数范围。这样才能分辨是配额生效,还是候选本身就只有一个有效来源。

source_stats = {}
for item in selected:
    source_stats.setdefault(item["source_id"], []).append(item["similarity_score"])

print("selected=", len(selected))
print("sources=", sorted(source_stats))
for source_id, scores in sorted(source_stats.items()):
    print(source_id, min(scores), max(scores))

检查时重点看两种异常:第一,selected 数量不足,说明召回池或每组上限过小;第二,来源集合仍只有一个,说明候选池里没有其他来源,继续调后处理没有意义。只有把来源集合扩展开,才值得比较最终回答是否减少了重复解释。

常见问题:来源多样性和相关性怎么取舍

按来源分组会不会把最高分片段丢掉?

会有这个可能,所以应保留组内最高分,并用离线问答集比较答案正确率,而不是追求来源数量最大化。

top_k 应该先调大还是先做分组?

建议先固定一个能放进上下文的 top_k,完成分组实验后再调大;否则重复片段和窗口压力会同时变化。

只有一个 source_id 时怎么办?

先查元数据过滤、切块和召回池是否只覆盖一份文档。后处理无法凭空制造第二个来源。

如何判断多样性真的改善了回答?

把来源集合、引用覆盖和答案评测放在同一次实验里记录;仅看去重后的条数,不能证明回答更准确。

把实验收敛成一条可观测规则

在 RAG 链路中,向量召回负责找到候选,来源分组负责控制证据结构,selected 再把有限候选交给生成模型。上线前给这三个阶段分别打日志,至少记录 top_k、来源数量、每组保留数和最终引用来源。这样答案变差时,能判断问题发生在召回不足、来源过度集中,还是生成阶段没有使用已提供的证据。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Python 调用大模型时如何用结构化输出校验 JSON:从解析失败到可重试Python 调用大模型时如何用结构化输出校验 JSON:从解析失败到可重试
上一篇
Python 调用大模型时如何用结构化输出校验 JSON:从解析失败到可重试
Go slog.Group 如何组织结构化日志字段:嵌套键与输出核对
下一篇
Go slog.Group 如何组织结构化日志字段:嵌套键与输出核对
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    463次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    526次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    545次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    493次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    312次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码