首页 / 文章 / 实用提示:RAG:悄悄降低检索质量的错误

实用提示:RAG:悄悄降低检索质量的错误

《实用笔记》操作指南:RAG——那些悄然损害检索质量的错误:适用于采用该模式的团队的契约、检查项及即插即用代码模块。

3320 词

可将此内容视为针对操作人员的《RAG:悄然损害检索质量的错误》一文的优化版本:清晰的阶段划分、有序的代码模块以及便于交接时参考的恢复说明。在“概览”阶段,若能将其视为可量化的基准,效果会更好——在扩大范围之前,先记录一份最佳示例、一个故障案例以及对应的回滚说明。除了功能结果外,还需记录处理时间以及令牌或查询成本。提前了解这些成本信息,就能避免在从演示环境过渡到共享环境时出现意外费用。

为何RAG的问题出在检索而非生成环节

在“为何”检索而非生成阶段,修改代码之前应明确输入内容、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置应置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 需注明实际作为答案依据的段落。若没有引用,操作人员就无法区分是幻觉内容还是索引缺失所致。

核心概念简述

在修改代码之前,先简要梳理核心概念,明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的内容。 要引用那些真正作为答案依据的段落。没有引用的话,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

错误1至4:分块处理错误

在“错误1至4”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。相比冗长的脚本,更应采用小型且可测试的单元。当某一步骤失败时,故障原因应能指向单一责任点,而非复杂的流程链。必须引用实际作为答案依据的段落;没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失所致。

错误1:未经测试就直接采用简单的固定长度分割方式

对于错误1中直接使用默认阶段的情况,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与已验证输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

from langchain_text_splitters import RecursiveCharacterTextSplitter

# Recursive splitting respects structure first, falls back to
# character boundaries only when a section is still too large.
splitter = RecursiveCharacterTextSplitter(
    chunk_size=512,       # tokens, not characters, if you swap in a token-aware splitter
    chunk_overlap=75,     # ~15% overlap, a reasonable starting point
    separators=["\n## ", "\n### ", "\n\n", "\n", ". ", " "],
)
chunks = splitter.split_text(document_text)

错误2:不考虑文档类型就选择分块大小

对于“错误2:选择阶段不当”的问题,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解这些成本信息,可避免在从演示环境切换到共享环境时出现意外费用。必须注明实际作为答案依据的段落;如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

错误3:忽视或处理不当块重叠问题

对于错误3中忽视或忽略阶段的情况,应在修改代码之前明确输入参数、该步骤的负责人以及结束标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审核。 需引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

错误4:忽视文档自身的结构

对于第4类错误——忽视流程阶段,在修改代码之前必须明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程与异常恢复路径。重试机制、人工审核环节以及错误处理措施都是产品本身的组成部分,而非后续需要补充的内容。 必须引用那些真正作为答案依据的段落。如果没有引用,操作人员就无法区分是虚假信息还是索引缺失导致的问题。

第5至7类错误:检索架构缺陷

在错误5到7的阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。相比冗长的脚本,更应采用小型且可测试的单元。当某一步骤失败时,故障原因应能指向单一责任模块,而非复杂的流程链。必须引用实际作为答案依据的段落;没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失所致。

错误5:仅部署向量搜索功能,从未添加BM25算法

在仅使用向量数据的错误5处理阶段,修改代码之前需先明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并杜绝无声的半完成状态。 必须引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever

bm25_retriever = BM25Retriever.from_texts(chunk_texts)
bm25_retriever.k = 20

vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 20})

# weights are a starting point for RRF-style blending; tune against your eval set
hybrid_retriever = EnsembleRetriever(
    retrievers=[bm25_retriever, vector_retriever],
    weights=[0.4, 0.6],
)
results = hybrid_retriever.invoke(user_query)

错误6:使用简单的加权平均法而非RRF来融合结果

在处理错误6的融合结果阶段时,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境切换到共享环境时出现意外费用。需注明实际作为答案依据的段落;没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

错误7:为当前阶段检索的候选项过多或过少

对于“获取阶段过多”的错误7,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,这样操作人员无需查看整个系统结构即可进行审核。 需引用实际作为答案依据的段落。如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。

错误8和9:重排序与嵌入模型错误

在处理错误8和错误9的阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理方式都是产品本身的组成部分,而非后续需要补充的内容。 当下一步操作为代码编写或工具调用时,应优先使用具有结构化格式且经过模式验证的输出,而非自由形式的文字描述。

错误8:完全跳过重新排序

针对错误8中跳过重排阶段的问题,在修改代码之前应明确输入参数、该步骤的负责人以及终止标准。操作人员应当能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应优先选择小型、可测试的单元,而非结构复杂的脚本。当某个步骤出现故障时,故障原因应能指向单一责任主体,而非错综复杂的流程。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。 针对错误8中跳过重排阶段的问题,在修改代码之前应明确输入参数、该步骤的负责人以及终止标准。操作人员应当能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 除了功能结果外,还应记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在流程从演示模式转为共享模式时出现意外费用。

环境。

from FlagEmbedding import FlagReranker

reranker = FlagReranker("BAAI/bge-reranker-v2-m3", use_fp16=True)

def rerank(query: str, candidates: list[str], top_k: int = 5) -> list[str]:
    pairs = [[query, c] for c in candidates]
    scores = reranker.compute_score(pairs, normalize=True)
    ranked = sorted(zip(candidates, scores), key=lambda x: x[1], reverse=True)
    return [text for text, _ in ranked[:top_k]]

final_context = rerank(user_query, fused_candidates, top_k=5)

错误9:将通用嵌入模型用于专业领域

在处理基于阶段的错误9时,首先列出规范:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将配置信息置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放,以便操作人员无需查看整个系统结构即可进行审计。 缓存稳定的系统指令和工具架构。重复发送相同的开头信息是造成资源浪费的常见原因。

错误10:跳过评估,凭感觉发布代码

在处理“错误10:跳过评估阶段”时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及死信处理都是产品本身的组成部分,而非后续的优化工作。 在调整提示词之前,先使用固定的问题集来衡量召回率。仅仅更换提示词很难解决检索效果不佳的问题。

from deepeval import assert_test
from deepeval.metrics import FaithfulnessMetric, ContextualPrecisionMetric
from deepeval.test_case import LLMTestCase

def test_refund_policy_query():
    test_case = LLMTestCase(
        input="What is the refund window for the Pro plan?",
        actual_output=rag_pipeline.run("What is the refund window for the Pro plan?"),
        retrieval_context=rag_pipeline.last_retrieved_chunks,
        expected_output="Pro plan purchases can be refunded within 14 days of purchase.",
    )
    faithfulness = FaithfulnessMetric(threshold=0.8)
    precision = ContextualPrecisionMetric(threshold=0.7)
    assert_test(test_case, [faithfulness, precision])

实际应用场景

在处理实际应用阶段时,首先需明确相关约定:所需输入、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 相较于庞大的脚本,应优先选择小型且可测试的单元。当某个步骤出现故障时,故障点应指向单一责任模块,而非复杂的流程链。 在调整提示词之前,先使用固定的问题集测试系统的召回率。仅仅更换提示词往往无法解决检索效果不佳的问题。 在处理实际应用阶段时,首先需明确相关约定:所需输入、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 在功能结果之外,还需记录执行时间以及token或查询成本。提前了解成本情况,可避免在系统从演示环境过渡到共享环境时出现意外费用。

对比:检索架构的选择

将“对比检索架构选择”这一阶段视为可度量的评估维度最为有效。在扩大范围之前,先记录一份最佳案例、一个失败案例以及回滚说明。 将配置置于应用程序代码之外。环境文件、密钥存储和功能开关应集中存放,以便操作人员无需查看整个架构就能进行审计。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

你应该选择哪个选项?

“应选择哪种方案”这一问题在被视为可度量的指标时最易解决。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。同时将正常流程与恢复流程都记录下来。重试机制、人工审核环节以及死信处理都是产品本身的一部分,而非后续需要补充的内容。应将分块策略与检索策略分开,当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

构建良好的RAG流程的优缺点

将阶段工作的优势与局限视为可度量的指标来分析最为有效。在扩大范围之前,先记录一份最佳范例、一个失败案例以及回滚说明。 相较于庞大的脚本,应优先选择小型且可测试的单元。当某个步骤出现故障时,故障点应指向单一责任主体,而非复杂的流程链。 应将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。 将阶段工作的优势与局限视为可度量的指标来分析最为有效。在扩大范围之前,先记录一份最佳范例、一个失败案例以及回滚说明。 除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

生产就绪检查清单

在准备投入生产的检查清单阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 需注明支撑答案的具体段落。如果没有引用,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

结论

在结论阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 需同时记录正常流程和异常恢复流程。重试机制、人工审核环节以及错误处理都是产品本身的组成部分,而非后续需要补充的内容。 必须引用那些为答案提供依据的原文段落。如果没有引用,操作人员就无法区分是虚假信息还是索引缺失导致的错误。

有用资源

在“实用资源”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 优先选择小型、可测试的单元,而非冗长的脚本。当某个步骤失败时,故障应指向单一责任点,而非复杂的流程链。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。 在“实用资源”阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 除了功能结果外,还需记录执行时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。

操作检查清单

在处理操作检查清单阶段时,首先写下相关契约:所需的输入参数、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。

将此阶段视为输入与验证后输出之间的契约。为相关成果命名,明确成功标准,杜绝默许的部分完成情况。

在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词往往无法解决检索效果不佳的问题。

在更改提示词或模型之前,先锁定一个基准版本。同时改动系统和评估标准会掩盖功能退化的问题。

只要预算允许,就在持续集成过程中使用测试用例而非真实的付费 API 来添加能够检测关键路径的冒烟测试。

请同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及死信处理都是产品不可或缺的部分,而非后续需要补充的功能。

在推广该技术栈之前,应先冻结版本,为关键流程保存标准日志记录,并明确回滚步骤。共享环境需要设置速率限制、进行租户身份验证,同时指定专人负责密钥轮换工作。与其展示花哨的一次性演示,不如追求扎实可靠的性能。

关于 f9fa4aa0641e 的批量处理说明:请勿将提供商密钥放入代码仓库,应为每个会话设置令牌使用上限,并将日志记录与评估用配置文件存放在同一位置,以便后续更换模型时仍能保持数据可比性。

在将加固措施视为可测量的表面时,第0阶段的效果最佳。在扩大范围之前,先记录一份理想的运行日志、一个故障案例以及回滚说明。 同时记录正常流程和恢复流程。重试机制、人工审核环节以及错误处理都属于产品本身的功能,而非后续需要补充的内容。

加固细节0/908:针对该措施需测量处理时间、错误类型以及令牌消耗情况,然后依据固定的问题清单而非个人经验来判断是否保留该变更。

对于加固措施的第1阶段,在修改代码之前需明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新执行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入参数与验证后输出结果之间的契约。为相关成果命名,定义成功检测标准,并拒绝默许的半完成状态。

强化措施细节1/908:记录该功能的执行时间、错误类型以及代币消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。

在处理强化措施的第二阶段时,首先需明确合约的必要输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改更加规范。 应将配置信息置于应用程序代码之外,环境文件、密钥存储以及功能开关应集中存放于一个位置,以便操作人员无需查看整个系统结构即可进行审计。

强化措施细节2/908:记录该功能的执行时间、错误类型以及代币消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。

强化措施第3阶段若被视为可测量的表面对象,效果最佳。在扩大范围之前,需记录一份理想的测试结果、一个故障案例以及回滚说明。 相比庞大的脚本,应优先选择小型且可测试的单元。当某一步骤失败时,故障应能指向单一责任主体,而非复杂的流程链。

强化措施细节3/908:需为该措施测量执行时间、错误类型以及令牌消耗情况,然后依据固定的问题清单而非主观经验来决定是否保留该变更。

在强化措施的第4阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新执行该步骤,而无需猜测隐藏状态。应在功能结果旁记录执行时间以及代币或查询成本。提前了解这些成本信息,可避免在从演示环境过渡到共享环境时出现意外费用。

强化措施细节4/908:需测量该步骤的耗时、错误类型以及代币消耗情况,然后依据固定的评估标准而非主观判断来决定是否保留该更改。

在处理强化措施的第5阶段时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。

同时记录正常流程和故障恢复流程。重试机制、人工审核环节以及错误处理方式都是产品本身的一部分,而非后续的优化工作。

强化措施细节5/908:需测量该步骤的耗时、错误类型以及令牌消耗情况,然后依据固定的评估标准而非个人经验来决定是否保留该变更。