跳转至

附录 D:证据包与门禁速查(可复制)

目标:把“质量控制”落成默认动作:每次改动都有证据包(可复跑、可裁决、可回滚);每条门禁都有入口、判定、证据与回滚。

说明:本书仓库是文档仓库;本附录描述的是你在产品/服务代码库里落地的结构与模板。命令均为示例,请按你的技术栈替换。

D.1 推荐的“可裁决”仓库骨架

把质量基础设施(评测/证据/Runbook)和源代码(src/)并列,保证它们不仅存在,而且“可见、可复跑、可审计”。

project-root/
├── src/                      # 源代码
├── docs/                     # 项目文档与设计规范(可选)
├── eval/                     # [核心] 评估体系(回归/红队/对比)
│   ├── sets/                 # 评测集与失败样本(版本化)
│   ├── scorers/              # 评分器/裁判 (Gatekeeper Judge)
│   └── configs/              # 评测参数配置 (Admission Policy)
├── reports/                  # [核心] 证据包归档 (Evidence Pack)
│   └── 2025-12-23/
│       └── fix-rag-topk-a1b2c/
├── runbooks/                 # [核心] 10 分钟止损手册 (Rollback Triggers)
│   ├── release.md
│   └── rollback.md
├── schemas/                  # 结构化输出契约
├── tools/                    # 门禁哨兵 (Gatekeeper Scripts)
├── tests/                    # 单元/集成测试
└── Makefile                  # 入口:gate / evidence / release

D.2 证据包标准(Evidence Package)

每次变更(Feature/Fix/Hotfix)上线前,生成一个独立的证据目录;让任何人都能回答四个问题: 1) 我改了什么?2) 用的什么版本组合?3) 怎么验证的?4) 失败怎么回滚?

目录结构与命名规范

  • 路径格式reports/YYYY-MM-DD/<change-id>/
  • 命名建议<type>-<short-desc>-<short-sha>,例如 feat-search-rerank-a1b2c
  • 敏感信息:证据包默认应作为 CI artifact/对象存储归档;若要提交到代码仓库,先确保已脱敏(不得包含密钥与用户 PII)。
<change-id>/
├── manifest.json             # [必须] 版本组合 + 环境指纹 + 门禁结论(模板见下)
├── diffs/                    # [必须] 关键变更快照(可读 diff)
│   ├── prompts.diff
│   ├── config.diff
│   ├── schemas.diff
│   └── infra.diff
├── gates/                    # [必须] 门禁执行输出(原始日志 + 汇总)
│   ├── unit_test_report.*
│   ├── eval_summary.*
│   └── security_scan.*
├── samples/                  # [建议] 本次新增/命中的失败样本(最小复现条件)
├── media/                    # [可选] 人工验收证据(截图/录屏/trace)
└── rollback.md               # [可选] 回滚记录(若发生回滚)

manifest.json 模板(建议最小字段)

{
  "meta": {
    "timestamp": "2025-12-23T14:30:00Z",
    "change_id": "feat-search-rerank-a1b2c",
    "builder": "ci-runner-01",
    "operator": "dev_alias"
  },
  "version_set": {
    "code": { "git_commit": "a1b2c3d4" },
    "config": { "config_hash": "sha256:0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" },
    "data": { "dataset_snapshot_id": "ds_2025_12_23_01" },
    "index": { "index_id": "kb_v17" },
    "model": { "model_id": "model_v4", "checkpoint": "ckpt_0123" },
    "prompt": { "prompt_id": "prompt_v12" }
  },
  "gates": {
    "unit_tests": { "level": "L0", "status": "PASS", "evidence": "gates/unit_test_report.*" },
    "ai_regression": { "level": "L0", "status": "PASS", "evidence": "gates/eval_summary.*" },
    "security": { "level": "L0", "status": "PASS", "evidence": "gates/security_scan.*" }
  },
  "verdict": { "decision": "SHIP", "reason": "score_up+cost_flat", "reviewer": "reviewer_alias" },
  "rollback": { "trigger": "any L0 FAIL", "target_change_id": "previous_version_set_id" }
}

D.3 变更类型与门禁矩阵(最低门槛)

不同类型的变更,对应不同的“最低留档证据”和“默认回滚动作”。你不需要一次性做全,但建议先把最常改的 3–5 类落到 L0。

变更类型 必须留档证据(示例) 最低门禁(示例) 失败判定(示例) 默认回滚动作
Prompt/策略 Prompt Diff + 回归报告 关键用例通过率 ≥ 阈值 核心语义漂移/拒答失控/成本飙升 回退到上一版 prompt
RAG 索引/语料 Chunk/索引快照 + 召回评测 Top-K 召回不低于基线 关键知识点召回失败/过期引用 切回旧 index alias
模型版本 对比评测 + 压测报告 质量不退化且守门指标达标 质量退化/超时率越界 配置指回旧模型
工具/Schema 契约 Diff + Mock 调用日志 向后兼容检查通过 必填字段缺失/类型不匹配 回退代码与契约
前端交互 关键流程截图/录屏 核心路径可用且无阻断错误 白屏/交互死循环/误导文案 回退前端构建产物
后端接口 契约测试 + 迁移脚本 集成测试全绿 5xx 激增/鉴权失败 回退服务版本
数据快照 分布统计 + Schema 校验 异常值/漂移检查通过 空值率激增/主键冲突 停止注入/回退快照
训练/微调 曲线 + held-out 评测 主指标达标且守门不过线 过拟合/灾难性遗忘 丢弃 checkpoint
推理配置 采样参数 diff + 格式检查 输出合同满足率 ≥ 阈值 空输出/重复死循环 回退配置
部署/基础设施 IaC Diff + dry-run 预发健康检查通过 资源不足/启动失败 一键回滚到上一版

D.4 “一条命令入口”:门禁与证据的统一动词

目标不是迷信 Makefile,而是把验证逻辑收敛为简单动词,让本地与 CI 复用同一入口:gate(跑门禁)→ evidence(产证据)→ release(发灰度)。

.PHONY: gate evidence release

TIMESTAMP := $(shell date +%Y-%m-%d)
COMMIT := $(shell git rev-parse --short HEAD)
CHANGE_ID ?= $(shell git branch --show-current | sed 's/\\//-/g')-$(COMMIT)
REPORT_DIR := reports/$(TIMESTAMP)/$(CHANGE_ID)

# 你只需要把下面三条替换成自己项目的命令入口
UNIT_TEST_CMD ?= <your_unit_test_cmd>
AI_EVAL_CMD ?= <your_ai_eval_cmd>
SECURITY_CMD ?= <your_security_scan_cmd>

gate:
    @mkdir -p $(REPORT_DIR)/gates
    @echo "[gate] unit tests" && $(UNIT_TEST_CMD)
    @echo "[gate] ai eval" && $(AI_EVAL_CMD)
    @echo "[gate] security" && $(SECURITY_CMD)

evidence: gate
    @mkdir -p $(REPORT_DIR)/diffs
    @git diff HEAD^..HEAD > $(REPORT_DIR)/diffs/changes.diff
    @echo "Replace this line: write $(REPORT_DIR)/manifest.json (version_set + gate results)"

release: evidence
    @echo "Replace this line: deploy (canary) + verify + rollback on breach"

落地要点: - gate 失败必须以非 0 退出码结束(否则 CI 无法阻断)。 - evidence 只做两件事:归档“原始输出”和“可读摘要”;不要只留截图不留源数据。 - release 必须绑定 Runbook:任何指标越界都有“立即动作 + 验证动作 + 证据留档”。

D.5 发布与回滚 Runbook 速查(10 分钟止损)

发布检查单(Pre-flight)

1) 版本组合可追溯:能说清本次 version_set(代码/配置/数据/索引/模型/prompt)。 2) 门禁可复跑:跑通 gate,并能定位证据输出路径。 3) 证据可归档:证据包已上传/留档(artifact/对象存储),且已脱敏。 4) 灰度可止损:Feature Flag/分桶策略可一键关闭;回滚目标版本组合已写清。 5) 上线后验证:至少跑 1 次端到端关键任务(含异常流),并把输出落进证据包。

回滚矩阵(Rollback Matrix)

触发场景 阈值/信号(示例) 立即动作 验证动作 证据留档
致命崩溃 5xx 错误率 > 阈值 立即回滚到上一版 健康检查恢复 + 关键用例可用 错误日志 + 回滚记录
严重幻觉 Bad case 激增/用户投诉集中 关闭特性开关或降级能力 抽样复测关键 Query Bad case JSON + 截图
性能恶化 P99 延迟 > 阈值 降级并发/缓存/模型,必要时回滚 观测曲线回落到基线 延迟指标导出
成本激增 Token/调用量 > 预算线 限流 + 降级到更便宜路径 单位成本恢复到阈值内 用量报表
安全合规 PII 泄露/越权/注入命中 紧急阻断入口 + 下线能力 红队复现并固定回归 攻击样本 + 审计日志

D.6 最常见 12 个工程坑(现象→根因→复现→修复→回归)

  1. Prompt 漂移
  2. 现象:场景 A 变好,但场景 B 彻底失效。
  3. 根因:Prompt 隐式耦合,且缺乏全局回归。
  4. 最小复现:只跑场景 A 的用例通过;一跑场景 B 回归集就失败。
  5. 修复:把“跨场景关键用例”固定进 L0 回归,并建立版本化评测集。
  6. 回归验证:全量回归通过率恢复到阈值,且失败样本已入库。

  7. 上下文溢出

  8. 现象:长对话/长文档触发 context_length_exceeded 或隐式截断导致答非所问。
  9. 根因:没做 token 预算与截断策略;检索/工具输出不受控。
  10. 最小复现:构造超长输入(或拼接多个长文档),观察报错或质量崩坏。
  11. 修复:上游做 token 计数 + 分段/滑窗;下游做摘要与引用合同。
  12. 回归验证:超长输入不崩溃,且能稳定产出“截断说明 + 可用答案”。

  13. 结构化输出解析失败

  14. 现象:看似输出了 JSON,但包含 Markdown 包裹/注释,解析直接崩溃。
  15. 根因:契约不硬(schema 不校验)或缺少重试/修复策略。
  16. 最小复现:让模型输出 ```json {"ok": true} ```,观察解析失败。
  17. 修复:启用严格模式(如 JSON-only)+ schema 校验;失败走“修复/重试/降级”分支。
  18. 回归验证:给一批脏输出样本,解析与校验能稳定产出结构化结果或明确失败。

  19. RAG 引用幻觉

  20. 现象:回答很像真的,但引用的文档 ID 不存在或内容不匹配。
  21. 根因:引用标记由模型自由生成,未做后置校验。
  22. 最小复现:问只有文档 A 有答案的问题,模型却引用文档 B。
  23. 修复:引用必须来自检索上下文集合;生成后校验引用有效性,无效则重试或拒答。
  24. 回归验证:引用校验通过率达标;无效引用会被拦截并形成失败样本。

  25. 密钥泄露

  26. 现象:密钥/凭据被提交到仓库或写进日志。
  27. 根因:缺乏 pre-commit/CI secret scan;日志策略不脱敏。
  28. 最小复现:在代码/日志中可检索到疑似密钥模式(例如 rg -n \"API_KEY|BEGIN PRIVATE|sk-\")。
  29. 修复:加 secret scan + 轮换密钥;日志脱敏与最小化。
  30. 回归验证:提交包含伪造密钥的改动被拦截;线上日志不再出现敏感字段。

  31. 环境配置漂移

  32. 现象:本地与线上行为不一致(例如线上仍指向旧模型/旧索引)。
  33. 根因:配置没纳入版本管理;生产环境靠手工改。
  34. 最小复现:本地 MODEL_ID=model_v4,线上仍是 model_v3
  35. 修复:配置代码化并进入版本组合;每次发布输出配置 diff 进入证据包。
  36. 回归验证:对比运行环境与 manifest.json 一致;差异直接阻断发布。

  37. 超时/重试不匹配

  38. 现象:长输出被默认超时截断;重试导致重复扣费或重复写入。
  39. 根因:把 LLM 当普通 HTTP;缺少幂等键与预算熔断。
  40. 最小复现:生成长文本或故障注入,观察超时与重复副作用。
  41. 修复:区分流式/异步;写操作要求幂等;重试要受预算与次数约束。
  42. 回归验证:故障注入下不重复写入/扣费,且能在阈值内自动止损。

  43. 测试集污染

  44. 现象:离线分数极高,上线效果却很差。
  45. 根因:评测集泄漏进训练/RAG 索引;或样本与线上分布脱节。
  46. 最小复现:检索评测题,命中“答案原文”就在索引中。
  47. 修复:物理隔离数据;评测集版本化与访问控制;上线后用真实失败样本补齐。
  48. 回归验证:评测题在索引中不可直接命中;离线/线上指标相关性提升。

  49. 非确定性导致的 Flaky Gate

  50. 现象:同样代码,门禁有时过有时挂。
  51. 根因:采样温度不为 0;并发竞态;评测集/裁判漂移。
  52. 最小复现:同一门禁连续跑 10 次,结果不一致。
  53. 修复:门禁默认确定性(低温/固定种子);对非确定评测用 N 次聚合判定。
  54. 回归验证:连续多次运行结论稳定;漂移会被记录为 L1 事件而非静默通过。

  55. 流式渲染乱码/闪烁

    • 现象:Markdown 标记被切分导致渲染闪烁或乱码。
    • 根因:前端把 chunk 当完整文本渲染,未做 buffer/增量解析。
    • 最小复现:Mock 把 **bold** 拆成多个 chunk 返回,观察渲染问题。
    • 修复:增量 buffer + 语法边界处理;必要时降级为纯文本。
    • 回归验证:在弱网与大输出下渲染稳定,且不会破坏结构化区块。
  56. Schema/迁移不兼容

    • 现象:新代码依赖新字段,但数据库/索引迁移未完成,导致线上报错。
    • 根因:迁移与部署时序错乱;缺少向后兼容策略。
    • 最小复现:在旧 schema 上运行新版本触发 Column not found
    • 修复:遵循“先兼容、后切换”;迁移进入发布门禁并产证据。
    • 回归验证:旧 schema + 新代码可运行;迁移完成后再打开新能力。
  57. 错误吞没(Silent Failure)

    • 现象:调用失败但 UI 显示“成功”,用户拿到空内容或错误结果。
    • 根因:过度 try/catch + 无日志/无用户提示;缺少失败分支设计。
    • 最小复现:让调用失败(断网/无权限/预算越界),观察 UI 与日志是否能解释。
    • 修复:失败必须可见:日志 + 埋点 + 用户可恢复入口(重试/降级/反馈)。
    • 回归验证:故障注入下能稳定产出“失败原因 + 下一步动作 + 证据记录”。