实用笔记:我构建了一个能自我审计的RAG系统,方法如下(附)
《实用笔记》操作指南:我构建了一个能自我审计的RAG系统,方法如下(附有合同模板、检查清单以及供采用该模式的团队直接使用的代码片段)。
本指南将逐步构建从原材料到可运行系统的完整流程,内容来自《我打造了一个能自我审计的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 的批量处理说明:不要将提供者密钥放入代码仓库,为每个会话设置令牌上限,并将转录内容存储在评估用示例文件旁边,以便后续更换模型时仍能保持可比性。