pbl_evidence/skill/SKILL.md

6.5 KiB
Raw Blame History

name description
pbl_evidence 产出物与证据幂等采集模块M5a/M5b——pbl_artifact/pbl_evidence 两表 CRUD、事件→证据幂等采集、证据列表/统计/水位查询。改本模块前先读完「契约接口」「陷阱」两节。

pbl_evidence 模块技能

定位

产出物artifact管理与学习证据evidence幂等采集。M5a 提供产出物 CRUD + 单事件/批量事件落证据; M5b回放/增量续采)直接复用本模块的采集器与水位接口。

挂载

from pbl_evidence.init import load_pbl_evidence
load_pbl_evidence()   # 宿主 apps/pbls 的 app/pbls.py init() 中按拓扑序调用

模块是 Python 包(无 app.py / 无端口 / 无独立部署单元),前端仅 wwwroot/ 下 .ui/.dspy 静态资源。

数据表2 张)

  • pbl_artifact:学生产出物(版本内联 version_no,唯一索引 uk_art_uid(tenant_id, artifact_uid)
  • pbl_evidence:学习证据(唯一索引 uk_ev_dedup(tenant_id, source_event_id, evidence_type) 防重)

表定义:models/{table}.json(四段式 summary/fields/indexes/codesCRUDjson/evidence_*.json。 编码种子:init/data.jsonFormat B3 组 appcodes见下「编码字典」

契约接口10 个,与 pbl_evidence/init.py 契约段 env 注册一一对应)

# 契约函数 .dspy 路径(/pbl_evidence/api/... 说明
1 pbl_artifact_create pbl_artifact_create.dspy 新建产出物,自动补 tenant_id/artifact_uid/version_no=1/status=draft/created_at
2 pbl_artifact_read pbl_artifact_read.dspy 按 id 读,强制租户过滤
3 pbl_artifact_update pbl_artifact_update.dspy 白名单列更新,禁改 tenant_id/id
4 pbl_artifact_delete pbl_artifact_delete.dspy 带归属校验的删除
5 pbl_artifact_list pbl_artifact_list.dspy 分页 + 会话/蓝图/作者/类型/状态过滤
6 pbl_evidence_collect pbl_evidence_collect.dspy 单事件 → 一条证据;重复调用返回 deduped=True
7 pbl_evidence_collect_from_events pbl_evidence_collect_from_events.dspy M5a 主能力:扫 pbl_runtime_event → 映射 → 幂等落库,返回 scanned/created/skipped/updated/ignored/failed + watermark
8 pbl_evidence_list pbl_evidence_list.dspy 学习者/产出物/会话/蓝图/类型/时间窗过滤 + 分页
9 pbl_evidence_stats pbl_evidence_stats.dspy 证据类型分布统计,供 M6 评估与概览页
10 pbl_evidence_watermark 无独立 .dspy(见下) 返回某会话/蓝图已采集到的时间水位,供 M5b/cron 增量续采

watermark 的调用方式(第 10 条契约刻意不生成 .dspy

  • 内部调用:宿主/cron/M5b 通过 ServerEnv().pbl_evidence_watermark(...) 直调;
  • 备选(若需走 HTTPpbl_evidence_stats.dspy 返回值中扩展 watermark 字段stats 内嵌), 当前实现 collector.evidence_stats 只返回 total/by_type尚未内嵌 —— 需要时改 stats 的 返回聚合(res['watermark'] = await _watermark(tid))即可,不必新增 .dspy
  • 若将来需要前端直接轮询,再新增 wwwroot/api/pbl_evidence_watermark.dspy,并同步登记 scripts/load_path.py + 中央 apps/pbls/scripts/load_path.py(双层 RBAC否则 403。

除上述 10 个对外契约外,init.py 还注册了 4 个内部能力(供 M5b 回放 / M6 评估复用,不算对外契约、 无 .dspypbl_collect_evidence_from_eventspbl_resolve_event_tablepbl_evidence_typespbl_event_to_evidence_map。核对口径:api.py 契约定义 10 == __init__.py 契约导出 10 == init.py 契约 env 注册 10 == wwwroot/api/*.dspy 9watermark 无 .dspy

编码字典init/data.jsonFormat B

parentid≤22 字符) 含义 子项
pbl_artifact_type 产出物类型 document/code/model/presentation/poster/video/dataset/other8
pbl_artifact_status 产出物状态 draft/submitted/reviewing/returned/graded/archived6
pbl_evidence_type 证据类型 取自 evidence_map.EVIDENCE_TYPES 8 类artifact/assessment/peer_review/reflection/milestone/collaboration/resource/observation

models/*.jsoncodes[].cond 一律 parentid='...'绝不用 id=),且三组 parentid 与 models/pbl_artifact.jsonmodels/pbl_evidence.json 中的 cond 完全一致。

陷阱

  • 库名一律 get_module_dbname('pbl_evidence').dspy 直接调用;.py 用 ServerEnv().get_module_dbname(...) 禁止硬编码 DBNAME;取不到库名直接抛 PblErrorfail-closed不退化成查错库。
  • sqlor 只有 C/U/D/R/I/sqlExe;查询统一走 pbl_common.apiq_all/q_one(已适配 sqlor 上下文)。 sor.I 只接 1 个参数(表名由上下文决定),sor.I('t', data) 会 TypeError。
  • 所有读写强制带 tenant_idpbl_common.api.tenant_id()),缺失即 fail-closed 报错。
  • dedup_key = md5(tenant_id|source_event_id|evidence_type)32 位定长,必须与 uk_ev_dedup 语义对齐; 改唯一索引必须同步改 evidence_map.build_dedup_key
  • 时间统一格式化为 YYYY-MM-DD HH:MM:SS 字符串(normalize_dt),避免 datetime/str 混用导致 sqlor 占位符绑定失败。
  • 未知事件类型降级为 observation(不丢事件);IGNORED_EVENTS 白名单外置在 evidence_map.py 新增噪声事件改这里,别在 SQL 里写死过滤。
  • 函数注册三处同步:① api.py 定义 → ② __init__.py 导出 → ③ init.py env.<name>=<name> 新增对外路径再加第 ④ 处 scripts/load_path.py+ 中央宿主清单,双层 RBAC禁通配符。 漏 ② → import 期 ImportError漏 ③ → .dspy NameError漏 ④ → 403。
  • 批量采集依赖 M11b 的 pbl_runtime_event;该表不存在时按设计抛 CollectError → PBL_E_DB_UNAVAILABLE(本地环境未联调,勿当成代码缺陷)。
  • wwwroot/{alias}/xls2ui 生成物)是只读构建产物,改逻辑改 json/*.json + models/*.json 后重跑 build。

依赖

  • 直接:sqlor(见 pyproject.toml,仅此一项)
  • 基础包(宿主 build.sh 安装,不写进 pyprojectapppublicahserverappbaserbac
  • 兄弟模块:pbl_common(租户上下文/错误码/时间序列化/查询封装)