2026-09-17 23:45:03 +08:00

144 lines
8.1 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# pbl_validation 模块技能M2 校验引擎)
## 定位
PBL 蓝图校验引擎:**14 维校验 + 5 级质量状态quality_state**,阈值全部配置化常量,
输出固定契约的校验报告,供 pbl_compiler发布门禁、pbl_assessment量规联动
前端蓝图编辑器(问题定位)消费。
## 挂载
```python
# app/pbls.py init()
from pbl_validation.init import load_pbl_validation
load_pbl_validation() # 建表 + 注册 5 个契约 + 内置 14 维规则种子(幂等)
```
库名唯一来源:`ServerEnv().get_module_dbname('pbl_validation')`**禁止硬编码 DBNAME**。
## 数据表2 张)
| 表 | 用途 | 关键字段 |
|---|---|---|
| `pbl_validation_rule` | 校验规则14 维,`script_type=1` 规则 JSON 形态) | tenant_id / rule_id / dim / weight / severity / enabled / rule_json(含 thresholds) |
| `pbl_validation_result` | 校验结果14 维得分 + 5 级质量状态) | tenant_id / result_id / blueprint_id / quality_state / total_score / dim_scores / dim_detail / report_json / fingerprint |
DDL`sql/pbl_validation.sql`(幂等,含 14 条内置规则种子)。
## 契约接口5 个,`/pbl_validation/api/<name>.dspy`
| 接口 | 说明 | 必填入参 |
|---|---|---|
| `pbl_validation_run` | 执行 14 维校验并落库,返回报告契约 | `blueprint`dict 或 JSON 串);`tenant_id` 缺省取上下文 |
| `pbl_validation_get` | 按 `result_id``blueprint_id`(最新一次)取报告 | 二者之一 |
| `pbl_validation_list` | 分页列出结果,可按 `blueprint_id`/`quality_state`/`keyword` 过滤 | — |
| `pbl_validation_rule_list` | 列出规则 + 14 维元信息 + 生效阈值/默认阈值 | — |
| `pbl_validation_rule_save` | 新增/更新规则(阈值配置化落库,脏阈值直接拒绝) | `dim`D01~D14 |
`pbl_validation_run` 可选入参:`blueprint_id` / `version` / `dims`(只跑指定维度)/
`thresholds`(阈值覆盖)/ `persist`0=dry-run 不落库)/ `operator`
## 14 维清单
| 维度 | code | 名称 | 权重 | 核心 |
|---|---|---|---|---|
| D01 | structure_completeness | 结构完整性7 类子对象齐备) | 1.2 | ★ |
| D02 | naming_quality | 命名规范 | 0.8 | |
| D03 | objective_alignment | 学习目标对齐 | 1.2 | ★ |
| D04 | driving_question | 驱动问题质量 | 1.0 | |
| D05 | role_design | 角色设计 | 1.0 | |
| D06 | scene_design | 场景设计 | 0.9 | |
| D07 | entity_model | 实体模型 | 0.9 | |
| D08 | task_chain | 任务链完整性 + **多路径覆盖率 mcRatio** | 1.3 | ★ |
| D09 | rule_integrity | 规则引用完整性 | 1.1 | |
| D10 | assessment_rubric | 评估量规(权重和=1 | 1.2 | ★ |
| D11 | evidence_binding | 产出物/证据绑定 | 1.0 | |
| D12 | difficulty_balance | 难度平衡 | 0.7 | |
| D13 | duration_balance | 时长平衡 | 0.7 | |
| D14 | safety_compliance | 安全合规扫描 | 1.0 | ★ |
★ = 核心维度,任一 fail 直接把质量状态压到 Q1。
## 阈值配置化常量(`constants.DEFAULT_THRESHOLDS`
**Q-OPEN-8`mcRatio = 0.7`**(多路径覆盖率阈值)。其余关键项:
`qualityBasicScore=0.40` / `qualityGoodScore=0.70` / `qualityExcellentScore=0.90`
`minSubObjectKinds=7``objectiveCoverageRatio=0.8``roleTaskCoverageRatio=0.8`
`taskEvidenceCoverageRatio=0.8``ruleRefIntegrityRatio=1.0``rubricWeightSum=1.0`
(容差 0.01)、`maxSafetyHits=0`
覆盖优先级(低→高):默认常量 → 规则记录 `rule_json.thresholds` → 接口入参 `thresholds`
(引擎内 `rule_json` 由接口层用「规则库阈值 + 入参阈值」合成,故入参最终生效)。
非法覆盖(未知 key / 类型不符 / 负数 / 比例 >1被忽略并记入 `threshold_ignored`
`rule_save` 侧则**直接拒绝**fail-closed不落脏配置
## 5 级质量状态规则(`engine.decide_quality_state`,自上而下短路)
| 规则 | 条件 | 结果 |
|---|---|---|
| R1 | 无维度结果 / 全部 skip | `Q0_DRAFT` |
| R2 | 存在 blocker 级问题 | `Q1_INCOMPLETE` |
| R3 | 核心维度D01/D03/D08/D10/D14有 fail | `Q1_INCOMPLETE` |
| R4 | 加权总分 < 0.40 | `Q1_INCOMPLETE` |
| R5 | 总分 0.90 且无 warn/fail | `Q4_EXCELLENT` |
| R6 | 总分 0.70 且核心维度全 pass | `Q3_GOOD` |
| R7 | 总分 0.40 | `Q2_BASIC` |
| R8 | 兜底 | `Q1_INCOMPLETE` |
总分 = Σ(维度得分×权重) / Σ权重skip 维度不计入分母)。维度得分pass=1.0、warn=0.6、
fail/skip=0D08/D10 等按覆盖率/权重和连续取值
**发布门禁**`is_publishable()` 要求 `quality_state ∈ {Q3_GOOD, Q4_EXCELLENT}`
## 校验报告契约 `pbl.validation.report/1.0`
```jsonc
{
"contract": "pbl.validation.report/1.0",
"engine_version": "1.0.0",
"report_id": "vr_xxxx", "tenant_id": "t1",
"blueprint_id": "BP1", "version": "v1", "ruleset": "builtin-14d",
"created_at": 1730000000000, "elapsed_ms": 3,
"fingerprint": "sha256前32位载荷+阈值,幂等复用)",
"quality_state": "Q4_EXCELLENT", "quality_label": "优秀", "quality_order": 4,
"quality_reasons": ["R5: 加权总分 0.9700 >= Q4 线 0.90 且无 warn/fail"],
"total_score": 0.97, "weighted_sum": 13.58, "weight_sum": 14.0,
"dim_count": 14, "dim_total": 14,
"state_summary": {"pass": 14, "warn": 0, "fail": 0, "skip": 0},
"severity_summary": {"blocker": 0, "major": 0, "minor": 0, "info": 0},
"issue_count": 0, "passed": true,
"dimensions": [{"dim":"D08","name":"任务链完整性","state":"pass","score":1.0,
"weight":1.3,"weighted_score":1.3,
"metrics":{"mcRatio":1.0,"mcRatio_threshold":0.7,"path_count":2,
"branch_exits_total":2,"branch_exits_covered":2,
"dangling":[],"cyclic_paths":0},
"issues":[]}],
"issues": [{"dim":"D08","code":"V-TASK-004","severity":"major",
"message":"多路径覆盖率 mcRatio=0.50 低于阈值 0.70Q-OPEN-8",
"path":"tasks.mcRatio","expected":0.7,"actual":0.5}],
"thresholds": {"mcRatio": 0.7, "...": "..."},
"threshold_ignored": [], "engine_errors": [],
"dimensions_meta": [{"dim":"D01","code":"structure_completeness","name":"结构完整性",
"weight":1.2,"desc":"..."}]
}
```
### 问题码索引
`V-TENANT-001` 租户缺失 · `V-INPUT-001~004` 载荷非法 · `V-DB-001/002` 库名或 sqlor 不可用 ·
`V-STRUCT-001/002` 结构 · `V-NAME-001~004` 命名 · `V-OBJ-001~003` 目标 ·
`V-DQ-001~004` 驱动问题 · `V-ROLE-001~003` 角色 · `V-SCENE-001~003` 场景 ·
`V-ENT-001~003` 实体 · `V-TASK-001~005` 任务链004=mcRatio005=环) ·
`V-RULE-001/002` 规则引用 · `V-RUB-001~004` 量规004=权重和≠1 ·
`V-EV-001~003` 产出物 · `V-DIFF-001~004` 难度 · `V-DUR-001~003` 时长 ·
`V-SEC-001~005` 安全破坏性 SQL / 危险命令 / 硬编码凭据 / 动态执行 / 不适宜内容 ·
`V-ENGINE-001~003` 引擎禁用 / 返回类型 / 维度异常兜底 fail)。
## 陷阱
- 库名一律 `ServerEnv().get_module_dbname('pbl_validation')`禁止硬编码 DBNAME
- sqlor 只有 `C/U/D/R/I/sqlExe`查询走 `pbl_common.api` `q_all/q_one`已适配
本模块 `api.q_all/q_one` 做了优先 pbl_common回落 sqlor.sqlExe双通道
- 所有读写强制带 `tenant_id``pbl_common.api.tenant_id()`缺失即 fail-closed 报错
- 新增契约需同步四处`api.py` 定义 + `API_REGISTRY` + `__init__.py` 导出 +
`init.py` env 注册 + `scripts/load_path.py` `API_PATHS`
- 引擎是**纯函数** DB/网络/随机同载荷同阈值必得同报告落库只在 api 层做
- 单维度 checker 抛异常不会中断整体引擎兜底记 `fail` + `V-ENGINE-003`fail-closed)。
- 蓝图载荷支持 4 种形态`{objects:[{kind:...}]}` / `{kinds:{task:[...]}}` /
顶层复数键`tasks`/`roles`/... / `{blueprint:{...}}` 包装
## 自测
```bash
cd modules/pbl_validation && python3 tests/test_validation_engine.py # 111 项断言全绿
python3 scripts/load_path.py # 打印路径与契约清单
```