首页 / 文章 / 实用笔记:我构建了一个能自我审计的RAG系统,方法如下(附)

实用笔记:我构建了一个能自我审计的RAG系统,方法如下(附)

《实用笔记》操作指南:我构建了一个能自我审计的RAG系统,方法如下(附有合同模板、检查清单以及供采用该模式的团队直接使用的代码片段)。

3096 词

本指南将逐步构建从原材料到可运行系统的完整流程,内容来自《我打造了一个能自我审计的RAG系统:实现方法与代码示例》。重点在于可操作的步骤、明确的检查点,以及无需猜测意图即可直接放入代码仓库的代码。在概览阶段,应在修改代码之前明确输入参数、该步骤的负责人以及完成标准。操作人员应能够从已知的检查点重新运行该步骤,而无需推测隐藏的状态。相比庞大的脚本,更应采用小型且可测试的单元。当某个步骤失败时,故障原因应能明确指向单一责任模块,而非复杂的流程链。

为何没人监控信息检索过程(以及这为何会给他们带来麻烦)

在处理“为何无人监控检索”这一阶段时,首先需写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,杜绝无声的半完成状态。 在调整提示词之前,先使用固定的问题集来衡量检索的召回率。仅仅更换提示词很难解决检索效果不佳的问题。

三种会悄然出问题的情况

在处理“Three Things That Go”阶段时,首先写下相关约定:所需的输入参数、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免从演示环境过渡到共享环境时出现意外的费用支出。 在调整提示词之前,先用固定的问题集测试召回率。仅仅更换提示词往往无法改善较差的检索效果。

1. 分块污染

在处理“1块数据污染”阶段时,首先需明确相关规范:所需输入、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 应将配置信息与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中于一个位置,以便操作人员无需查看整个系统结构即可进行审计。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词往往无法解决检索效果不佳的问题。 在处理“1块数据污染”阶段时,首先需明确相关规范:所需输入、成功标志以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 相比庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤出现故障时,故障点应能指向单一的责任模块,而非复杂的流程链。

2. 嵌入式模型漂移

将“2个嵌入漂移”阶段视为可测量的界面能发挥最佳效果。在扩大范围之前,先收集一份理想的转录文本、一个失败案例以及回滚说明。把这一阶段视为输入与经过验证的输出之间的契约,为相关成果命名,明确成功标准,杜绝默许的半完成状态。应将分块策略与检索策略分开,当质量指标发生变化时,修改其中一项不应强制要求重写另一项。

3. 上下文窗口浪费

将“3个上下文窗口浪费阶段”视为可测量的指标时,其效果最佳。在扩大范围之前,先记录一份优秀的转录文本、一个失败案例以及回滚说明。在功能结果旁同时记录时间戳以及令牌或查询成本。提前了解成本情况,可避免在系统从演示环境过渡到共享环境时出现意外费用。应将分块策略与检索策略分开,当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

我们正在构建什么

将“我们正在构建什么”阶段视为可度量的对象来管理效果最佳。在扩大范围之前,需记录一份理想案例、一个故障实例以及回滚说明。 配置应与应用程序代码分开。环境文件、密钥存储和功能开关应集中存放,以便操作人员无需查看整个系统结构即可进行审计。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。 将“我们正在构建什么”阶段视为可度量的对象来管理效果最佳。在扩大范围之前,需记录一份理想案例、一个故障实例以及回滚说明。 优先选择小型且可测试的单元,而非庞大的脚本。当某个步骤出现故障时,故障应指向单一责任模块,而非复杂的流程链。

▣ 检查项#1:分块相关性评分。

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

▣ 检查#2:嵌入向量漂移检测。

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

▣ 检查#3:上下文窗口效率。

在“检查3”上下文窗口阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作员无需查看整个流程即可进行审计。 需引用实际作为答案依据的段落。若没有引用,操作员就无法区分是幻觉内容还是索引缺失导致的错误。 在“检查3”上下文窗口阶段,修改代码之前需明确输入参数、该步骤的负责人以及终止标准。操作员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于庞大的脚本,应优先使用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任模块,而非复杂的处理流程。

构建审计机制:从黄金查询集开始

在“构建审计机制起始阶段”中,首先需明确合同条款:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 将此阶段视为输入与验证后输出之间的契约。为相关成果命名,定义成功判定标准,并杜绝无声的半完成状态。 在调整提示词之前,先使用固定的问题集来衡量召回率。仅仅更换提示词往往无法解决检索效果不佳的问题。

[
  {
    "query": "What is the refund policy for digital products?",
    "expected_chunk_ids": ["faq_doc_chunk_12", "faq_doc_chunk_13"],
    "notes": "Customer FAQ, policy updated 2024-Q1"
  },
  {
    "query": "How do I reset my API key?",
    "expected_chunk_ids": ["api_docs_chunk_07"],
    "notes": "API documentation, stable"
  }
]

构建时需要注意的几点:

在处理该阶段的几项任务时,首先写下相关契约:所需的输入参数、成功信号以及部分失败时的处理方式。这样的清单能确保后续的代码修改始终符合预期。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免从演示环境过渡到共享环境时出现意外的费用支出。 在调整提示词之前,先使用固定的问题集测试召回率。仅仅更换提示词往往无法改善较差的检索效果。

审计脚本

在处理“审计脚本”阶段时,首先需明确相关规范:所需的输入参数、成功标志以及部分失败时的处理方式。这份清单能确保后续的代码修改始终符合既定要求。 应将配置信息与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中于一个位置,这样操作人员无需查看整个系统结构即可进行审计。 在调整提示词之前,先使用固定的问题集来测试系统的召回率。仅仅更换提示词往往无法解决检索效果不佳的问题。 在处理“审计脚本”阶段时,首先需明确相关规范:所需的输入参数、成功标志以及部分失败时的处理方式。这份清单能确保后续的代码修改始终符合既定要求。 相比庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤出现故障时,故障点应能明确指向某个具体的功能模块,而非整个复杂的流程。

Github仓库结构

将 Github 仓库结构阶段视为可度量的工作面,效果最佳。在扩大范围之前,先记录一份完美的成果示例、一个失败案例以及回滚说明。 把这一阶段视为输入与经过验证的输出之间的契约。为相关成果命名,明确成功标准,绝不允许出现无声的半完成状态。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应强制要求重写另一项。

rag-retrieval-audit/
├── audit/
│   ├── __init__.py           # Package exports
│   ├── rag_audit.py          # Main audit logic — all three checks
│   └── config.py             # All thresholds and model settings
├── examples/
│   ├── golden_queries.json   # Sample golden set (10 queries)
│   └── run_audit.py          # End-to-end demo — runs without an existing collection
├── tests/
│   └── test_audit.py         # Unit tests for all scoring functions
├── requirements.txt          # sentence-transformers, chromadb, scipy, numpy
└── README.md                 # Setup, usage, how to read the report
# rag_audit.py — structure overview
# Full implementation: https://github.com/satyam671/rag-retrieval-audit

# ── CONFIGURATION (tune to your pipeline)
EMBEDDING_MODEL      = "all-MiniLM-L6-v2"  # must match your index
TOP_K                = 5
RELEVANCE_THRESHOLD  = 0.70
DRIFT_P_THRESHOLD    = 0.05
EFFICIENCY_THRESHOLD = 0.40

# ── CHECK 1: Are the right chunks coming back?
def score_relevance(query_embedding, chunk_embeddings, expected_ids, retrieved_ids):
    """Cosine similarity per chunk + expected chunk hit/miss against golden set."""
    ...
# ── CHECK 2: Has retrieval quality shifted over time?
def detect_drift(current_sims, baseline_path=None):
    """Two-sample KS test comparing current similarity distribution to baseline."""
    ...
# ── CHECK 3: How much of the context window is signal?
def score_efficiency(retrieved_docs, answer):
    """Token overlap between retrieved chunks and the LLM answer."""
    ...
# ── RUNNER
def run_audit(golden_set_path, collection_name, chroma_persist_dir,
              baseline_path=None, answers_path=None, output_path="rag_audit_report.json"):
    """Runs all three checks, writes a structured JSON report, saves the baseline."""
    ...

详细解析每项检查的实际功能

将每个阶段视为可度量的对象来处理,这样才能最有效地开展工作。在扩大范围之前,先记录一个成功的案例、一个失败案例以及回滚说明。 在功能结果旁同时记录处理时间以及令牌或查询成本。提前了解成本情况,可避免在从演示环境过渡到共享环境时出现意外费用。 应将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

▣ 分块相关性评分

将“分块相关性评分”阶段视为可测量的对象时,其效果最佳。在扩大范围之前,需记录一份理想样本、一个故障案例以及回滚说明。 配置应置于应用程序代码之外。环境文件、密钥存储和功能标志应集中存放,以便操作人员无需查看整个系统结构即可进行审计。 应将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。 将“分块相关性评分”阶段视为可测量的对象时,其效果最佳。在扩大范围之前,需记录一份理想样本、一个故障案例以及回滚说明。 相比庞大的脚本,宜采用小型且可测试的单元。当某个步骤出现故障时,故障应指向单一责任模块,而非复杂的处理流程。

# WITHOUT the relevance gate — standard approach most teams use
def build_context_naive(query, collection, top_k=5):
    results = collection.query(
        query_embeddings=[embed(query)],
        n_results=top_k,
        include=["documents"]
    )
    # Pass everything back, no quality check
    return "\n\n".join(results["documents"][0])

# WITH the relevance gate - what the audit tells you to build
def build_context_gated(query, collection, model, top_k=5, threshold=0.70):
    q_emb   = model.encode([query], normalize_embeddings=True)[0]
    results = collection.query(
        query_embeddings=[q_emb.tolist()],
        n_results=top_k,
        include=["documents", "embeddings", "ids"]
    )
    passed_chunks = []
    for i, chunk_emb in enumerate(results["embeddings"][0]):
        sim = cosine_sim(q_emb, np.array(chunk_emb))
        if sim >= threshold:
            passed_chunks.append(results["documents"][0][i])
    # Empty context is better than wrong context
    return "\n\n".join(passed_chunks)

▣ 嵌入模型漂移检测

在嵌入漂移检测阶段,修改代码之前需明确输入内容、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 应将此阶段视为输入与经过验证的输出之间的契约。为相关成果命名,定义成功判定标准,并拒绝默许的半完成状态。 需引用实际作为答案依据的段落。没有引用的话,操作人员就无法区分幻觉与索引缺失。

▣ 上下文窗口效率

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

阅读报告

在“阅读报告”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 配置信息应置于应用程序代码之外。环境文件、密钥存储以及功能标志应集中存放于一个位置,以便操作人员无需查看整个流程即可进行审计。 需注明实际作为答案依据的段落。如果没有引用,操作人员就无法区分是幻觉内容还是索引缺失导致的错误。 在“阅读报告”阶段,应在修改代码之前明确输入参数、该步骤的负责人以及终止标准。操作人员应能够从已知的检查点重新运行该步骤,而无需猜测隐藏状态。 相较于庞大的脚本,更应优先使用小型且可测试的单元。当某个步骤失败时,故障原因应能指向单一责任模块,而非复杂的流程链。

运行 run_audit.py 后的预期输出:

在处理“运行后的预期输出”这一环节时,首先需明确合同条款:所需的输入参数、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改始终符合要求。 将此环节视为输入与验证后输出之间的契约。为相关成果命名,定义成功判定标准,杜绝无声的半完成状态。 在调整提示词之前,先使用固定的问题集来衡量召回率。仅仅更换提示词往往无法改善较差的检索效果。

python -m examples.run_audit

检索审计速查表

在完成“检索审计检查表”阶段时,首先写下相关约定:所需输入、成功信号以及部分失败时的处理方式。这样的检查清单能确保后续的代码修改保持一致性。 在功能结果旁记录执行时间以及令牌或查询成本。提前了解成本情况,可避免从演示环境过渡到共享环境时出现意外费用。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词很难改善较差的检索效果。

你会做出的不同改变

在处理“唯一任务”时,首先应写下相关规范:所需输入、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持透明可溯。 配置信息应与应用程序代码分开存放。环境文件、密钥存储以及功能开关应集中管理,这样操作人员无需查看整个系统结构即可进行审计。 在调整提示词之前,先使用固定的问题集来测试召回率。仅仅更换提示词往往无法解决检索效果不佳的问题。 在处理“唯一任务”时,首先应写下相关规范:所需输入、成功标志以及部分失败时的处理方式。这样的清单能确保后续的代码修改保持透明可溯。 相比庞大的脚本,更应优先选择小型且易于测试的单元。当某个步骤出现故障时,故障原因应能明确指向某个具体的功能模块,而非整个复杂的流程。

未涵盖的内容

“What This Doesn’t Stage”阶段若被视为可度量的界面,效果最佳。在扩大范围之前,先记录一份理想的成果样本、一个失败案例以及回滚说明。 将此阶段视为输入与已验证输出之间的契约。为相关成果命名,明确成功标准,绝不允许默许部分完成的情况。 将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应强制要求重写另一项。

参考资料

将“参考资料”阶段视为可度量的对象来处理效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个故障案例以及回滚说明。

在功能结果旁记录处理时间以及令牌或查询成本。提前了解成本情况,可避免在系统从演示环境过渡到共享环境时出现意外费用。

应将分块策略与检索策略分开。当质量指标发生变化时,调整其中一个不应强制要求重新编写另一个。

操作检查清单

将“操作检查清单”阶段视为可度量的对象来处理效果最佳。在扩大范围之前,需记录一份理想的处理结果、一个故障案例以及回滚说明。

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

将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

在预算允许的情况下,使用测试数据而非真实的付费 API,在持续集成过程中添加用于检测关键路径的冒烟测试。

优先选择小型、可测试的单元,而非结构复杂的脚本。当某个步骤失败时,故障应能指向单一责任点,而非混乱的整个流程。

将分块策略与检索策略分开。当质量指标发生变化时,修改其中一项不应迫使重新编写另一项。

在推广该技术栈之前,先冻结版本,为关键路径记录标准输出结果,并确认回滚步骤。共享环境需要设置速率限制、租户验证机制,以及明确的密钥轮换负责人。与其追求炫酷的一次性演示,不如注重扎实的可靠性。

关于 ffe9673a9f17 的批量处理说明:不要将提供者密钥放入代码仓库,为每个会话设置令牌上限,并将转录内容存储在评估用示例文件旁边,以便后续更换模型时仍能保持可比性。