基础篇
进阶篇
高频篇
精选篇
手写篇
面经篇
AI 篇
原理篇
每日一题
小程序题库
知识卡片
  • 场景篇按分类整理的大前端场景考点
  • 历年面经按年份追踪真实考点
  • 算法题库NEW在线编码即时判题
  • 专项自测100 题快速查漏
  • 前端基础
    • HTTP从报文一路讲到 HTTPS
    • 浏览器渲染、事件循环、进程
    • 计算机基础Linux、网络、操作系统
  • 进阶专项
    • 设计模式23 种模式怎么用
    • 前端系统进阶学习大型项目工程化
    • 前端综合文章长期沉淀的实践文
  • 工程与工具
    • Node学习指南从环境搭建到服务端
    • NPM工作流script、依赖与发布
    • Docker容器化部署上手
    • Canvas图形与动画实战
  • 路线与导图
    • 思维导图知识点全景图
    • 学习路线按图索骥不跑偏
  • 动态
    • 公众号动态公众号历史文章
    • 博客动态站长的技术博客
    • 开发者导航常用工具与文档站
  • AI 助手随时提问,即时解析
  • AI 模拟面试模拟真实面试 + 报告
  • AI 知识地图串起全站知识点
  • AI 定制路线按你的简历现排
AI 热点
旧版
AIHT
内容
精选全部 AI 动态热点榜AI 日报收藏
分类
模型行业编程产品技术观点开源
更多
Agent 接入
返回首页开发者导航
AIHT
精选 / 资讯详情
8.0
热点
AI SCORE
技术实践2026-11-02 10:19

RAG 准确率暗降,先查索引是否过期

dev.to · AI#RAG#向量索引#数据一致性
Editor brief · 编辑速览

RAG 即使没有模型或提示词变更,也可能因源文档与向量索引不同步而逐渐失准。文章指出文档修改未触发重新嵌入、任务静默失败,以及删除文档后残留分块等常见原因。

文章思维导图
Knowledge map
拖拽缩放
AI deep read

AI 深度解读

RAG 上线时答得准,几个月后却开始引用旧政策、介绍已下架产品,未必是模型变差了。作者指出,一个容易被忽略的原因是:源文档已经变化,向量索引却没有同步,系统还在把过期材料组织成流畅的答案。

这篇文章的价值,在于把「回答质量下降」拆成可以检查的工程问题:文档与分块是否一致、向量是否来自同一模型、正确来源还能否被检索到,以及系统有没有承认资料不足的能力。不过,作者给的是生产经验和示例代码,没有提供效果对照数据。

没人发版,为什么答案也会变差

检索增强生成(RAG)维护着两份知识:源文档,以及从文档切分、计算嵌入后得到的分块。它们通过不同代码路径、在不同时间更新,只要同步出现缺口,检索就可能拿到旧信息。

作者列出了五种变化。

第一,文档更新,分块没有更新。政策正文改了,但嵌入任务可能还在排队,可能三周前就静默失败,也可能接入流程只支持首次导入,压根没处理编辑。

第二,文档删除,分块仍然存在。产品已经下架,说明文档也删了,但索引里留下的分块仍会被召回。生成模型看到它们,自然还会继续回答相关问题。

第三,新增资料改变排序。加入两百篇文档后,旧文档虽然一字未改,也可能被语义更接近的新内容挤出前五名。旧问题因此失去了原来的依据。

第四,嵌入模型发生变化。作者设想,供应商在相同 API 名称背后更新模型,新查询与旧分块使用了不同版本。如果两者的向量空间不兼容,相似度仍然可能返回数值,但已经失去比较意义。原文没有给出具体供应商或实际事故。

第五,用户开始问资料库没有覆盖的问题。检索总能找出某个「最近的结果」,但最近不代表相关,更不代表足以回答。

没有代码变更,不等于检索系统的输入和行为没有变化。

这些变化通常不会触发异常。请求成功、模型正常返回、文字也很顺,错误藏在答案所依赖的材料里。

先检查文档和索引是否一致

作者提出的低成本做法,是在导入时对源文档计算 SHA-256,将结果作为 source_hash 写入每个分块,同时记录 embedding_model 和 indexed_at。

示例中的 content_hash(text: str) 使用 hashlib.sha256(text.encode()).hexdigest()。ingest_document(doc: Document, tenant_id: UUID) 先读取已有分块的哈希;内容相同就跳过,否则通过 chunk_text 重新切分,再调用 embed_batch 计算向量。

旧分块的删除和新分块的插入放在同一个 db.transaction() 中,避免替换过程中出现旧数据已经删除、新数据尚未写入的空档。新记录同时保存 document_id、tenant_id、content、embedding、source_hash、embedding_model、indexed_at,模型标识取自 EMBEDDING_MODEL。

这样,索引是否过期就能转化为数据库查询:

  • 将 source_hash 与源内容重新计算的 encode(sha256(d.content::bytea), 'hex') 比较,可以发现正文已改、分块未更新的记录。
  • 用 LEFT JOIN 查找 documents 中已不存在的文档,可以发现孤儿分块。
  • 按 embedding_model 分组并执行 count(*),可以检查索引里是否混入多个模型标识。

作者建议定时执行并告警,而不是等用户指出答案过期。

这里也有一个代码层面的缺口:示例只凭内容哈希决定是否跳过。如果正文没变,但嵌入模型需要升级,这个判断会阻止重新计算。真正做模型迁移时,还要把模型版本纳入更新条件。

把检索失败和生成失败分开看

只检查最终答案,很难定位问题:究竟是模型理解错了正确资料,还是模型拿到的资料从一开始就错了?

作者建议单独维护检索用例,标注「这个问题必须检索到哪篇文档」,并每晚执行。下面这段代码检查目标文档是否出现在前五个结果中:

RETRIEVAL_CASES = [
    {
        "query": "what is the refund window for annual plans",
        "must_retrieve": "billing-policy-v4",
    },
    {
        "query": "how do I rotate an API key",
        "must_retrieve": "api-authentication",
    },
]

async def check_retrieval(tenant_id: UUID) -> list[dict]:
    failures = []
    for case in RETRIEVAL_CASES:
        results = await search(case["query"], tenant_id=tenant_id, limit=5)
        retrieved_docs = {r["document_slug"] for r in results}

        if case["must_retrieve"] not in retrieved_docs:
            failures.append({
                "query": case["query"],
                "expected": case["must_retrieve"],
                "got": list(retrieved_docs),
            })
    return failures

要看的是 must_retrieve 和 limit=5:这里验证的是指定来源能否进入候选集合,并没有验证最终答案正确,也没有证明返回的具体分块包含完整依据。

它仍然很有用。新增文档改变排序、嵌入模型变化,或者分块方式调整,都可能让这组测试失败,从而先把排查范围缩到检索链路。

作者还要求把「没有足够相关的材料」作为正常状态处理。下面的示例先过滤弱结果,全部不合格时记录缺失并停止生成:

MIN_SIMILARITY = 0.35

async def answer(query: str, tenant_id: UUID):
    results = await search(query, tenant_id=tenant_id, limit=6)
    relevant = [r for r in results if r["similarity"] >= MIN_SIMILARITY]

    if not relevant:
        await log_retrieval_miss(query, tenant_id)
        return "I don't have information about that."

    return await generate(query, context=relevant)

这里要留意 MIN_SIMILARITY = 0.35。原文没有说明这个值的测定过程,也没有给出所用相似度度量,不能直接把它当作通用阈值。

log_retrieval_miss 留下的问题,则可以帮助团队判断:用户正在问什么,资料库又缺少什么。作者认为,缺失率持续上升可能比用户投诉更早暴露问题。

我的判断

我赞同作者把排查重点放在知识同步和检索质量上。对于依赖政策、产品文档或操作手册的问答系统,这比一遇到错误就换模型、改提示词,更有针对性。提示词无法替索引删除已经失效的资料。

但文章中的「多数漂移可以被这些检查抓住」,属于经验判断。原文没有提供样本规模、故障占比或实施前后的准确率,不能据此推算收益。

模型版本检查尤其需要谨慎。embedding_model 分组只有一行,只能证明存储的标识一致;如果供应商真的在同名 API 后更换了不兼容模型,字符串可能根本不变。多个标识也需要结合向量兼容性和索引隔离方式判断,不能只数行数。

同样,哈希一致只能证明内容一致,不能证明内容仍然正确;目标文档进入前五,也不能证明模型拿到了足够证据。缺失率上升还可能来自阈值变化或检索故障,不能全部归因于资料不足。

这些检查能缩短定位路径,但不能替代最终答案评估。

相比只做端到端问答测试,分层检查增加了一些维护工作,却能让失败原因更清楚。两者应当并行保留。

能拿来做什么

  • 给现有 RAG 导入流程补上 source_hash、模型版本和索引时间,并定时检查内容差异与孤儿分块。先让同步问题可见,再决定是否需要重建索引。
  • 为团队知识库整理一组「问题与预期来源」用例,沿用 limit=5 的检查思路每晚执行。政策和文档发生替换时,也要更新预期来源,避免测试固守旧资料。
  • 在 Node 问答服务中明确区分正常召回、弱召回和无结果,并用实际问题校准阈值。前端应展示服务返回的资料不足状态,让用户知道系统缺少回答依据。
  • 定期查看 log_retrieval_miss 对应的问题,同时检查检索链路。确认确实缺资料后,再安排补充文档,避免把基础设施故障误当成内容需求。
Original source

本文由 AI 通读原文后重新编写,观点部分为站内判断,细节以原文为准,来源 dev.to · AI,原文版权归原作者所有。

阅读原文
上一篇
给 n8n 智能体加上防重、限额与急停
下一篇
推理降延迟:投机解码与蒸馏怎么选

资讯速览

dev.to · AI · 2026-11-02 10:19

AI 评分 8.0 / 10

相关标签

#RAG#向量索引#数据一致性

继续阅读

  • AI 降低重写成本,测试资产仍是关键
  • 给 n8n 智能体加上防重、限额与急停
  • 推理降延迟:投机解码与蒸馏怎么选
  • 低比特权重为何更利于生成加速
  • 给智能体传令牌,如何避开聊天日志