2026-09-18 18:24:31 +08:00

13 KiB
Raw Permalink Blame History

name description
pbl_compiler PBL Compiler v1确定性编译 + Game DefinitionM3a/M3b——蓝图版本快照 → GD 10 顶层键29.6 确定性sort_keys/零空白/strip_volatile/sha256/registry_hashUNIQUE(tenant_id, content_fingerprint) 幂等落库,审批+质量双门禁 fail-closed。改本模块前先读「铁律」与「陷阱」两节。

pbl_compiler 模块技能

本文档是 README.md浓缩版(面向 agent。README 为事实源,两者必须双向同步(同步规则见 README §9。 磁盘实测15 个 .dspy 契约、4 张表、api.py:__all__ 35 项、自检 pass=37 fail=0exit=0)。

定位

PBL 产线编译层:把 pbl_blueprint 的蓝图版本快照(含 7 类子对象)编译为运行时可直接消费的 Game DefinitionGD,保证「同输入必得同输出」(需求 29.6 / US-11 / F-CP-03纯确定性、零 LLMmanifest.llmUsed=Falsedeterministic=True)。

不做:蓝图 CRUDpbl_blueprint、14 维校验实现(pbl_validation,只消费其 quality_state)、 Agent 推理(pbl_agent_runtime)、前端渲染(pbl_scense_ext/scense)、证据采集(pbl_evidence)。

挂载

from pbl_compiler.init import load_pbl_compiler   # 应用 apps/pbls/app/pbls.py 的 init() 中按序调用
load_pbl_compiler()                               # 注册 15 契约 + 13 内部函数到 ServerEnv

本模块是 Python 包(业务模块)app.py、无端口、无 Dockerfile、无模块级 build.sh—— 安装步骤由宿主应用 apps/pbls/build.sh 承担pip install → sql/pbl_compiler.sql 建表 → wwwroot 软链 → scripts/load_path.py RBAC → py_compile + scripts/test_m3a_selfcheck.py 门禁)。

数据表4 张,models/*.json 四段式 + sql/pbl_compiler.sql

所有表首列 tenant_id,所有 SQL 强制带 tenant_id;缺失租户上下文 fail-closed 抛错,无「默认租户」兜底。

用途 关键约束
pbl_game_definition GD 编译产物append-onlycontent_fingerprint/definition_json/compiler_version_id/world_id/scene_id/entity_count/event_count/quality_state/duration_ms UNIQUE uk_gd_fp (tenant_id, content_fingerprint) ← 确定性幂等的 DB 级保证;idx_gd_bp (tenant_id, blueprint_id)
pbl_compiler_version 编译器版本谱系:code/semver/rules_hash/entrypoint/enabled/notes UNIQUE uk_cv_code (tenant_id, code)
pbl_capability_registry 能力注册表:capability_key/category/args_schema_json/permission_required/is_enabled/version_no UNIQUE uk_cap_key (tenant_id, capability_key, version_no)
pbl_compile_task 编译任务与审计载体:task_no(确定性派生)/status/approval_id/game_def_id/content_fingerprint/error_code/error_msg/triggered_by/duration_ms UNIQUE uk_ct_task_no (tenant_id, task_no)idx_ct_blueprintidx_ct_status

CRUD 定义(json/compiler_pbl_game_definition.jsoncompiler_pbl_compiler_version.jsoncompiler_pbl_capability_registry.jsonpbl_compile_task 为纯后端任务表,无 CRUD 页面)。

契约接口15 个,路径 /pbl_compiler/api/<name>.dspy

pbl_compiler_compile(POST 编译主流程) · pbl_compiler_preview(POST 试编译不落库) · pbl_compiler_compare(POST 对比) · pbl_compiler_verify_determinism(POSTUS-11/F-CP-03 验收唯一入口) · pbl_compiler_task_get · pbl_compiler_task_list · pbl_game_definition_get · pbl_game_definition_get_by_blueprint · pbl_compiler_version_register · pbl_compiler_version_save · pbl_compiler_version_get · pbl_compiler_version_list · pbl_compiler_version_diff · pbl_capability_list · pbl_capability_register

内部函数(非 .dspy,经 ServerEnv 供模块内/其它模块调用):compile / compile_blueprint / get_compile_task / list_compile_tasks / get_game_definition / get_game_definition_by_blueprint / verify_determinism / register_compiler_version / get_compiler_version / list_compiler_versions / diff_compiler_versions / is_approved / ladder_at_or_above 常量:COMPILER_VERSION / RULESET_VERSION / RULES_HASH_SEED / GD_SCHEMA / GD_TOP_KEYS / QUALITY_LADDER / COMPILE_GATE_MIN_STATE

代码结构(磁盘实测)

pbl_compiler/{__init__.py, init.py, api.py, canonical.py, gd_builder.py}
models/{pbl_game_definition,pbl_compiler_version,pbl_capability_registry,pbl_compile_task}.json
json/compiler_pbl_*.json    sql/pbl_compiler.sql    wwwroot/{index.ui, api/*.dspy ×15}
scripts/{load_path.py, test_m3a_selfcheck.py}    skill/SKILL.md    pyproject.toml    README.md

磁盘无 compiler.py / versioning.py / 模块级 build.sh / app.py——编译主流程在 api.py:compile_blueprint() GD 构建在 gd_builder.py。文档禁止描述磁盘不存在的文件。

确定性规则(canonical.py,改则历史指纹全变)

  • canonical_jsonjson.dumps(sort_keys=True, separators=(',',':'), ensure_ascii=False, allow_nan=False) —— 键排序 + 零空白 + 中文不转义 + 拒绝 NaN/Infinity
  • 浮点定点化(固定精度、去尾零);整数不带小数点;11.0 稳定且互不混淆。
  • strip_volatile:递归(含 list[dict])剥离 id/created_at/updated_at/duration_ms/task_no/ started_at/finished_at/created_by/trace_id/nonce 等,只剥不改业务值。
  • 数组稳定化:语义无序集合按元素规范串排序;语义有序(场景步骤、事件时间线)保持原序,由 gd_builder 显式决定。
  • fingerprint = sha256(canonical_bytes(strip_volatile(obj))) → 64 位小写 hexshort_hash(text, n) 定长前缀哈希。
  • registry_hash(capabilities):归一为 {capability_key, version_no, args_schema_hash, permission_required, is_enabled} 五元组 → 排序 → 规范化 → sha256。入参兼容原始注册表行(args_schema_json)与 GD 派生行 args_schema_hash),两路同值(口径铁律,见陷阱 P1
  • stable_id(blueprint_id, version_no, kind, seq) = {bp}:{ver}:{kind}:{seq:05d}——GD 内一切 id 由输入派生,禁 uuid/random
  • 禁项:指纹路径出现 time.time()/datetime.now()/uuid/random/os.getpid()/dict 迭代序依赖/无 ORDER BY 的 SQL 行序依赖。

GD 10 顶层键(gd_builder.GD_TOP_KEYS,缺任一即 PBL_E_COMPILE

manifest, pbl, world, scenes, entities, events, states, capabilities, assessment, assets manifestschema=pbl.game_definition.v1/gdTopKeys/blueprintId/blueprintVersion/compilerVersion/ rulesetVersion/rulesHash/registryHash/fingerprint/deterministic=True/llmUsed=False/counts

编译主流程 compile_blueprint() ①-⑧fail-closed

① 审批门禁 is_approved()(未审批 → PBL_E_STATE_ILLEGAL/403+ 质量门禁(quality_state 未达 COMPILE_GATE_MIN_STATE → 拒绝)→ ② 建 pbl_compile_task(status=running, approval_id, task_no 确定性派生) → ③ get_version() 取蓝图版本快照(输入锁定)→ ④ build_game_definition(snapshot, ctx, caps) 纯函数生成 GD 10 键(零 LLM→ ⑤ canonical_json+strip_volatile+sha256content_fingerprint → ⑥ 落库 append-only先按 (tenant_id, content_fingerprint) SELECT命中即复用idempotent=true),未命中 INSERTuk_gd_fp 兜并发)→ ⑦ 任务 status=success + 回填 game_def_id/content_fingerprint/duration_ms/finished_at → ⑧ write_audit。 返回 {task_no, status, game_def_id, fingerprint, idempotent, ...}失败必做error_code(PBL_E_*)/error_msg 回写任务 + 落审计 + 不产生 GD 行 + 返回结构化错误(非 500 裸异常)。

自检(scripts/test_m3a_selfcheck.py

cd modules/pbl_compiler && python3 scripts/test_m3a_selfcheck.py ; echo "exit=$?"

退出码 0 = 6 组全过1 = 有 FAIL2 = 被测模块加载失败。纯函数级importlib 按路径加载 canonical.py/ gd_builder.py不连库、不依赖 ServerEnv/ahserverCI 可直接跑。6 组断言: G1 canonical 键序无关/指纹形态 · G2 零空白/ensure_ascii=False/浮点定点/NaN 拒绝 · G3 strip_volatile 易变字段不参与指纹(含递归 list[dict]、算法口径一致) · G4 GD 10 顶层键 + 指纹一致 + manifest.registryHash == registry_hash(GD capabilities.items)(统一口径)+ 派生不丢语义 · G5 同输入重编译指纹全等3 次)/ctx 易变字段不影响/入库顺序颠倒不影响/业务变更必变 · G6 registry_hash 语义(顺序无关、增删能力/升版/改 schema 必变、易变字段不变、空表确定哈希)。 本轮实测:--- summary: pass=37 fail=0 ---exit=0

铁律

  1. 库名禁硬编码:一律 ServerEnv().get_module_dbname('pbl_compiler').py/ get_module_dbname('pbl_compiler').dspy 全局)。 模块内不存在 DB = '...' / DBNAME = '...'。自查:grep -rn "^DB *=\|DBNAME *= *'" pbl_compiler/ --include='*.py' 应无输出。
  2. 三处同步 + RBAC:新增/删除契约必须同改 ① api.py(+__all__) ② __init__.py 导出 ③ init.py env.x = xscripts/load_path.py PATHS禁通配符 %/*,逐条显式)。漏一处 → 路由不可达 / NameError
  3. tenant_id 强制打头:所有读写 SQL 带 tenant_idpbl_common.api.tenant_id()),缺失即 fail-closed。
  4. 指纹路径纯函数:禁时间/随机/uuid/行序依赖GD 内 id 用 stable_id() 派生。
  5. 文档与磁盘一致README/SKILL 的端点表、目录树、表字段必须与磁盘实测一致;声称 write_file 的文件必须真实落盘 (幽灵文件按造假直接退回)。
  6. README ↔ SKILL 同一次提交内同步(表名/约束、15 端点、canonical 规则、GD 10 键、陷阱 P1-P7

陷阱

  • P1 registry_hash 口径不一致(历史两轮退回根因):gd_builder.build_capabilities()补齐派生字段后known.values() 算 hash旧自检对原始注册表行算 → 永不相等 → G4 FAIL、exit=1、安装门禁自证失败。 统一以 GD 内 capabilities.items 为唯一口径,canonical.registry_hash() 兼容两路入参同值。
  • P2 端点表与磁盘不符:曾列出磁盘不存在的 pbl_compiler_status.dspy/pbl_game_definition_list.dspy/ pbl_capability_registry_list.dspy,漏列磁盘已有的 pbl_capability_list/pbl_capability_register/ pbl_compiler_version_save。以 ls wwwroot/api/*.dspy 为准。
  • P3 幽灵文件:声称写入的 scripts/test_m3a_selfcheck.py 曾磁盘不存在 → 按造假退回。
  • P4 硬编码 DB = 'pbl' → 换库/多租户即断。
  • P5 格式串占位符与参数个数不匹配register() TypeError: not enough arguments for format string RBAC 收口失败('[%s] ... total=%d ok=%d pending=%d' % (MODULE, len(PATHS), done, len(missing)))。 离线环境 register() 返回 False + 打印 PENDING <role> <path> 属预期 fail-safe判据是不抛异常
  • P6 三处同步漏一处.dspy 500 NameError: name 'xxx' is not defined
  • P7 volatile 混入指纹 → 每次编译指纹不同,uk_gd_fp 幂等失效;新增易变字段必须同步进 VOLATILE_KEYS
  • P8 文档描述磁盘不存在的代码文件compiler.py/versioning.py/模块 build.sh)。
  • P9 指纹路径引入不确定性NaN、无 ORDER BY、dict 迭代序、datetime.now())。
  • P10 return 写在 async with db.sqlorContext(...) → 静默 Nonereturn data type error, <class 'NoneType'> 块内收集、退出块后return。sqlor 只有 C/U/D/R/I/sqlExesor.I(ns) 只 1 个参数); 查询走 pbl_common.apiq_all/q_one(已适配)。
  • 其它:.dspy禁 import(全局已预载)、禁 ServerEnv() 取 per-request 用户(用 request._run_ns)、 py_compile.dspy 无效(用 grep 审计)、trigger 是 MySQL 关键字(本模块用 triggered_by 规避)、 手工建表必须 COLLATE utf8mb4_unicode_ci(否则 Illegal mix of collations)。

依赖

上游pbl_common(租户/DB 适配/错误码/审计/CRUD 工厂)、pbl_blueprint(蓝图 + 版本快照 + 审批)、 pbl_validationquality_state 门禁)、pbl_appcodes(枚举)、world/sceneGD 落库目标)、 基础模块 ahserver/sqlor/appbase/rbac(只读,禁改)。 下游pbl_agent_runtime(M4)、pbl_evidence(M5)、pbl_assessment(M6)、pbl_kdb_ext(M7)、 pbl_scense_ext/scense(M9)、pbl_runtime_ext(M11)。 开发顺序pbl_commonpbl_appcodespbl_blueprint(M1) → pbl_validation(M2) → pbl_compiler(M3)pbl_agent_runtime(M4) → pbl_evidence(M5) → pbl_assessment(M6) → pbl_kdb_ext(M7) → pbl_scense_ext(M9) → pbl_runtime_ext(M11)。