外观
检索服务降级与质量回退故障复盘
30 秒复盘结论| 这是一场故障演练:候选索引在完整性和 ACL 契约未通过时被切为可读,部分请求出现空召回,另一些请求在静默降级后返回错误证据。止损不是调大 Top-k,而是固定
request_id和版本、回切最近健康索引、权限失败时保持 fail-closed,并把降级结果显式标记。长期修复采用候选索引隔离、版本 Manifest、原子别名切换、分阶段 Trace 和固定问题集门禁;当前文档只有设计、合成 Trace 和验证方案,不代表本仓库发生过真实生产事故。
目录
- 1. 复盘摘要
- 2. 小白先看懂
- 3. 故障教学图片
- 4. 事实边界与工程证据
- 5. 影响范围、时间线与指标口径
- 6. 技术栈与故障域架构
- 7. 故障与恢复调用流程
- 8. 现象、检测与临时止损
- 9. 根因分析
- 10. 长期修复与横向选型
- 11. 修复验证与防复发
- 12. 技术难点、方案亮点与经验
- 13. 面试表达
- 14. 关联证据与参考资料
- 15. 总结
1. 复盘摘要
| 项目 | 内容 |
|---|---|
| 复盘类型 | 故障演练,不是真实生产事故 |
| 演练目标 | 验证索引发布不完整、版本契约不一致和检索通道超时时,系统能否正确止损、恢复并保留证据 |
| 一句话现象 | 同一类问题在部分节点空召回,另一些节点返回不支持答案的旧证据 |
| 直接原因 | 查询命中新索引别名,但 ACL/过滤契约仍按旧字段解释;Dense 通道异常后又静默退化为未通过质量门禁的单路结果 |
| 根本原因 | 索引发布缺少隔离构建、完整性校验、版本握手和原子切换,降级只关注接口可用性,没有检查证据质量 |
| 临时止损 | 冻结候选索引、别名回切健康版本、清理带版本的缓存、权限失败时拒答、降级结果显式标记 |
| 长期修复 | Index Manifest、候选索引隔离、兼容矩阵、影子回放、原子别名、分阶段 Trace、质量感知降级 |
| 当前状态 | 文档和生成图片已形成;故障注入尚未执行,图片等待人工复核,保持 draft |
1.1 为什么值得独立复盘
这个问题横跨数据发布、权限过滤、Sparse/Dense 检索、Reranker、上下文和生成。只把它写成“向量数据库空召回”会漏掉三个关键事实:
- 接口成功不代表有正确证据;
- 权限过滤不能为了恢复召回而放宽;
- 旧索引、新过滤和新缓存可能共同造成不可重放的混合结果。
2. 小白先看懂
一家大型仓库准备更换货架编号。夜间人员只换完一半货架,就提前把新版地图发给了白班;门卫仍按旧通行证规则放行,查询终端偶尔又切回旧地图。顾客问同一件商品,有时收到“没有库存”,有时被带到放着旧型号的货架。
2.1 生活场景与技术映射
| 仓库场景 | 技术对象 |
|---|---|
| 顾客描述商品 | 原始 Query 与改写 Query |
| 门卫核验通行证 | 租户身份、ACL 和元数据过滤 |
| 旧地图与新地图 | 语料、Embedding、索引和 Filter Schema 版本 |
| 货架目录与相似商品区 | BM25 与 Dense 候选召回 |
| 导购重新排序 | Reranker 与上下文选择 |
| “没有库存”或带错货架 | 空召回或错误证据 |
| 收回新版地图 | 回切健康索引别名 |
| 清点所有货架再发图 | Manifest 完整性门禁和固定问题集回放 |
回到专业机制:RAG 必须沿 Query → ACL/过滤 → Sparse/Dense 候选 → 融合 → 重排 → final_context → 生成与引用 保存候选数量、淘汰原因和版本。排障目标不是“让最终一定有答案”,而是找到正确证据第一次消失、错配或被截断的阶段。
类比边界:真实检索还涉及 ANN 近似误差、分数分布、缓存、并发、超时和跨节点一致性;仓库故事不能替代 Trace、版本清单和回放实验。
3. 故障教学图片
图:故障教学图片|检索降级的证据链与恢复闭环
替代文本: 上半部分从 Query 与身份、ACL 与过滤进入 BM25 和向量检索的混合召回,再进入重排上下文与生成引用;索引版本混用和过滤契约不一致形成红色故障断点,导致空召回或错误证据。下半部分通过固定 request_id、回切稳定索引、固定问题集回放、灰度恢复和版本门禁完成绿色恢复闭环,左上角明确标为故障演练。
教学图片待人工审图检索服务降级与质量回退的故障证据和恢复闭环 暂不公开,正文与 Mermaid 图可正常阅读。
读图结论: 检索退化先定位正确证据首次消失或错配的阶段,再决定回滚、受控降级或根因修复,不能用接口成功率掩盖质量失败。
图中红色只表示索引版本和过滤契约的故障断点,绿色表示恢复动作。BM25 与向量检索仍受同一身份和版本契约约束;任一路恢复都不能绕过 ACL。
图片生成记录: model=gpt-image-2,generated=2026-07-15,prompt_version=v1;查看生成 Prompt。
模型证据: PNG 的 C2PA 元数据记录 softwareAgent=gpt-image、version=2.0 与 OpenAI Media Service API,用于核验本图由 gpt-image-2 生成。
质检状态: 已完成 Agent 视觉检查,图内标题、中文标签、箭头、事实边界和技术关系与 Prompt 一致;仍等待维护者人工复核,因此 image_status=generated-awaiting-review,文档保持 draft。
4. 事实边界与工程证据
4.1 证据分级
| 结论 | 类型 | 证据 | 当前边界 |
|---|---|---|---|
| RAG 应沿 Query、权限、候选、重排、上下文和生成逐层排查 | 已验证仓库事实 | RAG 生产工程、多模态监控和项目文档已有相同排障顺序 | 证明方法存在,不证明本演练已执行 |
| 半成品索引提前可读会造成新旧引用混合 | 仓库已有故障演练 | AI 知识库项目的半成品索引演练 | 不是本仓库真实线上事故 |
| ACL 失败时不能放宽权限恢复召回 | 工程安全约束 | 根规则、RAG 主题与 Milvus 生产问题文档 | 具体权限服务实现未知 |
| 本演练的根因是发布不原子与契约不一致 | 示例假设 | 本文设计的合成 Trace、Manifest 和故障注入步骤 | 尚无真实运行日志 |
| 修复会降低空召回率和错误证据率 | 待验证推断 | 计划使用固定问题集和故障注入验证 | 没有填写虚构改善比例 |
4.2 本文使用的工程证据
本文提供三类可复核证据:
- 合成 Trace:展示候选在哪一层首次归零,明确标为演练数据;
- 发布 Manifest Schema:固定语料、解析、切片、Embedding、ACL、索引和缓存版本;
- 架构、调用时序与故障注入矩阵:说明故障如何发生、怎样止损和如何证明修复。
缺失的真实证据包括:生产 request_id、线上指标、真实索引别名变更、发布工单、故障时间线、修复提交和人工业务复核。取得这些证据前,本文不得升级为真实事故复盘。
4.3 合成 Trace 样例
下面数字只用于解释排障顺序,不代表真实系统指标:
json
{
"request_id": "drill-rag-001",
"incident_type": "failure-drill",
"tenant_id": "tenant-demo",
"query": {
"raw_hash": "sha256:demo",
"rewrite_version": "rewrite-v3"
},
"versions": {
"corpus": "corpus-2026-07-15",
"acl_schema": "acl-v1",
"filter_builder": "filter-v2",
"embedding": "embed-v2",
"index": "index-v2-candidate",
"reranker": "rerank-v1",
"prompt": "answer-v4"
},
"candidate_counts": {
"before_acl": 24,
"after_acl": 0,
"bm25": 0,
"dense": 0,
"rerank": 0,
"final_context": 0
},
"degraded": true,
"fallback_reason": "dense_timeout",
"outcome": "empty_retrieval"
}这条 Trace 的价值不是数字本身,而是同时暴露 acl_schema=acl-v1 与 filter_builder=filter-v2,并证明候选首次在 ACL 之后归零。若 after_acl 非零而 final_context 为零,则应继续检查融合、重排和上下文预算,不能沿用同一根因。
5. 影响范围、时间线与指标口径
5.1 演练影响范围
| 维度 | 演练设定 |
|---|---|
| 受影响请求 | 命中候选索引、使用新版 Filter Builder、涉及 ACL 或有效期过滤的 Query |
| 未受影响请求 | 固定在健康索引、无版本混用且候选链路完整的 Query |
| 用户影响 | 合法问题被误判为无答案,或答案引用不支持结论的旧证据 |
| 数据风险 | 不允许通过放宽 ACL 恢复;删除或撤权内容仍可见视为 P0 安全失败 |
| 系统影响 | 接口成功率可能正常,但空召回、引用支持率和降级率恶化 |
| 成本影响 | 静默重试和多路重复查询可能放大尾延迟与调用成本,本文不假设具体金额 |
5.2 相对时间线
由于没有真实事故时间,本演练使用相对时间:
| 时间 | 事件 | 应固定的证据 | 决策 |
|---|---|---|---|
| T-30m | 候选索引完成部分构建 | Manifest、预期/实际 Chunk、失败分区 | 不应进入可读状态 |
| T-10m | 新 Filter Builder 灰度 | 兼容矩阵、影子请求、版本握手 | 兼容失败应阻断 |
| T0 | 别名误切到候选索引 | Alias 审计、发布操作者、节点缓存 | 触发演练故障 |
| T+3m | 空召回和降级率告警 | 分租户/版本指标、失败 request_id | 冻结发布并止损 |
| T+8m | Trace 发现 ACL 后首次归零 | 原始/规范化 Filter、候选数、版本 | 回切健康索引 |
| T+15m | 固定问题集回放通过 | Recall、引用支持、权限负向集、延迟 | 小流量恢复 |
| T+30m | 防复发任务登记 | 测试、告警、Runbook、Owner | 复盘未完结 |
5.3 指标定义
指标必须先定义口径再讨论优化:
text
empty_retrieval_rate
= 合法且已授权、在 k_recall 阶段候选数为 0 的请求数
/ 合法且已授权的检索请求总数text
evidence_support_rate
= 人工或规则确认“引用支持答案核心 Claim”的回答数
/ 被抽检且返回了答案与引用的回答总数text
degraded_request_rate
= 任一检索、重排或生成阶段进入降级路径的请求数
/ 同一统计窗口内的有效请求总数统计窗口至少同时观察发布前基线、灰度窗口和恢复窗口,并按租户、Query 类型、节点、语料版本、索引版本、Filter 模板和降级原因分桶。空召回率下降不能单独证明修复,因为降低阈值可能用噪声换取非零结果。
6. 技术栈与故障域架构
6.1 受影响技术点清单
| 技术点 ID | 技术点/环节 | 类型 | 采用方案 | 链路职责 | 版本/证据边界 |
|---|---|---|---|---|---|
| TP-01 | 身份与检索前过滤 | 安全/服务 | 可信身份 + Typed Filter Builder + fail-closed | 在召回前限定租户、ACL、状态和有效期 | 具体权限产品未知;以规范化 Filter 和授权审计为证据 |
| TP-02 | 多路候选召回 | 检索/服务 | BM25 + Dense ANN + RRF 基线 | 兼顾专名精确匹配与语义召回,输出 k_recall 候选 | 向量库与模型不绑定;需固定集实测 |
| TP-03 | 重排与上下文 | 模型/服务 | Cross-Encoder + 去重 + Token 预算 | 将候选压缩到 k_rerank 和 k_context,保留必要证据 | 具体模型 ID 未选定;必须记录候选去留原因 |
| TP-04 | 索引发布治理 | 基础设施 | 候选索引 + Version Manifest + 原子 Alias | 隔离构建、校验完整性、切换和回滚 | 本文为设计方案,未执行真实切换 |
| TP-05 | Trace 与质量门禁 | 可观测/评测 | 分阶段 Span + 候选快照 + 固定问题集 | 定位证据首次消失层,控制影子、灰度和恢复 | Trace 为合成样例;阈值需业务基线确定 |
6.2 故障域架构
图:架构|检索服务的版本、证据与发布边界
替代文本: 用户请求携带可信身份进入查询编排,先经过 Query 改写和 ACL/元数据过滤,再并行访问 BM25 与 Dense 索引,经融合、重排、上下文和生成返回带引用答案;离线摄取构建隔离候选索引,Manifest 校验通过后原子切换别名,观测平面记录各阶段候选、版本、延迟和降级。故障边界位于候选索引、过滤契约和别名切换之间。
图表加载中…
读图结论: 检索服务的发布单元不是单个向量索引,而是语料、解析、切片、ACL、Embedding、Sparse/Dense 索引、缓存和查询契约组成的一致版本集合。
图中的 Alias 只在 Manifest 门禁通过后切换;观测平面贯穿在线请求和发布过程,避免“发布成功”与“检索质量健康”成为两个互不相干的判断。
7. 故障与恢复调用流程
图:技术调用流程|版本混用导致质量回退及恢复验证
替代文本: 用户请求进入编排器后固定身份和版本,ACL 生成过滤条件并并行调用 BM25 和 Dense;健康路径完成融合、重排、上下文与引用,故障路径因过滤契约与候选索引不兼容而空召回,或因 Dense 超时静默使用低质单路结果。恢复控制器固定证据、回切健康别名、清理版本缓存并回放固定问题集,只有权限、质量、延迟和降级指标通过才灰度恢复。
图表加载中…
读图结论: 相关性组件可以受控降级,权限不能降级;候选为零或证据不足时,明确拒答比静默生成更安全。
该流程显式区分接口错误、合法空结果、过滤后空召回和低质降级,避免把所有非正常响应压成一个 success=false。
8. 现象、检测与临时止损
8.1 用户现象与系统信号
| 层级 | 现象 | 关键证据 |
|---|---|---|
| 用户 | 同一问题偶发“无资料”,重试后又出现答案 | request_id、节点、索引版本、缓存键 |
| 引用 | 答案引用旧版本或引用不支持核心 Claim | evidence_id、content_hash、corpus/index version |
| 检索 | ACL 前有候选,ACL 后为零;或一路超时后单路候选不足 | Filter 原文/规范化形式、各阶段候选数、timeout |
| 系统 | HTTP 成功率稳定,但空召回、降级率和引用不支持率升高 | 分层质量指标而非只看接口指标 |
| 发布 | Alias 已切换,候选索引仍有失败分区或版本不兼容 | Manifest、预期/实际 Chunk、兼容矩阵、Alias 审计 |
8.2 最短排障顺序
- 固定
request_id、身份、节点、时间和全量版本; - 核对权威语料中是否存在当前答案;
- 比较原始/改写 Query,检查租户、ACL 和规范化 Filter;
- 找 Sparse、Dense、融合、重排和
final_context的候选首次归零或错配层; - 检查索引新鲜度、Alias、Embedding、过滤契约和缓存键;
- 若正确证据已进入
final_context,再检查 Prompt、模型和引用映射; - 把失败样本放入固定集,完成回切和灰度验证。
8.3 临时止损动作
| 顺序 | 动作 | 目的 | 风险 | 验证与回滚条件 |
|---|---|---|---|---|
| 1 | 冻结候选索引写入和 Alias 变更 | 防止爆炸半径继续扩大 | 新内容暂时不发布 | Alias 审计不再变化 |
| 2 | 回切最近健康索引版本 | 恢复可重放的一致检索 | 新内容暂时不可见 | 固定集和权限集通过 |
| 3 | 清理带版本缓存 | 避免“代码回滚、结果未回滚” | 缓存命中率短时下降 | 新旧版本无混合命中 |
| 4 | ACL 服务异常时 fail-closed | 防止越权和撤权内容泄露 | 私有内容可能临时不可用 | 权限服务健康且负向集通过 |
| 5 | 单路降级必须带 degraded 标记 | 保留部分可信能力 | 回答覆盖率下降 | 引用支持率达到恢复门禁 |
| 6 | 无可靠证据时拒答或转人工 | 阻断幻觉和错误引用 | 用户体验变保守 | 证据链恢复后逐步放量 |
9. 根因分析
9.1 假设与排除过程
| 假设 | 支持证据 | 反证或区分动作 | 验证动作 | 演练结论 |
|---|---|---|---|---|
| Query 改写丢失专有名词 | 改写后实体缺失会造成双路候选下降 | 使用原始 Query 重放;比较改写前后候选 | 关闭改写单变量回放 | 可能是其他故障,不是本演练主根因 |
| ACL/Filter 契约不兼容 | ACL 前非零、ACL 后归零;版本矩阵不一致 | 用同一身份和旧 Filter Builder 重放 | 对比规范化 Filter 与必要证据集合 | 直接原因之一 |
| Embedding 查询新模型、索引仍为旧模型 | Dense 单路异常、BM25 可能正常 | 比较向量维度、预处理、模型与索引版本 | 版本握手和 Dense 固定集 | 可造成相似现象,本文通过版本证据区分 |
| 候选索引未完成就切换 | Manifest 显示失败分区或数量/哈希不完整 | 切回健康 Alias 后恢复 | 注入构建失败并尝试发布 | 直接原因之一 |
| Reranker 删除了正确证据 | 召回阶段存在必要证据,重排后消失 | 绕过 Reranker 或固定候选回放 | 比较 k_recall、k_rerank、k_context | 本 Trace 在 ACL 后已归零,排除为首因 |
| LLM 幻觉 | final_context 已含正确证据但答案不忠实 | 固定上下文重放模型 | 对比 Prompt/模型版本 | 本 Trace 上下文为空,不能先归因于模型 |
9.2 直接原因、根本原因与促成因素
- 直接原因一:候选索引处于未完整验证状态,却被 Alias 切为可读;
- 直接原因二:查询侧使用新版 Filter Builder,索引侧 ACL 字段仍符合旧 Schema;
- 直接原因三:Dense 通道超时后只看“还有 BM25 结果”,没有检查必要证据和引用支持;
- 根本原因:发布单元被错误简化成“一个索引文件”,没有把语料、Parser、Chunk、ACL、Embedding、Sparse/Dense、缓存和查询契约作为不可拆分的版本集合;
- 促成因素:Trace 缺少阶段候选数和淘汰原因,告警只看接口成功率,缓存键未包含完整版本,恢复流程没有固定问题集门禁;
- 非原因:本文合成 Trace 在 ACL 后已归零,因此 Top-k、Reranker 和 LLM 不是首次故障点;它们仍需在其他失败样本中独立验证。
9.3 为什么现有防线失效
| 防线 | 失效方式 | 防复发方向 |
|---|---|---|
| 单元测试 | 只测试 Filter 语法,不验证与真实索引 Schema 兼容 | 加版本兼容和权限成对用例 |
| 离线评测 | 只看整体 Recall,未按 ACL、租户和版本分桶 | 加权限负向集和版本桶 |
| 发布门禁 | 索引构建任务成功即允许切换 | 校验数量、哈希、失败分区、金标和兼容矩阵 |
| 监控告警 | 只看 HTTP 成功率和平均延迟 | 加空召回、首次消失层、引用支持和降级率 |
| 回滚 | 只回滚代码,不处理 Alias 与缓存 | 回滚清单覆盖索引、配置、缓存和节点版本 |
| Runbook | 没有权限与相关性降级边界 | 明确 ACL fail-closed、何时拒答和恢复门禁 |
10. 长期修复与横向选型
10.1 修复候选横向对比
| 技术点 ID | 候选方案 | 优点 | 缺点/代价 | 适用场景 | 不适用场景 | 选择结论与依据 |
|---|---|---|---|---|---|---|
| TP-01 | LLM/字符串直接拼 Filter | 开发快、表达灵活 | 注入、类型、转义和版本风险高 | 只读实验且有强校验 | 多租户和权限检索 | 不采用;Filter 必须由可信身份和类型化构造器生成 |
| TP-01 | Typed Filter Builder + fail-closed | 可验证、可审计、契约明确 | 需维护 Schema 与兼容矩阵 | 企业 RAG、多租户、撤权场景 | 无身份边界的公开语料 Demo | 采用;权限正确性高于回答覆盖率 |
| TP-02 | 仅 Dense ANN | 语义召回链路简单 | 专名、编号和 OOV 可能弱;单路故障无替代 | 语义问答 PoC | 企业专名和高可用生产 | 不作为唯一生产通道 |
| TP-02 | BM25 + Dense + RRF | 精确词与语义互补,异构分数无需直接相加 | 双索引、融合和观测成本更高 | 企业知识检索 | 无评测和无运维能力的小型 Demo | 作为生产基线,权重学习需另有标注集 |
| TP-03 | 无 Reranker,直接取召回 Top-k | 延迟和成本低 | 候选噪声、重复和顺序质量弱 | 小语料、低风险问答 | 多证据和高噪声检索 | 作为降级路径但必须有质量门禁 |
| TP-03 | Cross-Encoder + 上下文选择 | 相关性判断更细,能显式控制上下文预算 | 推理延迟、成本和超时风险 | 高价值复杂问题 | 极低延迟或候选极少 | 主路径采用,超时退回 RRF 且标记降级 |
| TP-04 | 增量写入当前可读索引 | 新鲜度高、链路简单 | 半成品可见、回滚和重放困难 | 可丢弃实验数据 | 生产知识与权限数据 | 不采用 |
| TP-04 | 隔离候选索引 + Manifest + 原子 Alias | 发布和回滚边界清晰,可做影子验证 | 双份存储、构建时间和治理成本 | 生产 RAG 版本发布 | 存储极紧张且可接受停机 | 采用;发布单元覆盖完整版本集合 |
| TP-05 | 仅错误日志和 HTTP 指标 | 接入成本低 | 无法定位候选首次消失层或质量回退 | 最小 Demo | 多阶段生产 RAG | 不足以支撑 RCA |
| TP-05 | 分阶段 Trace + 固定集 + 在线分桶 | 可重放、可归因、能控制灰度 | 存储、隐私、标注和维护成本 | 生产变更和故障复盘 | 无数据治理能力 | 采样、哈希和脱敏后采用 |
10.2 Index Manifest 最小 Schema
yaml
release_id: rag-release-2026-07-15-candidate
state: VALIDATING
versions:
corpus: corpus-2026-07-15
parser: parser-v3
chunker: chunker-v2
acl_schema: acl-v2
filter_builder: filter-v2
embedding: embed-v2
sparse_index: sparse-v18
dense_index: dense-v27
reranker: rerank-v1
cache_schema: cache-v4
checks:
expected_chunks: synthetic-placeholder
actual_chunks: synthetic-placeholder
failed_partitions: []
content_hash_verified: false
acl_negative_set_passed: false
golden_query_set_passed: false
publish:
alias: rag-active
previous_release: rag-release-stable
rollback_tested: false状态至少区分 BUILDING → VALIDATING → ACTIVE → RETIRED。只有所有必需检查通过,才能从 VALIDATING 进入 ACTIVE;失败必须保留旧 Alias,不能把“稍后补齐”当成发布策略。
10.3 为什么不是直接调大 Top-k
Top-k 只能从已经进入候选集合的结果中选更多项,不能补回被 ACL、错误 Filter、错误索引版本或超时丢掉的必要证据。若候选阶段为零,增大 k_context 只是在空集合上取更多结果;若候选包含旧证据,增大 Top-k 还会增加错误上下文和 Token 成本。
11. 修复验证与防复发
11.1 故障注入与回归矩阵
| 注入场景 | 期望系统行为 | 关键断言 | 防回归资产 |
|---|---|---|---|
| 候选索引缺一个分区 | 禁止 Alias 切换 | state != ACTIVE,旧索引继续服务 | Manifest 集成测试 |
| Filter Builder v2 查询 ACL Schema v1 | 版本握手失败,私有内容 fail-closed | 无越权、原因码明确 | Schema 兼容测试 |
| Dense 通道超时 | 使用 BM25/RRF 受控降级或拒答 | degraded=true、有可信引用 | 超时故障注入 |
| BM25 通道超时 | Dense 候选通过质量门禁后受控响应 | 专名查询不足时拒答 | 专名固定问题集 |
| Reranker 超时 | 退回融合排序,不无限重试 | 端到端 deadline 不被突破 | Deadline 测试 |
| Alias 切换中断 | 查询只命中完整旧版或完整新版 | 单请求只有一个 release_id | 原子切换测试 |
| 代码回滚但缓存未清 | 版本缓存键阻止混用 | cache key 含 release_id | 缓存回滚测试 |
| 撤权传播延迟 | 高优先级告警并阻断相关缓存/索引 | 撤权内容不可见 | 权限负向回放 |
11.2 恢复门禁
恢复不使用本文虚构阈值,真实项目应先从业务基线和 SLO 确定数值。门禁至少要求:
- 原失败样本全部重放并得到预期证据或预期拒答;
Recall@k_recall、MRR/NDCG 等选择的检索指标不低于健康基线允许范围;- 答案正确性、引用支持率和无答案拒答符合门禁;
- 权限负向集零越权,撤权传播满足安全 SLO;
- P95/P99、超时率、重试率和降级率处于容量预算;
- 同一请求只绑定一个完整
release_id; - 回滚和人工接管演练通过后再逐级放量。
11.3 监控与告警
| 层级 | 指标或事件 | 分桶维度 | 触发后的第一动作 |
|---|---|---|---|
| 数据/索引 | 构建失败、Chunk 差异、新鲜度、Alias 变化 | release、partition、tenant | 冻结切换并检查 Manifest |
| 权限/过滤 | ACL 拒绝、过滤前后候选、撤权延迟 | tenant、policy、filter template | 保持 fail-closed,检查契约和投影 |
| 检索/排序 | 各路候选数、首次归零层、重复率、Rerank 增益 | query type、index、node | 找具体通道和版本,不先调 Top-k |
| 上下文 | 必要证据覆盖、截断、Token、冲突 | prompt、model、query type | 检查 final_context 与预算 |
| 生成/引用 | 正确性、groundedness、引用支持 | model、prompt、release | 固定上下文重放生成层 |
| 系统/成本 | P95/P99、超时、重试、降级、缓存 | service、node、provider | 限流、熔断或切回健康版本 |
| 安全 | 越权、撤权内容命中、跨租户缓存 | tenant、policy、cache schema | 立即阻断、清缓存并升级事件 |
11.4 最短 Runbook
text
1. 选取失败 request_id,固定身份、节点、时间和 release_id
2. 核对权威语料和权限真值
3. 查原始/改写 Query 与规范化 Filter
4. 找候选首次归零或错配的阶段
5. 检查 Manifest、Alias、版本矩阵和缓存键
6. 权限问题 fail-closed;相关性问题回切或受控降级
7. 重放原失败样本、固定问题集和权限负向集
8. 门禁通过后按影子、小流量、分租户逐步恢复
9. 登记测试、告警、Runbook、Owner 和复验日期12. 技术难点、方案亮点与经验
12.1 难点卡
这个演练最难的不是发现“没有结果”,而是在接口成功、缓存存在、多路检索部分可用的情况下,同时判断可用性、相关性和权限是否安全。难点来自版本组合多、正确证据可能在任一阶段消失,以及降级可能把显式错误变成隐蔽质量问题。本文把问题拆成阶段候选 Trace、完整 Release Manifest 和固定集门禁;当前只完成设计与合成证据,尚未通过真实故障注入验证。
12.2 亮点卡
原来的脆弱基线是“索引任务完成就切别名,接口还有结果就认为降级成功”。本文选择“完整版本集合 + 质量感知降级”,而不是只给向量索引加一个状态字段,因为 ACL、Filter、缓存和查询侧版本同样能造成不可重放。亮点证据是 Manifest、调用流程和故障注入矩阵能分别回答发布、运行和回归问题;代价是双索引存储、影子流量、Trace 数据和评测集维护成本。
12.3 生产风险卡
在故障演练中假设新索引被提前切为可读,影响带 ACL 的部分问题。先冻结发布并回切健康 Alias,再用 request_id 和阶段候选把问题定位到 Filter 契约与索引版本不一致。长期修复是候选索引隔离、版本握手、原子切换和质量门禁,计划通过失败分区、通道超时、切换中断和撤权延迟注入验证,并新增版本混用、空召回、引用支持和撤权传播告警。
12.4 可复用经验与反模式
可复用规则是:先找正确证据第一次消失或错配的阶段,再修那个阶段;发布和回滚必须覆盖完整结果版本,而不是只覆盖代码。
适用范围包括 RAG、搜索、推荐和多阶段证据系统。若系统是单机、无权限、无增量发布的离线实验,可以简化 Alias 和灰度,但仍应保存数据、索引和查询配置版本。
反模式:
- 看见空召回先调大 Top-k 或降低阈值;
- 为恢复结果关闭 ACL 或复用旧权限缓存;
- 只回滚应用代码,不回滚索引、配置和缓存;
- 一路超时后静默生成,响应不标记
degraded; - 只看 HTTP 成功率和平均延迟,不看引用支持与阶段候选;
- 复盘结束只写“加强监控”,没有测试、Owner 和复验日期。
13. 面试表达
13.1 30 秒回答
我复盘的是一次 RAG 检索降级故障演练:候选索引未通过完整性与 ACL 契约校验就被切为可读,部分请求空召回,另一些请求静默降级后返回错误证据。我先固定 request_id 和全量版本,回切健康 Alias,并保证权限失败时 fail-closed;长期用 Version Manifest、原子切换、分阶段 Trace 和固定问题集门禁修复。当前只有合成 Trace、架构和故障注入方案,不能表述成真实线上事故或已经取得量化提升。
13.2 60~90 秒回答
这次演练的关键不是接口报错,而是接口可能返回 200,但证据质量已经退化。我先沿 Query、ACL、Sparse/Dense 候选、融合、重排和 final_context 找正确证据首次消失层,合成 Trace 显示 ACL 前有候选、ACL 后归零,同时 Filter Builder 与 ACL Schema 版本不一致。止损阶段冻结候选索引、回切最近健康 Alias、清理带版本缓存;权限不可用就拒绝私有内容,相关性通道超时才允许带 degraded 标记的受控降级。长期方案把语料、Parser、Chunk、ACL、Embedding、Sparse/Dense、缓存和查询契约绑定成一个 Release Manifest,影子和固定集通过后原子切换。选择这个方案的代价是双份存储、Trace 和评测维护,但它能让发布、回滚和故障重放都落到同一版本边界;真实阈值仍需项目基线验证。
13.3 2~3 分钟项目复盘回答
背景是一套多租户 RAG 检索链路,需要同时保证召回质量、尾延迟和权限隔离。演练设定为新索引分区未全部成功,但发布系统提前切换 Alias;查询侧又使用新版 Filter Builder,索引里的 ACL 字段还是旧 Schema。用户看到两种现象:部分问题空召回,部分问题在 Dense 超时后只用低质单路结果,最终引用旧证据。
我先把止损和长期修复分开。止损先冻结 Alias 和候选索引,回切最近健康版本,缓存键按 release_id 清理;权限服务异常时 fail-closed,没有可靠证据就拒答或转人工。定位时不先调 Top-k,而是固定 request_id、身份、节点和所有版本,记录每路候选数、过滤原因、Rerank 和 final_context,找到正确证据第一次消失的位置。合成 Trace 在 ACL 后归零,因此先排除 Reranker 和模型,把主因定位到发布不原子和 Filter 契约不兼容。
长期修复不是只给向量索引加状态,而是把语料、Parser、Chunk、ACL、Embedding、Sparse/Dense、缓存和查询契约放进 Version Manifest。候选索引隔离构建,数量、哈希、失败分区、权限负向集和固定问题集通过后再原子切换;一路超时时允许质量感知降级,但必须标记 degraded,必要证据不足就拒答。验证计划覆盖构建失败、版本不兼容、通道超时、切换中断、缓存未失效和撤权传播延迟,并同时观察检索、答案、引用、权限、P99 和成本。
这次沉淀的经验是:生产 RAG 的发布单元是完整结果版本,不是单个模型或索引;排障要找证据首次消失层,而不是先换模型或调参数。当前证据边界是故障演练和设计方案,没有真实线上日志、事故影响或提升数字,下一步必须在最小可运行环境执行故障注入后才能升级复盘状态。
13.4 递进追问
- 如何区分合法无答案、过滤后空召回和检索服务异常?
- 为什么 BM25 与 Dense 原始分数通常不能直接相加?
- 哪些降级可以接受,哪些权限边界绝不能降级?
- 为什么回滚代码后结果仍可能没有回滚?
- Index Manifest 至少应绑定哪些版本?
- 修复后为什么不能只看空召回率下降?
- 如果正确证据已经进入
final_context,下一步如何继续定位?
14. 关联证据与参考资料
14.1 仓库内单一事实源
- RAG 基础链路:数据、切片、元数据、ACL 和版本基础;
- RAG 评测与生产工程:分阶段评测、发布和生产排障;
- RAG 多模态监控篇:Trace、阶段指标、降级和版本分桶;
- Milvus 生产问题与性能调优:过滤空召回、写后不可见、版本与 Load 排障;
- AI 知识库项目:半成品索引提前可读的故障演练与 RAG 项目边界;
- RAG 偶发检索为空如何排查:快速阅读版排障入口。
14.2 事实说明
本文没有引用动态产品能力、价格或具体厂商版本,也没有声称完成真实生产修复。未来若绑定 Milvus、Elasticsearch、OpenSearch、PostgreSQL/pgvector 或托管向量服务,必须按实际产品版本、配置和官方资料重新验证发布与一致性机制。
15. 总结
一句话记忆: 检索质量回退要沿 Trace 找到正确证据首次消失层,并用完整 Release Manifest、原子切换和固定集回放把恢复变成可证明的工程闭环。
- 接口成功不等于检索质量健康,必须同时观察候选、上下文、引用、权限和降级;
- 权限过滤只能 fail-closed,不能为了恢复召回而关闭 ACL 或猜测身份;
- 发布和回滚必须绑定语料、解析、切片、ACL、Embedding、索引、缓存和查询契约;
- 止损先回切健康版本,长期修复再用候选索引、Manifest、影子和故障注入消除根因;
- 当前内容是故障演练,图片等待人工复核,真实项目需补运行日志、指标、提交和回归证据。