xsl 67d9754823 test(golden): data-driven golden set framework wired into CI
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 单条即拦截。)

偏差说明:无
2026-08-14 21:19:57 +08:00

meida_backend · 美搭服务端

「美搭」智能穿搭美学助手的服务端。当前状态是工程地基 + 一条打通的纵向切片 (登录取 token → 配额预扣 → 真实调用 DeepSeek → 记成本 → 提交配额), 业务域大多只有目录与 README,见各 packages/*/README.md。

技术选型与分层依据见仓库外的《技术架构文档》《技术选型与调研报告》,本文只讲怎么跑起来。

快速开始

cp .env.example .env          # 填入 DEEPSEEK_API_KEY
make install                  # uv sync + 装 pre-commit
make up                       # PostgreSQL(pgvector) + Redis,端口 55432 / 56379
make migrate                  # 建表并创建 vector 扩展
make run                      # http://localhost:8000/api/v1/docs

验证真实链路(会产生真实 API 费用,是唯一花钱的入口):

make smoke

它会起依赖、迁移、拉起 API、发一次真实对话,然后把 ob_ai_call 的成本行与 bl_ledger 的配额流水打出来。跑通即证明「计量 + 配额 + 大模型」三者串通。

目录结构

目录 职责
apps/api HTTP 入口。只做鉴权、校验、幂等、配额、统一响应,不放业务规则
apps/worker Celery 长耗时任务入口
packages/domain 纯领域逻辑,不许 import 任何框架与 I/O
packages/llm 大模型网关:场景路由、Prompt 版本、结构化输出、成本计量、预算熔断
packages/shared 配置、日志脱敏、DB、Redis、错误码、中间件
packages/<业务域> identity / billing / task / agent / kb / retrieval / report …
services/ 需要独立伸缩的重型服务(换装、抠图、视觉),V1 为占位
prompts/ 按 <prompt_id>/<version>.txt 归档,与 ob_ai_call.prompt_version 对应
migrations/ Alembic;CI 强制 upgrade → downgrade → upgrade 往返可用

依赖方向由 lint-imports 强制(pyproject.toml 的 [tool.importlinter])。 apps → packages → domain 是单向的,业务包也不得绕过网关直连 provider。

提交前

make check      # lint + typecheck + imports + test + secrets,与 CI 同一套

单测不连库、不发网络请求。需要真实中间件的用例在 tests/integration, 默认跳过,用 make test-integration 显式开启。

两件容易踩的事

  • Prompt 固定前缀不许含时间戳、随机值等易变内容。 命中缓存的输入单价只有未命中的 约 1/30,前缀被污染会让输入成本翻数十倍。LLM_CACHE_PREFIX_MODE=strict 下启动即失败。
  • DeepSeek 自 2026-08-17 起峰谷定价,高峰(北京时间 09:00-12:00、14:00-18:00) 单价是空闲时段的 2 倍。单价配两套,由 packages/llm/cost.py 按调用时刻选择; 成本在调用当时算好落库,事后不按当前价重算。
S
Description
No description provided
Readme
1.4 MiB
Languages
Python 98.4%
Jinja 0.7%
Shell 0.5%
Makefile 0.3%