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。
123 lines
7.2 KiB
Python
123 lines
7.2 KiB
Python
"""诊断维度字典(需求文档《美学报告-需求文档》§4 的机器可读形态)。
|
||
|
||
32 个维度是报告产出的**事实核心**:diagnosis_stage 逐维装配四要素(T-P3-06),
|
||
任何输入组合下都必须输出全部 32 个维度——缺失只能显式 skipped,不允许缺键。
|
||
|
||
逐行口径(与需求文档 §4 表格一一对应,2026-08-14 版):
|
||
|
||
- ``source`` 按表「数据来源」列归类到六类:questionnaire(问卷)/ vision
|
||
(视觉分析)/ color(色彩算法)/ kb(知识库映射)/ rule(规则)/ llm。
|
||
「视觉 + 问卷」「规则 + LLM」这类复合来源取**主生产方**(视觉、规则),
|
||
复合事实写进对应维度的 name_cn 与注释。
|
||
- ``basic_coverage`` 按表「初级」列归类:✅→full(完整输出)、标签/Top1→label
|
||
(仅标签)、部分/粗判/8 色/3 色→partial(粗判或子集)、❌→none(不输出)。
|
||
|
||
逐行统计(验收「初级 21/32」口径的来源,见需求文档 §4 表格下方统计):
|
||
|
||
- full(10 项):#1 gender、#2 height_weight、#3 birthday、#5 body_shape、
|
||
#8 face_shape、#12 skin_tone、#17 season_code、#26 psyche_style、
|
||
#27 fusion_style_name、#28 core_goals;
|
||
- label(3 项):#9 face_proportions、#10 facial_details、#25 gene_style;
|
||
- partial(8 项):#6 body_features、#13 face_hue、#14 face_lightness、
|
||
#19 best_colors、#21 avoid_colors、#22 volume、#23 curve_straight、
|
||
#30 preferences_taboos;
|
||
- none(11 项):#4 occupation、#7 body_ratio、#11 face_depth、#15 face_chroma、
|
||
#16 face_contrast、#18 season_detailed、#20 neutral_colors、#24 motion、
|
||
#29 occasion_weights、#31 hair_status、#32 hair_color。
|
||
|
||
合计 10 + 3 + 8 + 11 = 32;初级覆盖(非 none)= 10 + 3 + 8 = **21/32**。
|
||
|
||
编号 33(五行分布)/34(幸运色)已于 2026-08-13 随 TD-31 永久移除,**不复用**:
|
||
避免历史数据与文档交叉引用错位。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass
|
||
|
||
#: basic_coverage 的合法取值(封闭枚举,防止拼写漂移出机器可读口径)。
|
||
BASIC_COVERAGE_VALUES: frozenset[str] = frozenset({"full", "label", "partial", "none"})
|
||
|
||
#: source 的合法取值(按需求文档「数据来源」列归类的六类)。
|
||
DIMENSION_SOURCES: frozenset[str] = frozenset(
|
||
{"questionnaire", "vision", "color", "kb", "rule", "llm"}
|
||
)
|
||
|
||
|
||
@dataclass(frozen=True, slots=True)
|
||
class DimensionSpec:
|
||
"""单个诊断维度的静态定义(字典行)。
|
||
|
||
只描述「这个维度是什么、数据从哪来、初级报告覆盖到什么程度」;
|
||
具体取值与置信度由 diagnosis_stage 装配成 DimensionResult。
|
||
"""
|
||
|
||
number: int # 1~32(33/34 已永久废弃,不复用)
|
||
key: str # 稳定英文键,与 kb/记忆系统词汇对齐(body_shape/face_shape/season_code…)
|
||
name_cn: str
|
||
source: str # questionnaire | vision | color | kb | rule | llm
|
||
basic_coverage: str # "full" | "label" | "partial" | "none"
|
||
|
||
|
||
#: 32 个维度,按需求文档 §4 表格逐行编码;number 恰好 1~32 连续。
|
||
DIMENSIONS: tuple[DimensionSpec, ...] = (
|
||
# -- 问卷直采(基础信息) --------------------------------------------------
|
||
DimensionSpec(1, "gender", "性别", "questionnaire", "full"),
|
||
DimensionSpec(2, "height_weight", "身高/体重", "questionnaire", "full"),
|
||
DimensionSpec(3, "birthday", "生日", "questionnaire", "full"),
|
||
DimensionSpec(4, "occupation", "职业", "questionnaire", "none"),
|
||
# -- 视觉分析(体型与比例) ------------------------------------------------
|
||
DimensionSpec(5, "body_shape", "体型", "vision", "full"),
|
||
DimensionSpec(6, "body_features", "身体局部特征", "vision", "partial"),
|
||
DimensionSpec(7, "body_ratio", "上下身比例", "vision", "none"),
|
||
# -- 视觉分析(面部) ------------------------------------------------------
|
||
DimensionSpec(8, "face_shape", "脸型", "vision", "full"),
|
||
DimensionSpec(9, "face_proportions", "三庭五眼", "vision", "label"),
|
||
DimensionSpec(10, "facial_details", "五官细节", "vision", "label"),
|
||
DimensionSpec(11, "face_depth", "面部立体度", "vision", "none"),
|
||
# -- 色彩(肤档与季型,视觉 Lab 采样 + 色彩算法) --------------------------
|
||
DimensionSpec(12, "skin_tone", "肤色档位", "vision", "full"),
|
||
DimensionSpec(13, "face_hue", "面部色相", "color", "partial"),
|
||
DimensionSpec(14, "face_lightness", "面部亮度", "color", "partial"),
|
||
DimensionSpec(15, "face_chroma", "面部色度", "color", "none"),
|
||
DimensionSpec(16, "face_contrast", "面部对比度", "color", "none"),
|
||
DimensionSpec(17, "season_code", "四季季型", "color", "full"),
|
||
DimensionSpec(18, "season_detailed", "十二季细分", "color", "none"),
|
||
# -- 知识库映射(色板) ----------------------------------------------------
|
||
DimensionSpec(19, "best_colors", "最佳色板", "kb", "partial"),
|
||
DimensionSpec(20, "neutral_colors", "中性色板", "kb", "none"),
|
||
DimensionSpec(21, "avoid_colors", "避雷色", "kb", "partial"),
|
||
# -- 视觉分析(风格量感) --------------------------------------------------
|
||
DimensionSpec(22, "volume", "量感", "vision", "partial"),
|
||
DimensionSpec(23, "curve_straight", "直曲(轮廓)", "vision", "partial"),
|
||
DimensionSpec(24, "motion", "动静", "vision", "none"), # 表列「视觉 + 问卷」,主生产方为视觉
|
||
DimensionSpec(25, "gene_style", "基因风格", "vision", "label"), # 初级 Top1
|
||
# -- 问卷偏好(心理侧) ----------------------------------------------------
|
||
DimensionSpec(26, "psyche_style", "心理风格", "questionnaire", "full"),
|
||
DimensionSpec(27, "fusion_style_name", "融合风格命名", "rule", "full"), # 表列「规则 + LLM」
|
||
DimensionSpec(28, "core_goals", "核心诉求", "questionnaire", "full"), # 问卷 + 推导
|
||
DimensionSpec(29, "occasion_weights", "场景权重", "questionnaire", "none"),
|
||
DimensionSpec(30, "preferences_taboos", "偏好与禁忌", "questionnaire", "partial"),
|
||
# -- 视觉分析(毛发现状) --------------------------------------------------
|
||
DimensionSpec(31, "hair_status", "发长与卷度现状", "vision", "none"),
|
||
DimensionSpec(32, "hair_color", "发色现状", "vision", "none"),
|
||
)
|
||
|
||
#: 初级报告**完整输出**(basic_coverage == "full")的维度键集合,共 10 项。
|
||
#: 这是验收「初级 21/32」口径里 21 = 10 full + 3 label + 8 partial 的机器可读形态。
|
||
BASIC_FULL_KEYS: frozenset[str] = frozenset(
|
||
spec.key for spec in DIMENSIONS if spec.basic_coverage == "full"
|
||
)
|
||
|
||
#: 初级报告**有产出**(full/label/partial,即 basic_coverage != "none")的维度键集合。
|
||
#: len == 21,对应需求文档 §4 统计表的「初级报告覆盖合计 21/32」。
|
||
BASIC_COVERED_KEYS: frozenset[str] = frozenset(
|
||
spec.key for spec in DIMENSIONS if spec.basic_coverage != "none"
|
||
)
|
||
|
||
#: number → spec(诊断装配按编号稳定排序输出用)。
|
||
DIMENSION_BY_NUMBER: dict[int, DimensionSpec] = {spec.number: spec for spec in DIMENSIONS}
|
||
|
||
#: key → spec(视觉/诊断阶段按维度键反查定义用)。
|
||
DIMENSION_BY_KEY: dict[str, DimensionSpec] = {spec.key: spec for spec in DIMENSIONS}
|