如何基于 vault 的稳定退出码与 --json 输出脚本化 StaffML 题库的构建与校验流程
【免费下载链接】cs249r_bookMachine Learning Systems项目地址: https://gitcode.com/GitHub_Trending/cs/cs249r_book
如果你的目标是把 StaffML 题库(question vault)的「校验 + 构建」做成 CI 或本地可重复执行的脚本,核心难点是:如何区分「题库数据坏了」和「命令敲错了」,以及如何拿到结构化的失败明细而不是去解析终端彩色的 rich 输出。cs249r_book 仓库中的interviews/vault-cli包(vault命令行)正是为此设计的:其退出码分类跨版本稳定(永不重排编号),所有子命令支持--json输出统一信封(envelope)格式,机器可以直接判断成败并提取错误列表。本文基于 interviews/vault-cli/docs/EXIT_CODES.md、interviews/vault-cli/docs/JSON_OUTPUT.md 和 interviews/vault-cli/README.md 的正文内容,给出一条「vault check把关 →vault build出 SQLite → JSON 字段做断言」的连续操作路径。
适用前提:仓库根目录下存在interviews/vault/题库目录(本仓库已包含,questions/下有数千个 YAML 题面文件);Python 版本 ≥ 3.12(pyproject.toml 中requires-python = ">=3.12";README 说明 CI 固定使用 3.12 以保证 hash 稳定)。
退出码契约:脚本分支的依据
vault的退出码定义在 src/vault_cli/exit_codes.py(ExitCodeIntEnum),文档明确「Codes are STABLE across releases. Never renumber. Scripts pin to these.」,即可以安全地按编号写脚本分支:
| 退出码 | 符号 | 含义 | 典型原因 |
|---|---|---|---|
| 0 | SUCCESS | 命令成功完成 | 正常路径 |
| 1 | VALIDATION_FAILURE | 数据不变量、schema 规则或完整性检查失败 | YAML 坏文件、内容 hash 不匹配、registry 不一致 |
| 2 | USAGE_ERROR | 命令调用本身非法 | 缺参数、未知 flag、冲突 flag(由 Typer/Click 触发) |
| 3 | IO_ERROR | 文件系统或本地 I/O 失败 | 权限不足、磁盘满、预期文件缺失 |
| 4 | NETWORK_ERROR | 对 D1、Cloudflare、LLM API 等外部服务的网络调用失败 | D1 不可达、超时、上游 5xx |
| 5 | USER_ABORTED | 交互式确认被拒绝,或确认中 Ctrl-C | 用户在vault rm --hard上输入n |
| 64–78 | — | 预留sysexits.h标准码 | 仅在上述都不适用时使用 |
文档特别指出几个区分点对脚本的意义:1 与 2 的区分决定下一步动作(1 表示「数据坏了,去 git 里修」,2 表示「命令写错了,重读 --help」);5 单独存在是为了脚本不把「用户取消」误判为 bug。对本文主路径(check+build,纯本地操作)而言,实际会遇到的就是 0、1、2、3 四类,4 主要出现在deploy、ship等联网命令。
--json 信封:统一的成功/失败结构
所有支持--json的子命令共用同一外层信封(见 JSON_OUTPUT.md):
{ "ok": true, "exit_code": 0, "exit_symbol": "SUCCESS", "command": "vault <subcommand>", "cli_version": "0.1.0", "data": { }, "errors": [], "warnings": [] }- 成功时:
ok=true、errors=[]、data有值; - 失败时:
ok=false、errors有值、data可能只部分填充; - 即使失败,stderr 退出码仍然正确,同时 stdout 输出上面的 JSON(例如失败时
{"ok": false, "exit_code": 1, "exit_symbol": "VALIDATION_FAILURE", "errors": [...]})。
信封的字段契约是版本化的:重命名ok/exit_code/data属于 CLI major 版本变更,data内新增字段属于 minor 变更。也就是说脚本可以放心依赖这三个顶层字段,不必假设data内部结构永不变。
errors数组的具体形状按命令而异。以vault check --json为例,JSON_OUTPUT.md 给出的是LSP 诊断形状(文档示例):
{ "ok": false, "exit_code": 1, "exit_symbol": "VALIDATION_FAILURE", "command": "vault check", "data": { "checks_run": 26, "checks_passed": 24, "checks_failed": 2, "tier": "structural" }, "errors": [ { "uri": "file:///.../questions/cloud/l4/diagnosis/foo-7f3a9c-0001.yaml", "severity": 1, "code": "topic-not-in-taxonomy", "source": "vault-check", "message": "topic 'kv-cachee' not found in taxonomy.yaml; did you mean 'kv-cache-management'?" } ] }上面的数字(26 项检查、2 项失败)只是文档示例,你的题库实际数值会不同。从当前代码 src/vault_cli/commands/check.py 可以确认:errors每项至少包含uri(YAML 文件路径)、severity(1=Error,LSP 规范)、code、source、message,脚本只需遍历这些字段即可打印可定位的错误清单。vault check --strict --json的data字段则由loaded(成功加载的题目数)、load_errors(YAML 加载/Schema 错误数)、invariant_failures(不变量失败数)组成,全部为 0 时退出码为 0。
主路径:check 把门、build 出库的脚本
两个命令的签名(来自 README 与 check.py、build.py 源码):
vault check [--vault-dir PATH] [--strict] [--tier fast|structural|all] [--json]:默认--vault-dir为interviews/vault;--strict同时跑 fast + structural 两个 tier(CI 默认),通过时退出 0、任何失败退出 1。--tier slow是 nightly 用的 LSH 场景去重,本文主路径不涉及。vault build [--vault-dir PATH] [--output|-o PATH] [--release-id ID] [--json]:把vault/questions/下的 YAML 编译为 SQLite,默认输出interviews/vault/vault.db,默认--release-id为dev。
注意一个边界:vault build对加载错误是容忍式的——有坏 YAML 时只打 warning、跳过这些记录继续构建(只有「一个题目都没加载出来」才以退出码 1 中止)。所以校验必须在 build 之前由vault check独立完成,不能指望 build 替你把关。下面是把两者串起来的完整脚本:
#!/usr/bin/env bash # 前提:仓库根目录执行;依赖 jq(自行安装)。 set -uo pipefail # ---- 第 1 步:校验题库(CI 默认 strict 档)---- check_out=$(vault check --strict --json 2>&1) rc=$? case $rc in 0) echo "check: PASS" ;; 1) echo "check: FAIL — 数据问题(YAML/schema/registry),需在 git 中修复" echo "$check_out" | jq -r '.errors[] | "\(.uri)\t\(.code)\t\(.message)"' ;; 2) echo "check: USAGE_ERROR — 命令参数写错,重读 vault check --help" ;; 3) echo "check: IO_ERROR — 检查文件系统权限/磁盘/路径" ;; 4) echo "check: NETWORK_ERROR — 外部服务不可达(check 本身通常不涉及网络)" ;; 5) echo "check: USER_ABORTED — 交互确认被取消" ;; *) echo "check: 未知退出码 $rc" ;; esac if [ "$rc" -ne 0 ]; then exit "$rc" fi # ---- 第 2 步:构建 vault.db ---- build_out=$(vault build --release-id dev --json) rc=$? case $rc in 0) echo "build: PASS" # 断言:ok 必须为 true,并提取发布戳信息 echo "$build_out" | jq -e '.ok == true' >/dev/null || { echo "build: ok 字段异常"; exit 1; } echo "$build_out" | jq -r '"release_id=\(.data.release_id) release_hash=\(.data.release_hash) published=\(.data.published_count)"' ;; 1) echo "build: VALIDATION_FAILURE(如:一道题都没加载出来)" ;; *) echo "build: 退出码 $rc" ;; esac exit "$rc"脚本里每一步的判断依据:
case分支直接映射 EXIT_CODES.md 的表;由于编号稳定,这段分支可以跨版本复用。jq -e '.ok == true'是对信封契约(而不是具体数值)的断言:成功时ok必为 true 且errors为空。build成功路径的data字段包含output(vault.db 路径)、release_id、release_hash(64 位 hex)、published_count、policy_version(JSON_OUTPUT.md 的vault build --json条目,其中具体数字为文档示例)。
如果只想在本地前端联调(让interviews/staffml的 dev server 直接渲染本地题目),可选分支是vault build --local:它额外把corpus.json写到interviews/staffml/src/data/corpus.json并镜像到interviews/staffml/public/data/corpus.json(Next.js 加载器实际取用的静态路径),同时镜像题目配图到public/question-visuals/。这是 dev-only 产物,生产构建不读这两个文件(build.py 的--local-json说明)。副作用是会覆盖这些前端目录下的对应文件,仅在跑本地开发时执行。
验证结果:确认构建产物与题库一致
主路径跑完(check 退出 0、build 输出ok=true)后,验证方式分两层:
- 直接断言:
vault.db已写到默认路径interviews/vault/vault.db(或你--output指定的路径),且 build 的 JSON 中data.output指向它。build 命令内部还有一道自校验:写入前端 manifest(interviews/staffml/src/data/vault-manifest.json)前会核对生成数量与 release policy 过滤后的题量是否一致,不一致即以退出码 1 中止——所以ok=true意味着这道校验也已通过。 - 文档声明的发布期校验(可选,属于发布流水线,见 README):
vault verify <release-id> [--git-ref <tag>]做「学术可引用性」round-trip,其--json输出含expected_hash、computed_hash、leaves_verified、match字段(JSON_OUTPUT.md 示例为文档示例值);match: false时退出码为 1,且errors列出前 10 个不一致的叶子。vault stats --json则输出题库的题量、topic 数、chain 数、按 track/level 的分布,适合写进发布记录。
限制与排错边界
--json-schema命令:JSON_OUTPUT.md 提到vault <sub> --json-schema可打印某命令的完整 JSON schema,但当前 src/vault_cli/ 源码中尚未实现该参数(全仓库检索不到)。文档与代码存在版本差异,本文主路径不依赖它;如需确认字段,以 check.py / build.py 实际输出的 JSON 为准。vault serve与vault api不支持--json:前者启动 Datasette、后者是常驻 HTTP 服务,不是 JSON 输出命令(JSON_OUTPUT.md 明确标注 Not applicable),脚本化流程里不要对它们做信封断言。- 退出码 5 的陷阱:如果 CI 中某个带交互确认的命令挂起等待确认后被超时杀掉,会得到可区分的 5(USER_ABORTED)而非一般失败,脚本不应把它计入「数据坏了」。本文的 check/build 流程无交互,不涉及此项。
- 失败时
data可能只部分填充:脚本对失败分支只应读errors,不要假设data完整。 --tier slow不要放进日常 CI:它是 nightly 级别的 LSH 场景去重(README),成本与用途都不同于--strict。- 本地测试套件(开发 vault-cli 本身时才需要):
pip install -e interviews/vault-cli/[dev]后pytest interviews/vault-cli/tests/(README「Run tests」节)。
下一步
脚本化流程跑稳之后,仓库文档给出的延伸路径是完整发布流水线:vault snapshot <ver>→vault migrations-emit <from> <to>→vault publish <ver>→vault verify <ver>(均支持--json,schema 见 JSON_OUTPUT.md),以及链式题序(chains)的构建脚本 scripts/ 五步流程(diagnose_chain_coverage.py→build_chains_with_gemini.py→apply_proposed_chains.py→merge_chain_passes.py,改动后需重跑vault check --strict与vault build --local-json)。这些属于独立任务,本文不展开;本文的 check→build 脚本即可作为它们的前置质量门复用。
【免费下载链接】cs249r_bookMachine Learning Systems项目地址: https://gitcode.com/GitHub_Trending/cs/cs249r_book
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考