fix(m5a): pyproject deps + SKILL.md 10契约 + README去幽灵引用 + init/data.json种子

This commit is contained in:
agent.develop 2026-09-21 13:26:11 +08:00
parent 35e272b01a
commit f654cc2115
4 changed files with 145 additions and 20 deletions

View File

@ -127,8 +127,24 @@ RBAC_SET_PERM=/path/to/set_role_perm.py python3 scripts/load_path.py # 实际
自检(无需连库,可放 CI
> **占位说明**:离线自检脚本(三处同步 / RBAC 清单 vs 磁盘 / 模型四段式 / 幂等键语义)
> 由后续子任务 M5a-2 产出,**当前磁盘上尚不存在**,故本 README 不引用其文件名。
> 在它落地前,用下列**当前磁盘上真实存在**的命令完成同等自检:
```bash
python3 scripts/selfcheck_m5a.py # 三处同步 / RBAC 清单 vs 磁盘 / 模型四段式 / 幂等键语义
# ① 依赖可安装pyproject 只声明 sqlor不拉基础包
pip install .
# ② 编码种子 JSON 合法 + parentid 与 models/*.json 的 codes.cond 一致
python3 -c "import json;d=json.load(open('init/data.json'));print([g['parentid'] for g in d['appcodes']])"
grep -o "parentid='[a-z_]*'" models/*.json | sort -u
# ③ 三处同步api.py 契约定义数 == init.py env 契约注册数(对外契约应为 10
grep -c "^async def pbl_" pbl_evidence/api.py
grep -c "^ env.pbl_" pbl_evidence/init.py
# ④ RBAC 清单与 wwwroot 磁盘一致性核对(不连库)
python3 scripts/load_path.py --check
```
## Integration宿主挂载方式
@ -179,9 +195,9 @@ pbl_evidence/
├── models/ # pbl_artifact.json / pbl_evidence.json四段式表定义
├── json/ # CRUD 定义tblname + params + editable
├── wwwroot/ # index.ui + api/ 9 个 .dspy自动路由 /pbl_evidence/...
├── init/data.json # 编码种子Format B3 组 appcodes + appcodes_kv 子项)
├── sql/pbl_evidence.sql # 建表 SQL 快照
├── scripts/load_path.py # RBAC 登记(模块层,禁通配符)+ --check 一致性核对
├── scripts/selfcheck_m5a.py # 离线自检(三处同步 / RBAC / 模型 / 幂等键)
├── skill/SKILL.md # 面向 agent 的模块技能文档
└── pyproject.toml
```

44
init/data.json Normal file
View File

@ -0,0 +1,44 @@
{
"appcodes": [
{
"parentid": "pbl_artifact_type",
"parentname": "PBL产出物类型",
"items": [
{"k": "document", "v": "文档报告"},
{"k": "code", "v": "程序代码"},
{"k": "model", "v": "模型/场景"},
{"k": "presentation", "v": "演示汇报"},
{"k": "poster", "v": "海报展板"},
{"k": "video", "v": "音视频作品"},
{"k": "dataset", "v": "数据/素材集"},
{"k": "other", "v": "其他"}
]
},
{
"parentid": "pbl_artifact_status",
"parentname": "PBL产出物状态",
"items": [
{"k": "draft", "v": "草稿"},
{"k": "submitted", "v": "已提交"},
{"k": "reviewing", "v": "评审中"},
{"k": "returned", "v": "退回修改"},
{"k": "graded", "v": "已评分"},
{"k": "archived", "v": "已归档"}
]
},
{
"parentid": "pbl_evidence_type",
"parentname": "PBL学习证据类型",
"items": [
{"k": "artifact", "v": "产出物"},
{"k": "assessment", "v": "测评作答"},
{"k": "peer_review", "v": "同伴互评"},
{"k": "reflection", "v": "反思日志"},
{"k": "milestone", "v": "里程碑达成"},
{"k": "collaboration", "v": "协作行为"},
{"k": "resource", "v": "资源使用"},
{"k": "observation", "v": "一般观测"}
]
}
]
}

View File

@ -3,7 +3,12 @@ name = "pbl_evidence"
version = "0.1.0"
description = "产出物与证据幂等采集M5a/M5b"
requires-python = ">=3.9"
dependencies = ["apppublic", "sqlor", "ahserver", "appbase", "rbac"]
# 只声明直接代码依赖PyPI 可解析)。
# 基础包 apppublic / ahserver / appbase / rbac 由宿主应用 build.sh 单独 git 安装,
# 不在 PyPI —— 写进来会让 `pip install .` 直接失败module-development-spec 铁律)。
# bricks_for_python本模块 .py 未直接 import前端仅 .ui/.dspy 静态资源),故不声明。
# pbl_common同机构 modules/ 下的兄弟模块,由宿主 pip install -e 装入,非 PyPI 包,故不声明。
dependencies = ["sqlor"]
[build-system]
requires = ["setuptools>=61"]

View File

@ -1,26 +1,86 @@
# pbl_evidence 模块技能(自动生成骨架 + 人工补充)
---
name: pbl_evidence
description: 产出物与证据幂等采集模块M5a/M5b——pbl_artifact/pbl_evidence 两表 CRUD、事件→证据幂等采集、证据列表/统计/水位查询。改本模块前先读完「契约接口」「陷阱」两节。
---
# pbl_evidence 模块技能
## 定位
产出物与证据幂等采集M5a/M5b
产出物artifact管理与学习证据evidence**幂等采集**。M5a 提供产出物 CRUD + 单事件/批量事件落证据;
M5b回放/增量续采)直接复用本模块的采集器与水位接口。
## 挂载
`from pbl_evidence.init import load_pbl_evidence``load_pbl_evidence()`(应用 app/pbls.py init() 中按序调用)
```python
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
- `pbl_evidence`:学习证据(幂等采集,唯一索引防重)
- `pbl_artifact`:学生产出物(版本内联 `version_no`,唯一索引 `uk_art_uid(tenant_id, artifact_uid)`
- `pbl_evidence`:学习证据(唯一索引 `uk_ev_dedup(tenant_id, source_event_id, evidence_type)` 防重)
## 契约接口7 个,路径 `/pbl_evidence/api/<name>.dspy`
- `pbl_artifact_create`
- `pbl_artifact_read`
- `pbl_artifact_update`
- `pbl_artifact_delete`
- `pbl_artifact_list`
- `pbl_evidence_collect`
- `pbl_evidence_list`
表定义:`models/{table}.json`(四段式 summary/fields/indexes/codesCRUD`json/evidence_*.json`
编码种子:`init/data.json`Format 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(...)` 直调;
- 备选(若需走 HTTP`pbl_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 评估复用,不算对外契约、
> 无 .dspy`pbl_collect_evidence_from_events``pbl_resolve_event_table``pbl_evidence_types`
> `pbl_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/*.json``codes[].cond` 一律 `parentid='...'`**绝不用 `id=`**),且三组 parentid 与
`models/pbl_artifact.json``models/pbl_evidence.json` 中的 cond 完全一致。
## 陷阱
- 库名一律 `ServerEnv().get_module_dbname('pbl_evidence')`,禁止硬编码 DBNAME。
- sqlor 只有 `C/U/D/R/I/sqlExe`;查询走 pbl_common.api 的 q_all/q_one已适配
- 所有读写强制带 `tenant_id`pbl_common.api.tenant_id()),缺失即 fail-closed 报错。
- 新增契约需同步三处api.py 定义 + __init__.py 导出 + init.py env 注册 + scripts/load_path.py 路径。
- 库名一律 `get_module_dbname('pbl_evidence')`.dspy 直接调用;.py 用 `ServerEnv().get_module_dbname(...)`
**禁止硬编码 DBNAME**;取不到库名直接抛 `PblError`fail-closed不退化成查错库。
- sqlor 只有 `C/U/D/R/I/sqlExe`;查询统一走 `pbl_common.api``q_all/q_one`(已适配 sqlor 上下文)。
`sor.I` 只接 1 个参数(表名由上下文决定),`sor.I('t', data)` 会 TypeError。
- 所有读写强制带 `tenant_id``pbl_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 安装,不写进 pyproject`apppublic``ahserver``appbase``rbac`
- 兄弟模块:`pbl_common`(租户上下文/错误码/时间序列化/查询封装)