# pbl_validation —— PBL 校验引擎(M2) 14 维校验 + 5 级质量状态(quality_state),阈值配置化常量(`mcRatio=0.7`,Q-OPEN-8), 输出固定契约 `pbl.validation.report/1.0` 的校验报告。 ## 目录结构 ``` modules/pbl_validation/ ├── pbl_validation/ # Python 包(模块本体,无 app.py) │ ├── __init__.py # 导出:load_pbl_validation + 5 契约 + 引擎纯函数 + 常量 │ ├── constants.py # 阈值配置化常量 / 5 级质量状态 / 严重度 / 阈值合并校验 │ ├── dimensions.py # 14 维定义 + CheckContext + DimResult/Issue + 14 个 checker │ ├── engine.py # 引擎主入口 run_validation + decide_quality_state + 报告契约 │ ├── api.py # 5 个契约接口(.dspy)+ DB 适配(q_all/q_one/tenant_id) │ └── init.py # load_pbl_validation():库名解析/建表/契约注册/规则种子 ├── sql/pbl_validation.sql # 2 张表 DDL + 14 条内置规则种子(幂等) ├── scripts/load_path.py # 模块路径与契约清单注册 ├── tests/test_validation_engine.py # 111 项断言自测(纯函数层,无需 DB) ├── skill/SKILL.md # 模块技能文档(详细规范) └── pyproject.toml ``` ## 快速使用 ```python from pbl_validation import run_validation, is_publishable report = run_validation(blueprint_dict, tenant_id="t1", blueprint_id="BP1", version="v1") print(report["quality_state"], report["total_score"]) # Q4_EXCELLENT 0.97 ok, msg = is_publishable(report) # 发布门禁:需 >= Q3_GOOD ``` 契约接口(HTTP): ``` POST /pbl_validation/api/pbl_validation_run.dspy {"tenant_id":"t1","blueprint":{...}} POST /pbl_validation/api/pbl_validation_get.dspy {"tenant_id":"t1","result_id":"vr_x"} POST /pbl_validation/api/pbl_validation_list.dspy {"tenant_id":"t1","page":1,"page_size":20} POST /pbl_validation/api/pbl_validation_rule_list.dspy {"tenant_id":"t1"} POST /pbl_validation/api/pbl_validation_rule_save.dspy {"tenant_id":"t1","dim":"D08","thresholds":{"mcRatio":0.8}} ``` ## 14 维 / 5 级 - 维度:D01 结构完整性、D02 命名规范、D03 学习目标对齐、D04 驱动问题质量、D05 角色设计、 D06 场景设计、D07 实体模型、D08 任务链完整性(含 mcRatio 多路径覆盖率)、 D09 规则引用完整性、D10 评估量规、D11 产出物绑定、D12 难度平衡、D13 时长平衡、 D14 安全合规。 - 质量状态:`Q0_DRAFT` / `Q1_INCOMPLETE` / `Q2_BASIC` / `Q3_GOOD` / `Q4_EXCELLENT`, 由 R1~R8 规则自上而下短路判定(详见 skill/SKILL.md)。 ## 自测 ```bash cd modules/pbl_validation && python3 tests/test_validation_engine.py # PASSED: 111 FAILED: 0 ``` ## 铁律 - 库名只用 `ServerEnv().get_module_dbname('pbl_validation')`,禁止硬编码 DBNAME; - 所有读写强制带 `tenant_id`,缺失 fail-closed; - sqlor 只用 `C/U/D/R/I/sqlExe`,查询走 `q_all/q_one`; - 引擎为纯函数(无 DB/网络/随机),确定性可复现。