tests/golden:JSON 用例 + 阈值配置 + runner(规则 pass_rate=1.0、检索 0.8 起步)+ pytest 壳;规则/检索两子集种子,Agent/报告子集占位(T-P2-11)。 实现: - runner.py 为普通模块(不依赖 pytest):加载/执行/指标(pass_rate、 first_hit_in_top3)/逐用例差异明细;test_golden.py 只是薄壳(marker=golden, 无 MEIDA_RUN_INTEGRATION 时整体 skip)。用例与 expect 的未知键一律判 fail(封闭词汇表,防断言静默失效)。 - 规则子集 13 例(rules_cases.json,走 packages.rules DSL/evaluate): 含 3 个硬禁忌场景(无条件合规硬拦、正式场合硬拦、复合优先级链 100硬>95禁忌>90体型>50趋势)+ 禁忌压制/维度隔离/多规则叠加/温度边界/ 缺字段静默/include 子集语义等。 - 检索子集 13 例(retrieval_cases.json,DeterministicEmbedder+hybrid_search): 含 7 个 must_miss 红线场景(禁忌体型/draft/archived/场景外/类型外/组合红线)。 种子实体以 golden: 前缀命名,跑前先删(幂等重跑);跑后也删——CI 防线 5c 与集成测试共用长活库,留下假实体会破坏其它用例对结果列表的精确断言。 确定性策略:query=目标 chunk 原文(余弦距离 0 必排第 1)+ 每例独立 marker token(gqNN)隔离关键词路;检索参数(1024 维/top_k 50/默认 RRF 权重)钉死在 runner,不读 env。 - thresholds.json:规则 pass_rate_min=1.0(硬结论子集一条不许退); 检索 pass_rate_min=0.8 + first_hit_in_top3_min=0.8 起步。 - README.md:四子集 330 例规划、两格式 spec、加用例流程、阈值口径与 “为什么规则必须 1.0”。 测试: - MEIDA_RUN_INTEGRATION=1 pytest -m golden --no-cov:2 passed(规则 13/13=1.0,检索 13/13=1.0、first_hit_in_top3=13/13);连续重跑幂等, 跑后库内 golden:% 残留为 0;不设 env 时 2 skipped;基线单测 473 passed、 集成 38 passed 不受影响;ruff check/format、lint-imports 全绿。 - 阻断语义自证(已改回,提交里只有正确版本):临时把规则用例 user-taboo-overrides-trend-recommendation 的 must_avoid 改成不存在的 取值并把 hard_blocks_nonempty 翻转为 true → 规则子集 12/13=0.9231 <1.0 fail,明细输出“must_avoid 未命中:期望 top[length=九分裤脚],实际 avoid=[top[length=[短款, 露脐]]]”与“hard_blocks_nonempty 期望 True, 实际 False(硬拦截规则:无)”;再临时破坏 3 条检索用例(2 条 must_hit 换成不存在的实体名、1 条 must_miss 加入本应命中的实体)→ 检索子集 10/13=0.7692<0.8 fail,明细含“must_miss 红线违规:golden:r12-good 出现在第 1 位——硬过滤零漏被破坏”。验证完三条全部改回,复跑全绿。 (注:检索阈值 0.8 起步,单条退化 0.92 尚不拦截、≥3 条即拦截,符合 种子期口径;规则阈值 1.0 单条即拦截。) 偏差说明:无
tests/
| 目录 | 内容 | 依赖 |
|---|---|---|
unit/ |
领域逻辑、LLM 网关、脱敏、错误码 | 无。不许连库、不许发网络请求 |
integration/ |
带 PostgreSQL / Redis 的链路 | make up,或 testcontainers |
golden/ |
黄金集回归(检索 / 规则 / 报告) | 尚未建立,见下 |
make test-unit # 默认只跑 unit(integration 自动跳过)
MEIDA_RUN_INTEGRATION=1 uv run pytest -m integration
覆盖率门槛
pytest 对 packages/llm 与 packages/domain 统计覆盖率,低于阈值即失败。
这两个包是 mypy strict 的范围,也是业务正确性的核心,其他层靠集成测试覆盖。
绝对不要做的事
- 不要在单测里调用真实的 DeepSeek。
scripts/smoke.sh是唯一会真的花钱的 入口。单测用httpx.MockTransport或tests/unit/test_gateway.py里的FakeProvider。 - 不要
time.sleep()。熔断器、预算闸门都支持注入时钟。 - 不要断言日志文本。断言行为。
黄金集(还没建,但这是最重要的一道防线)
架构 §16.2:
AI 改代码时最容易破坏的是那些「没有断言保护的隐性行为」。检索链路和规则引擎 必须有一套固定输入 → 期望输出的样本集。
M2 落地知识库时必须同步建立(FR-KB-11,≥ 100 条用例): 检索 Top-5 命中率 ≥ 80%、硬过滤零漏、规则命中正确性、报告结构完整性、 硬性禁忌违反数 = 0。指标退化即阻断合并。
已覆盖的关键不变量
| 测试 | 保护的是什么 |
|---|---|
test_prompt_cache_prefix.py |
固定前缀不含易变 token,否则输入成本翻数倍 |
test_daily_budget.py |
日成本超限时拒绝非关键调用,不是只告警 |
test_gateway.py |
路由、故障转移、熔断、结构化重试、每次调用都记成本 |
test_quota_domain.py |
预扣三段式;退还幂等;服务端硬校验 |
test_task_state.py |
任务状态机没有非法跃迁 |
test_log_redaction.py |
手机号 / 令牌 / 密钥 / 诊断结论不进日志 |
test_response_envelope.py |
每个错误码都有文案;配额文案不含价格字样 |