实验仓库共享小工具:配置校验 / dispatch 注册表 / 键使用审计 / 实验流程。
把实验仓库反复踩过的坑固化成代码:
- 静默吞键 → schema 白名单校验,未登记键直接报错,错误信息带完整点路径
- dispatch 散落 → 注册表统一登记,新变体 = 新文件 + 一个装饰器,中心代码不再修改
- 键半接线 → 静态审计,扫出"代码读了但 schema 没登记"(野键)与"schema 登记了但代码不读"(死键)
- 流程失序 → 四件套脚手架 + 生命周期状态机 + 一致性体检,防伪闭环
设计原则:只解决三个实证过的问题,不做编排 / 日志框架 / 插值。
要求 Python >= 3.8,依赖 pyyaml。
pip install .装好后提供两个命令:explab 与 explab-audit。
| 模块 | 作用 |
|---|---|
| explab/config.py | 配置加载:base 继承 + 深合并 + schema 校验 + 解析快照 |
| explab/registry.py | dispatch 注册表:@register 登记,create() 按配置字符串创建 |
| explab/audit.py | 键使用审计:代码读取 vs schema 声明双向 diff |
| explab/workflow.py | 实验流程:四件套脚手架 + 状态机 + 一致性体检 + 原子收尾 |
| explab/cli.py | 命令行入口 |
from explab import load_config, register, create
# 配置加载:schema 白名单校验,未知键默认报错
cfg = load_config("configs/run.yaml", schema=schema)
# 注册表:新变体 = 新文件 + 一个装饰器
@register("matcher", "cosine")
class CosineMatcher:
def __init__(self, cfg): ...
matcher = create("matcher", cfg["matching"]["matcher"], cfg["matching"])配置支持 base: <相对本文件的另一个 yaml> 继承:先递归加载 base,再把当前文件的键深合并覆盖上去(dict 递归合并,非 dict 整体覆盖)。消融配置只写与主配置的差异,主配置改动自动传播。
- schema 叶子写类型名字符串:
int/float/number/str/bool/list/dict/any(float接受int,any跳过检查) - 开放子树:该层写
{"_any": "<类型>"}表示键名不限、值按类型查(如逐物体覆盖表,谨慎用) on_unknown支持error(默认)/warn/ignore,迁移期可先降级为警告dump_resolved(cfg, run_dir)把解析后的完整配置写进 run 目录resolved.yaml,复现不靠记忆
替代散落在 pipeline 里的 if/elif 分发。未注册的名字创建时直接报错并列出可用项——没有静默默认。
from explab import register, create, available
@register("matcher", "cosine")
def cosine_matcher(cfg): ...
create("matcher", "cosine", cfg) # 按名字创建
available("matcher") # ['cosine']静态扫描代码里读过的配置键(只认字面量:.get("key") 调用与 cfg["key"] 下标,变量间接访问扫不到),与 schema 双向 diff:
- 野键(代码读了、schema 没登记)→ 报错级:要么补进 schema,要么是拼错的死代码
- 死键(schema 登记了、代码从不读)→ 警告级:"yaml 里改了不生效"的排查线索
explab-audit src/ configs/schema.yaml把实验仓库验证过的纪律固化成代码,新仓库 explab init 即可套用:
explab init # 在当前目录搭四件套脚手架(不覆盖已有文件)
explab init --upgrade # 搭脚手架 + 把 AGENTS.md 托管段升到当前模板版本
explab upgrade # 只升级 AGENTS.md 托管段,不动其它文件
explab check # 流程体检:四件套在场/状态合法/引用路径真实
explab start <实验ID> # 开工:队列行 todo→running + 创建 run 记录
explab finish <实验ID> <状态> # 收尾:状态取 done/dead/blocked所有子命令在项目根下工作,可用 --root <路径> 指定其它项目根。
| 文件 | 职责 |
|---|---|
docs/STATE.md |
唯一权威状态源:冠军数字 / 在跑 / 黑名单 / 已知坑 |
docs/LEDGER.md |
每实验一行状态标签 |
experiments/QUEUE.md |
实验队列表,领实验的唯一入口 |
experiments/runs/*.md |
每实验一份运行记录(由 RUN_TEMPLATE.md 复制而来) |
状态机:todo → running → done / dead / blocked。
防崩坏机制:
start前队列行必须存在且状态为todo,run 记录不存在才允许开工finish一次性改完 QUEUE + run 记录(四件套同一次操作更新),并自动写入完成时间戳check校验:四件套在场、根目录无散落文件、队列与记录一一对应、记录里引用的路径必须真实存在、blocked 行必须写明卡住原因、done/dead 必须带可追溯的证据指针- 告警(不阻塞):done/dead 缺证据指针、running 超 7 天无记录更新
explab init 还会生成 scripts/check_state.py(单文件、纯标准库)与带托管标记的 AGENTS.md;项目特有的黑名单、口径规则、进程核对留在项目自己的脚本里,explab 只管通用纪律。
pip install pytest
pytest