dimensions.py 逐行编码需求文档 §4(32 项四要素、缺失显式 skipped)+ VisionProvider 接口位与确定性实现 + 规则引擎联动 rule_hits(T-P3-01/06)。 实现: - dimensions.py:DimensionSpec 恰 32 项(number 1~32 连续,33/34 永不复用), basic_coverage 口径机器可读:full 10 / label 3 / partial 8 / none 11, 初级覆盖 21/32(BASIC_FULL_KEYS + BASIC_COVERED_KEYS)。 - vision.py:VisionProvider Protocol(派生特征形状写进 docstring,真实 RTMPose/BiRefNet 实现后续按此替换)+ DeterministicVisionProvider(问卷 BMI/自评驱动体型与肤色,sha256(object_key+canonical问卷) 驱动其余枚举, 同输入同输出)+ build_vision_provider(未知名 CONFIG_ERROR)。 - vision_stage.py:derived_features 已存在 → skipped/reused(T-P3-09); 否则分析 → 写中间态并 flush;photo_meta.quality_flags 透传进产物(T-P3-01)。 - diagnosis_stage.py:32 维恒全键,缺输入显式 skipped+原因(T-P3-06 红线); 每维四要素 value/confidence/source/evidence/rule_hits + status;构造 RuleContext → RuleService 载入 published 规则 → evaluate,body_shape/color 类命中回落 #5/#19/#21,全量命中聚合进 #28/#30 的 rule_hits 与 evidence; 色板 #19/#20/#21 从 kb_color 按季型读取,库空 → skipped「色板未录入」。 测试:单测 29 项(32 项完整性/编号连续/确定性/工厂拒绝未知名;四要素齐全 性——故意缺问卷+缺视觉断言 32 键全在且全部 skipped 带原因;规则命中进 rule_hits 用内置最小规则集不连库);集成 1 项(种 2 条 published 规则 → VisionStage → DiagnosisStage:中间态落库与复用 skipped/reused、32 维全键、 rule_hits 出现、kb_color 空时色板 skipped、coverage basic_full=10/32)。 全绿:ruff check/format、mypy packages/report、lint-imports;单测 536 通过 (3 处既有 skip)、集成 46 通过。 偏差说明: 1. 任务文档写 RulesService,实际类名为 RuleService(packages/rules/service.py),按实际代码调用。 2. kb_color 联表键采用四季代码 spring/summer/autumn/winter(仓库无既定口径),已在 vision.py 注释声明。 3. 任务文档只给 basic_coverage 四枚举,需求表格中的「粗判/8色/3色」归入 partial、「Top1」归入 label。
packages/ — 业务域与能力包
架构 §3.2:单体不等于混乱。这里的每个子目录都是一个边界,越界会被 CI 拦下。
目录含义
| 包 | 类型 | 内容 |
|---|---|---|
domain/ |
纯逻辑层 | 领域模型与业务规则。无 IO、无框架、可纯单测 |
shared/ |
基础设施 | 配置、日志、错误码、统一响应、DB、Redis、可观测 |
llm/ |
AI 能力 | LLM 网关(已实现) |
retrieval/ rules/ memory/ |
AI 能力 | 混合检索 / 规则引擎 / 记忆系统(占位) |
compliance/ |
业务域 | 统一合规出口(占位,CR-05/CR-06) |
identity/ billing/ task/ agent/ |
业务域 | 已有实现 |
profile/ report/ tryon/ kb/ notify/ wardrobe/ |
业务域 | 占位 |
每个业务域包的标准结构:
packages/<域>/
├─ __init__.py
├─ README.md 这个域负责什么、不负责什么
├─ service.py 对外唯一入口。其他域只能调这里
└─ repo.py SQL。只有本域可以 import
硬性规则(由 make importlint 机器校验)
- 禁止跨域直接查表。要用别的域的数据,调它的
service,不要 import 它的repo,更不要在自己的 SQL 里 JOIN 别人的表。表名的域前缀(id_/bl_/tk_/ …)就是为将来拆库预留的边界。 packages/domain/必须保持纯净。不许 importfastapi、sqlalchemy、httpx、redis、celery,也不许 importpackages/shared。放不进去说明 它不是领域逻辑,是应用逻辑,应该待在service.py。compliance不得依赖任何业务域。它必须能被任何模块安全调用。identity不得依赖任何其他业务域。登录链路不能被业务拖累。- AI 能力包(
llm/retrieval/rules)不得依赖业务域,也不得 importapps。它们要能在 V2 原样拆成独立服务。
加一个新业务域时
- 建目录,写
README.md说明职责与不负责的事。 - 在
pyproject.toml的[tool.importlinter]契约里把它加进相应的layers/forbidden_modules列表 —— 新域默认没有约束,忘了加就等于没有边界。 - 领域规则先写进
packages/domain/<域>/并配单测,再写service.py。 - 表加域前缀,走 Alembic,迁移必须向后兼容(NFR-MAINT-06)。