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

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

来源: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_idgroup_by_sourceselected:候选先进入来源组,组内截断后再汇总,最终仍受 top_k 限制。示例输出会包含 deploy-guideincident-2024-11runbook-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推荐
  • ljg-skills -
    ljg-skills
    ljg-skills 是李继刚开源的 AI 技能与提示词集合,面向大模型使用者整理了一批可复用的 prompt、角色设定和任务技能模板,适合用于学习提示词设计、搭建个人 AI 工作流和沉淀团队常用智能体能力。
    5421次使用
  • MELO音乐 - AI 音乐生成平台,支持多模态创作能力
    MELO音乐
    MELO音乐是一站式AI视频与音乐制作助手,对标suno, udio的高品质体验。提供伴奏生成、原创写词、无损导出、哼唱识曲、混音变声等全套音频与短视频编辑工具。无论是流行Kpop、电音说唱、民谣古风、摇滚儿歌还是商用轻音乐,MELO为你免费谱曲,轻松做同款!
    4911次使用
  • UniScribe - AI 免费在线音视频转文字平台
    UniScribe
    UniScribe 是一款 AI 音视频转文字与内容整理工具,支持上传音频、视频文件或粘贴 YouTube 链接,自动生成转写文本、摘要、思维导图和关键问题,并支持多格式导出,适合会议记录、课程学习、访谈整理和内容创作复盘。
    4834次使用
  • 剧云 - 免费 AI 智能中文剧本创作平台
    剧云
    剧云是专业中文剧本创作平台,安全稳定运行十余年,集成AI编剧、剧本医生审核、人物小传、剧情关系图、大纲编写、多人协作、Word导入导出、版权管控功能,数据安全防护,轻松高效创作剧本。
    5097次使用
  • 万象有声 - AI 一站式有声内容创作平台
    万象有声
    万象有声,一个专为有声创作者打造的新一代智能有声内容创作平台。平台提供专业的智能拆章、智能画本编辑、AI配音、AI生成音效、后期制作、智能对轨、智能审听等有声创作全流程工具,可以帮助创作者高效、低成本创作出引人入胜的有声作品。立即体验,让有声书制作更简单!
    5055次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议隐私政策
返回登录
  • 重置密码