验收链:七道门,一个判定,一个退出码
全部验收标准写在 src\verify_artifact.py 的模块注释里(第 1 行到 """ 结束),
逐条给出出处、判据、参考数值、以及为什么这道门存在。本技能是它的浓缩版。
.\dist\pack-ternary.cmd verify out\bonsai.ninfer --sweep
# 或直接:
.\dist\.venv\Scripts\python.exe verify_artifact.py <artifact> [选项]
| 选项 | 作用 |
|---|---|
| --gguf PATH | 源 GGUF。默认是 PQ2_0 路径——不覆盖就会拿错源去比。 |
| --template PATH | 启用 STEP 1 的 provenance 交叉校验(identity + 借用对象存在性) |
| --dump PATH | 引擎 embedding activation dump → 打开 STEP 5 |
| --oracle-dump PATH | gemm_oracle.py dump / 旋转 harness 的 dump 目录 → 打开 STEP 5b |
| --oracle-object NAME | gemm_oracle.py 检查哪个对象(默认 text/layers/3/mlp/gate_up) |
| --sweep | 64 层全覆盖(48 GDN + 16 全注意力) |
| --json | 末尾追加 {"artifact", "ok", "results":[{step,status,detail}]} |
| --quiet | 已声明但从未被读取的死选项 |
退出码 0/1。末尾打印 N passed, N failed, N skipped 和
RESULT: PASSED 或 RESULT: FAILED -- do not load this artifact into the engine。
pack-ternary.cmd 把退出码转成桌面通知,FAIL 文案就是"不要装载"。
results 里的 step 键:provenance、inspect、payload_order、row_order、
row_order_sweep、assembly、signs、embedding、engine_oracles。
🚨 必须把同一个
--gguf传给验收。 拿验证器默认的 PQ2_0 gguf 去验一份 PTQ1_0 制品, 比的是两套不同的权重,结果每个 row-order section 都以matched=0失败,而对制品本身 一个字都不说。pack-ternary.cmd的注释里明确记着这条。
链路顺序
SKIP 不是 FAIL:需要本包产不出的东西(引擎 dump、nvcc dump)的门报 SKIP 并说明什么
能打开它。
STEP 1 inspect + provenance
STEP 2 payload order
STEP 3 row order (+ 3b: --sweep 的 64 层覆盖)
STEP 3c assembly audit 17 条位置比对 + 4 条负控
STEP 4 signs
STEP 5 embedding 需要引擎 activation dump
STEP 5b engine oracles 需要 nvcc 内核 dump
负控是判据的一部分,不是装饰
上游原话(verify/oracle_rot.py):
"判据刻意做成双向:文档化变体必须吻合,且一族貌似合理但错误的变体必须全部失败。 单向检查分不清『内核是对的』和『判据太松』"——并把它列为 docs/03 第 4.2 节 6 条判据的 第 3 条。
所以 STEP 3c 不只审制品:它把同一套审计喂给 4 个故意错误的排布,并要求每个都被抓住。 一个存活的负控意味着判据无法失败,于是无论制品如何该门都报 FAILED——因为不能失败的判据 什么都证明不了。
6 条负控与实测
| 位置 | 负控 | 实测结果 |
|---|---|---|
| STEP 2 | offset 0(base/码平面) | 被拒:neg=119273 nonfinite=7749 median=1.5615 |
| STEP 2 | rows*groups*base_bytes(PTQ1_0 落在 high 平面内) | 被拒:neg=118596 nonfinite=8552 median=1.3496(PQ2_0 上这个偏移就是真偏移,所以不提供该负控) |
| STEP 3c | 交换两行 | 被抓:order_mismatches=2 |
| STEP 3c | 整体旋转一行 | 被抓:order_mismatches=6144 |
| STEP 3c | perm48 作用在行上(上游 bug) | 被抓:repeated=4064 missing=4064,与 MAPPING.json 记录的数字一致 |
| STEP 3c | 重复一行、丢一行 | 被抓:repeated=2 missing=2 |
负控只在装配与排布这两道门上。 STEP 1/3/4 是等式与子集判定,不存在"貌似合理的错误 变体"这一类可比对象;要给它们配负控得先定义该变体。
STEP 1 — inspect + provenance
制品必须能通过 tools.artifact 重新打开(MAGIC "NINFER\x00\x02"、目录能解析、
payload 区间不重叠/不乱序/不越界)。强制:identity.model_id 与 identity.weights_id
非空,对象清单非空。
provenance(需要 --template):制品 identity 必须与模板逐字段相同;每个借用对象
(vision/*、mtp/*、frontend/*、text/draft_head*,此模型 353 个)必须存在于模板。
这条专抓"打错了模板"——别的门都发现不了,因为产出的 text 对象两种情况下都看着对。
模板找不到时报 SKIP 而非 FAIL。
STEP 2 — payload order
三元 payload 必须按 row-split 平面存储。两种候选排布解码语义完全相同,所以只有分布
指纹能分辨。scale 平面按格式实际所在的偏移读,偏移取自 row_split_geometry()——
即 pack.py 和引擎加载器用的同一个函数:
PQ2_0_G128 base(32 B/组) | scale(2) scale @ rows*groups*32
PTQ1_0_G128 base(24 B/组) | high(2) | scale(2) scale @ rows*groups*26
每个采样对象强制:
| | 判据 |
|---|---|
| (a) | fp16 scale 平面 neg == 0 |
| (b) | fp16 scale 平面 nan == 0(inf 也算进去,oracle 把它们并在一起) |
| (c) | 0 < median(scale 平面) < 1 |
| (d) | 仅 PQ2_0:\|first_code_zero_share - 0.3278\| <= 0.002 |
(d) 只对 PQ2_0:check_payload_order.py 在 if obj.format == "PQ2_0_G128" 下算码指纹,
而 PTQ1_0 的零码走的是另一条 qs 通道((prod * 3) >> 8 == 1),没有发布过理论占比。
与其编一个常数,不如把该门报为不适用,而 (a)-(c) 照常适用。
健康对象参考值(n=5120, k=6144):
PQ2_0 scale @ rows*groups*32 : neg=0 nan=0 median=0.0167847 max=0.44458
PTQ1_0 scale @ rows*groups*26 : neg=0 nan=0 median=0.016785
zero-share 0.3277 vs 理论 0.3278
PQ2_0 上同样的数字额外与已发布的 check_payload_order.py 交叉比对——一致性是
演示出来的,不是假定的。
STEP 3 — row order
两条 MAPPING.json 规则是 INFERRED 而非实测,且都是整行置换。行是被置换而不是被
变换,所以字节级行指纹是精确测试——这是数值相关性给不了的结构性自证,因为权重
活在旋转基里,相关性无意义。
规则 A — gdn_value_z
value_z = concat(tiled_to_grouped_heads(attn_qkv[4096:10240]),
tiled_to_grouped_heads(attn_gate[0:6144]))
48-head 置换必须在 HEAD 粒度上做,因为该张量的行轴是 48 head x 128 行:
src_row = perm48(row // 128) * 128 + row % 128
perm48(i) = (i % 3) * 16 + (i // 3)
MAPPING 的 risk 字段原话:if the engine test disagrees, flip this single permutation。
evidence:2026-09-19 由引擎测试 RESOLVED(原为 INFERRED)。ssm_conv1d 的 V 通道实测是
tiled 的、其规则已按 head 粒度置换(cos +0.99504);alpha/beta 是 48 行 = 48 head,
所以行粒度就是head 粒度,用同一置换实测 +1.00000 / +0.99587。
规则 B — attn_q_per_head_interleave
attn_q.weight 是 N=12288,视为 48 个 256 行的块。
块 0,2,...,46 = 24 个 query head(顺序不变);块 1,3,...,47 = 24 个 output-gate head。
目标: attention/query_key = concat(query(6144), attn_k(1024))
attention/gate_value = concat(gate(6144), attn_v(1024))
evidence:INFERRED for main layers from the MTP layer——在 MTP 层实测过(逐块
cos +0.99982,24/24 与 24/24 块精确)。MTP 层是形状相同的全注意力层。
判据
每个 section 三条字符串必须全部出现在 oracle 输出里:
(a) "documented rule holds row-by-row: True"
(b) "rows DUPLICATED in the artifact: 0"
(c) "source rows MISSING from the artifact: 0"
这比已发布脚本更严——后者只看 report() 的布尔值。判据里用"多重集"措辞是刻意的:
集合成员测试不充分,因为重复的行仍然能找到源。artifact[a] 要直接与规则指名的
那一行比较,绝不靠搜索匹配指纹来定位。
覆盖
默认只覆盖 layer 0(规则 A)和 layer 3(规则 B)。规则是逐层的,一层不能代表另外 63 层。
--sweep 覆盖 MAPPING.json 列出的全部层:48 个 GDN 层(value_z)+ 16 个全注意力层
(query / key / gate)= 64。对象缺失的层记为 absent 并 FAIL 该 sweep;layer3-only
制品报 1/64 并非零退出,绝不可能静默通过。
check(pack.py)不是替代品:它比的是字节数,而 bug 历史证明字节数恰是这类检查的盲区。
STEP 3c — assembly audit
check_row_order 只覆盖两个被置换的规则。按文档应当逐字拷贝的对象此前无人验证:
token_embedding、output_head、mlp/down、mlp/gate_up、gdn/query_key、
gdn/output、attention/output。pack.py 的 CHECK 3 只比它们的字节数——而字节数
恰恰是"拷贝顺序错了也照样精确正确"的量。
17 条位置比对
text/token_embedding <- token_embd.weight
text/output_head <- output.weight
layer 0 mlp/down <- ffn_down
layer 0 mlp/gate_up[gate 半] <- ffn_gate
layer 0 mlp/gate_up[up 半] <- ffn_up
layer 0 gdn/query_key <- attn_qkv[0:4096]
layer 0 gdn/output <- ssm_out
layer 0 gdn/value_z[0:6144] <- attn_qkv[4096:10240] head-perm
layer 0 gdn/value_z[6144:] <- attn_gate[0:6144] head-perm
layer 3 mlp/down <- ffn_down
layer 3 mlp/gate_up[gate 半] <- ffn_gate
layer 3 mlp/gate_up[up 半] <- ffn_up
layer 3 attention/query_key[0:6144] <- attn_q 偶数块
layer 3 attention/query_key[6144:] <- attn_k[0:1024]
layer 3 attention/gate_value[0:6144] <- attn_q 奇数块
layer 3 attention/gate_value[6144:] <- attn_v
layer 3 attention/output <- attn_output
17 而不是上游的 15:mlp/gate_up 被拆成 gate 半与 up 半,所以拼接边界也做位置比对,
而不是当成一整块审计。15 + 2 = 17。这比上游更强,不是更弱。
每条审计强制:无重复行、无缺失行、顺序不符为 0。加上 4 条负控(见上)。
格式通用性
已发布的 check_assembly.py 是 PQ2_0-only(if tt != 142: raise、CODE_BYTES = 32、
假设 scale 紧跟码平面)。而且 check_row_order.artifact_row_keys 里 GROUPS = 40 写死,
遇到 k=6144 的对象(48 组)直接 assert 失败——也就是说上游脚本根本验不了
attention/output 这类对象。
本包的指纹走 verify/ternary_rows.py,几何从 row_split_geometry() 读、GGML 块布局从
pack.py 的 block_to_planes() 读,于是只有一份布局定义。等价性实测而非假定:
python verify\ternary_rows.py --self-check <PQ2_0 制品> <PQ2_0 gguf>
要求它与已发布实现在 PQ2_0 上指纹完全相同(已通过:制品侧 True、GGUF 侧 True),
所以对 PQ2_0 这是重构而非第二意见。PQ2_0 侧的等价性另行实测:query_key /
gate_value 与上游 helper 的逐行指纹完全相同。
STEP 4 — signs
制品的 text/hadamard_signs(扁平 F32,±1)和 text/hadamard_widths(I32 前缀和)
必须与源 GGUF 的 prism.hadamard.* 元数据块逐位相同——不是捐赠模板的。
由 check_signs.py 强制,判据是 np.array_equal(signs, meta_values)。
Bonsai-2-27B 的参考值:sign_widths = [5120, 6144, 17408](和 28672)、block_size 1024、
sign_mode explicit、transform normalized-sylvester-walsh-hadamard、
inverse_weight_names ['token_embd.weight']、gdn_v_grouped 1、401 个 folded weight 名
(含 inverse 共 402 个 folded 矩阵)。
GGUF 没有 prism.hadamard.* 键时这一步 SKIP 而非 FAIL——那它就不是 Prism folded 导出,
检查不适用。
STEP 5 — embedding
只有在引擎加载过制品并 dump 出 embedding 激活之后才能跑;没有 --dump 就 SKIP。
check_embedding.py 把引擎 dump 与一份独立的 numpy 重建对比:
h = s * (H * z), z = decode(token_embedding_row[id])
H 是尺寸 1024 的归一化 Sylvester-Walsh Hadamard。要求最差相对 L2 ≤ 0.02,覆盖最多
8 个 token。
上游边界,必须复述,因为它限制了所有这些能证明什么:
T=1时两个候选激活布局重合, 所以只测 T=1 的测试永远发现不了布局或 stride 错误。docs/03 第 1.3 节记录了实测形态: T=1 cos +0.999997 OK,T=55 cos +0.006 错。制品侧的门完全看不到这一类。
STEP 5b — engine oracles
上面所有门都是字节级的,而 MAPPING.json 明说了原因:402 个三元矩阵活在 Hadamard 旋转基
里,所以它们没有一个能用数值相关性验证——只有字节核算、往返相等和引擎加载测试可以。
带 --oracle-dump 时:
gemm_oracle.py check <art> <object> <dump-dir>
纯 numpy 解码,用 float64 重建 y = W' * (H * (s * (P * x)))。覆盖
"到目前为止没有任何字节/尺寸/oracle 检查覆盖的东西:平面指针、2-bit 解码、
本宽度的符号块,以及旋转如何组合成文档化的数学"。
oracle_rot.py <dump-dir>
真实旋转内核 vs 显式 numpy 重建,双向:文档化变体必须吻合,
且貌似合理但错误的变体必须全部失败。
产 dump 需要 verify/harness/ 里的独立 nvcc harness 加 CUDA 工具链,本包不构建。
没有 --oracle-dump 就 SKIP。
PASS 意味着什么
VERIFY 的裁决:任何 FAIL 都会传播——进程非零退出、pack-ternary.cmd 转成桌面通知、
文案是 "do not load it"。PASS 意味着制品清过了所有能跑的门。
它不是引擎加载测试和困惑度测量的替代品,那两者仍是数值质量的最后权威。
实测基线(2026-09-29,PTQ1_0 完整制品):
verify --sweep : 7 passed, 0 failed, 2 skipped RESULT: PASSED
provenance identity 匹配模板,borrowed 353/353
inspect 1126 对象,payload_offset 176,128
payload_order scale @ 6,389,760 (26 B/组): neg=0 nan=0 median=0.0168;2/2 负控被拒
row_order 5/5 section 三条硬门全中
row_order_sweep 64/64 层
assembly 17/17 位置比对 + 4/4 负控被抓
signs 与源 GGUF 的 prism.hadamard.* 逐位相同,max|diff| = 0.0
embedding SKIP —— 需要引擎 activation dump
engine_oracles SKIP —— 需要 nvcc harness dump
所以"完整无损"现在是实测结论,不再是推断。 没验的是第 5 与 5b 门。
相关技能
ninfer-ternary-format— 平面几何、块布局、perm48(负控 3 的数据来源)ninfer-ternary-pack— 产物是怎么来的ninfer-ternary-tooling— 怎么把诊断脚本单独跑起来ninfer-ternary-traps— 代码与文档不一致的地方(改验收脚本前必读)
Scan to join WeChat group