AIQR 识别字符串规范 v1.1
AIQR Identifier String — Normative Specification
状态:v1.1。字符集、格式与校验算法为稳定接口,v1.x 内不作破坏性变更。
v1.1 变更(2026-07-27)
v1.0 的字符集是靠排版学分簇挑的,从未在真实 OCR 上验过。现已实测
(docs/ocr-measurement.md,2,900+ 次识别),结果改动如下:
| 改动 | 原因 |
|---|---|
| 擦除上限由 3 降为 2(§5.5) | 三擦除会吃光全部三个校验式,求解必然成功——那不是修复,是伪造。实测端到端误解析 7 例全部由此产生;封顶后归零 |
| 新增 §7.4:解析器 SHOULD 把 OCR 约束到字符集 | 零成本,实测把误解析从 7 例降到 0 |
| §5.4 折叠表理由重写 | 原理由("折错比擦除更糟")被实测推翻,方向相反 |
| §8 补入端到端实测数字 | 原 §8 只有符号层数学性质,不含光学性质 |
线格式(字符集、长度、校验算法)未变;v1.0 生成的标识符在 v1.1 下完全有效。 变的只有解析器的严格度:v1.0 解析器会接受 v1.1 拒绝的三擦除输入。 许可:CC0。任何人可自由实现,无需授权、无需注册、无需接入任何服务。
本文档中的 MUST / MUST NOT / SHOULD / SHOULD NOT / MAY 按 RFC 2119 解释。
本规范只定义印在标签上、给人和 OCR 同时读的那串字符。 二维码本体、字幕版式、Manifest 与发现机制见
aiqr-v0.2.md。
1. 目标
一个印在物理标签上的标识符,同时满足:
- 人能念、能抄、能在电话里报
- OCR 与视觉模型能读,且在翻拍、模糊、压缩、污损条件下仍能读
- 读错时必须能被发现,而不是安静地解析到另一个真实存在的实体
第 3 条是硬约束。当扫码的一端是能执行动作的 Agent 时,"读错一位、解析到另一台设备" 不是显示错误,是执行到错误的物理对象上。因此本规范宁可拒绝,绝不猜测。
三条防线,按承担的工作量排列:
| 层 | 机制 | 作用 |
|---|---|---|
| 1 | 回避(Avoidance) | 字符集里每个易混簇只留一个成员,常见误读拼不出来 |
| 2 | 折叠(Folding) | 集外字符若有唯一近邻,直接映射回去,不消耗校验预算 |
| 3 | 校验(Parity) | 3 个校验符,定位并修复剩余损伤,或明确拒绝 |
2. 字符集
0 2 3 5 6 8 9 A C E H J M P T X Y (17 个符号)
符号 MUST 为大写。符号在 GF(17) 中的值等于它在上串中的下标(0=0 … Y=16)。
2.1 构造依据
从 36 个字母数字出发,按视觉混淆簇分组,每簇只保留一个成员:
| 混淆簇 | 保留 | 被排除 |
|---|---|---|
0 O D Q |
0 | O D Q |
1 I L |
(无) | 1 I L |
2 Z |
2 | Z |
4 A |
A | 4 |
5 S |
5 | S |
6 G |
6 | G |
7 T |
T | 7 |
8 B |
8 | B |
E F |
E | F |
K X |
X | K |
M N W |
M | N W |
P R |
P | R |
U V Y |
Y | U V |
| 无簇 | 3 9 C H J |
— |
1 I L 簇刻意不保留任何成员。这一簇没有安全的幸存者:在多数无衬线字体下三者几乎不可分辨,
留任何一个都会把误读引入字符集内部——那是校验也救不回来的错误类型。宁可牺牲一个符号位。
2.2 为什么是 17
17 是质数,因此 GF(17) 的运算就是普通的 % 17,不需要多项式表或对数表。
一个符合规范的实现在任何语言里都是几十行。对一个要求"大家都遵循"的标准,
可实现性的价值高于每字符多榨出的零点几个比特。
(对比:Crockford Base32 是为人手打字优化的,它保留了 0/D/Q、5/S、2/Z、
8/B、6/G —— 这些在打字时不混,在 OCR 时混。它不适合本用途。)
3. 格式
3.1 规范形式
AIQR:XXXX-XXXX-XXXX-XXXX
- 哨兵 MUST 为字面量
AIQR: - 标识符 MUST 为 16 个符号:13 个数据符 + 3 个校验符
- 校验符 MUST 位于末尾(下标 13、14、15)
- 分组 MUST 为 4 个一组,以
-连接
示例(真实值,由 §9 的派生算法产生):
AIQR:0ATA-XJH3-YXYC-PCC5
AIQR:5PYA-M8YM-E82Y-635M
3.2 哨兵
AIQR: 是给机器的信号,不是给人的说明。它的作用是让视觉模型在一张杂乱的照片里
确定这里存在一个可解析的标识符,从而触发解析流程,而不必靠猜。
哨兵同时承载版本:本规范定义的一切属于 AIQR:。未来的破坏性版本 MUST 使用新哨兵
(如 AIQR2:),MUST NOT 复用 AIQR: 表示不兼容的格式。
标识符脱离标签被引用时(口头、工单、数据库),SHOULD 连哨兵一起引用。
3.3 容量
13 个数据符 × log₂17 = 53.1 比特,即 17¹³ ≈ 9.90 × 10¹⁵ 个标识符。
碰撞风险(生日界,实测计算):
| 已分配标识符数 | 至少一对碰撞的概率 |
|---|---|
| 1,400 万 | 1% |
| 9,950 万 | 50% |
4. 校验
4.1 定义
设 v[0..15] 为 16 个符号值。三个校验式 MUST 全部成立:
S_j = Σ (i+1)^j · v[i] ≡ 0 (mod 17) j = 0, 1, 2
编码时,数据符给定,解 3×3 线性方程组得到三个校验符。系数矩阵是节点为 (i+1) 的
范德蒙矩阵;因为字长 16 < 17,各节点在 mod 17 下互不相同,方程组恒有唯一解。
4.2 为什么是 3 个而不是 2 个
2 个校验符足以定位单字符错误,但定位之后就没有余量去判断损伤是否其实更严重。 在本字符集上实测:2 个校验符会把 68.2% 的双字符错误"修"成另一个合法标识符—— 对一个解析到物理设备的标签,这意味着自信地打开错误的产品。
第 3 个校验符买到的是一个一致性判据。单个错误(位置 p,偏移 e)必然满足:
S0 = e , S1 = (p+1)e , S2 = (p+1)²e ⟹ S1² ≡ S0·S2
而两个错误(位置 p≠q,偏移 e₁,e₂ ≠ 0)代入后:
S1² − S0·S2 = −e₁e₂(p−q)²
因为 e₁,e₂ ≢ 0 且 0 < |p−q| < 17,该值在 mod 17 下恒不为零。
所以 双字符错误 100% 必然被检出,这是代数保证,不是概率。
(穷举验证 184,320 例,拒绝率 100.0000%。)
5. 规范化与折叠
解析器在校验前 MUST 依次执行:
- 大小写 —— 全部转为大写。
- 分隔符 —— 忽略
-、空格、_、.、·、U+2010–U+2015(各种连字符/破折号)。 OCR 会把-读成—、读成空格、或整个丢掉,三种都 MUST 接受。 - 哨兵 ——
AIQR后允许半角:或全角:,前后允许空白。 - 折叠 —— 按下表把集外字符映射为集内字符:
O → 0 D → 0 Q → 0
G → 6 S → 5 Z → 2 B → 8
U → Y V → Y N → M W → M
F → E R → P K → X 4 → A 7 → T
折叠 MUST 在校验之前完成,因此不消耗校验预算——被折叠的字符与正确字符等价。
- 擦除(Erasure) —— 既不在字符集、也不在折叠表中的字符(典型如
1、I、L, 以及任何无法辨认的墨点),MUST 标记为擦除:位置已知、值未知。
5.5 擦除上限(规范性)
解析器 MUST NOT 尝试求解超过 2 个擦除,即使字长允许。
解 k 个擦除消耗 k 个校验式,剩下 3−k 个用于验证结果。k=3 时没有任何校验式剩下, 求解在数学上必然成功——无论其余 13 个字符是否也被读错。那不是修复,是伪造。
实测:允许 3 擦除时,1,200 次端到端识别产生 7 例解析到错误标识符;上限降到 2 后归零,
正确率仅从 74.7% 降至 74.1%。见 docs/ocr-measurement.md §3。
折叠与擦除的分工,实测结论与直觉相反:折叠的价值不在于折得对,而在于把字符挡在 擦除计数之外。折错只变成一个替换错误,而单个替换错误 100% 可修复;三个擦除完全不可修复。 但把
1 I L也折叠、让擦除永不发生,会让任意 16 字符垃圾串只靠校验把关, 约 5% 蒙混过关——实测重新产生 3 例误解析。1 I L不可折叠正是挡住这笔交易的护栏。
6. 解析结果
解析器 MUST 返回以下四种状态之一:
| 状态 | 含义 | 调用方应做什么 |
|---|---|---|
valid |
校验全过 | 直接使用 |
repaired |
校验定位并修复了损伤 | MUST 二次确认,见 §7.3 |
unrepairable |
损伤超出 3 个校验符的能力 | MUST NOT 解析,向用户报告读取失败 |
malformed |
根本不是 AIQR 标识符 | 不是本规范的输入 |
7. 一致性要求
7.1 生成器
- MUST 只输出 §2 字符集内的符号
- MUST 输出 §3.1 的规范形式,含哨兵与分组
- MUST NOT 输出未通过 §4 校验的标识符
7.2 解析器
- MUST 接受 §11 的全部测试向量并给出标注的状态与结果
- MUST 实现 §5 的全部规范化步骤
- MUST 区分
repaired与valid并把区别暴露给调用方 - MUST NOT 在损伤超出能力时返回猜测值
7.3 修复结果不得静默使用(规范性)
repaired 状态的标识符 MUST NOT 被静默解析并用于后续动作。
Agent MUST 满足以下之一:
- 向用户展示修复内容(原读数 → 修复值)并取得确认;或
- 通过独立通道交叉确认——最常见的是同一标签第 2 行的 URL,或解析到的
Manifest 中的
id字段与之一致
理由:修复是基于"损伤不超过设计预算"这一假设的推断。假设不成立时(例如三处替换), 修复可能产生一个合法但错误的标识符。校验能保证双错必被拒,但不覆盖更严重的损伤。 在物理世界里,标签被恶意替换、涂改、部分覆盖都是现实场景,见
aiqr-v0.2.md§8。
7.4 约束 OCR 的字符集(规范性)
解析器 SHOULD 在识别阶段就把 OCR 引擎限制到 §2 的 17 个符号。
- Tesseract 系:设
tessedit_char_whitelist为字符集加- - 视觉模型:在提示词中给出字符集,并说明输出只能来自其中
- 自研引擎:在解码阶段剪掉集外类别
这是零成本的,而且实测收益比任何字符集调优都大:同一批 1,200 次识别,只改白名单, 误解析从 7 例降到 0 例(正确率 74.7% → 66.3%,降的部分全部变成"拒绝"而非"错误")。
机制是引擎输不出集外字符,因而不产生擦除,三个校验式全部留作验证。
8. 保证与限制
本节分两层:8.1–8.2 是符号层的数学性质(由 scripts/test-id.mjs
穷举产出);8.3 是光学层的端到端表现(由 scripts/ocr-confusion.mjs
在真实 OCR 上测得)。两层不可互相外推。本节不写未经测量的断言。
8.1 保证(符号层,穷举)
| 损伤 | 结果 | 实测 |
|---|---|---|
| 任意单字符替换 | 100% 定位并修复为原值 | 20,480 / 20,480 |
| 1 个不可读字符 | 100% 精确恢复 | 1,280 / 1,280 |
| 2 个不可读字符 | 100% 精确恢复 | 3,600 / 3,600 |
| 3 个不可读字符 | 100% 拒绝(见 §5.5) | 280 / 280 |
| 4 个不可读字符 | 100% 拒绝 | 260 / 260 |
| 任意两字符换位 | 100% 绝不解析为错误标识符 | 8,998 / 8,998 |
| 任意双字符替换 | 100% 拒绝(代数保证,见 §4.2) | 184,320 / 184,320 |
| 16 组折叠对 | 100% 恢复,不消耗校验预算 | 252 / 252 |
| 折叠 + 1 个真实错误 | 100% 修复 | 225 / 225 |
8.2 限制(同样是规范的一部分)
- 3 个及以上不可读字符:拒绝。 不是能力不足,是刻意的——见 §5.5。
- 3 个及以上替换错误:不保证。 超出设计预算。一致性判据可能被偶然满足, 产生一个合法但错误的标识符。这正是 §7.3 强制二次确认的原因。
- 错误 + 擦除混合:不支持。 存在擦除时,本规范只按纯擦除处理;若同时存在
位置未知的替换错误,解析 MUST 落到
unrepairable,MUST NOT 尝试联合求解。 - 碰撞不是零。 见 §3.3。标识符不是全局主键——全局身份由标签第 2 行的 URL 承载,DNS 已经保证其唯一性。标识符的职责是:标记这是 AIQR、在 URL 污损时提供冗余路径、 让误读可被发现。仅凭标识符解析 MAY 产生歧义,MUST 与解析到的 Manifest 交叉确认。
8.3 端到端表现(光学层,真实 OCR)
Tesseract,4 种字体 × 12 档劣化(最狠一档 0.18 倍缩放 + 模糊 + JPEG q20 + 旋转), 1,200 次整串识别走完整解析器:
| 正确解析 | 正确拒绝 | 解析到错误标识符 | |
|---|---|---|---|
| 擦除上限 3(v1.0) | 74.7% | 24.3% | 7 (0.6%) |
| 擦除上限 2(v1.1) | 74.1% | 25.9% | 0 |
| 上限 2 + OCR 白名单(§7.4) | 66.3% | 33.7% | 0 |
视觉模型臂(盲测,Claude Opus 5,8 档劣化,n=8):8/8 逐字符全对。
必须一起读的限定:单引擎、合成劣化、每字符 48 次样本、视觉臂 n=8 且评分者即作者。
完整方法、被推翻的推理、以及这次测量不能证明什么,见
docs/ocr-measurement.md。
9. 派生
标识符 SHOULD 由 Manifest URL 确定性派生:
canonical = lowercase(trim(url)) 去掉尾部所有 '/'
digest = SHA-256(canonical)
n = digest 的前 128 位,按大端解释为整数
数据符 = n 在 17 进制下的低 13 位(高位在前)
校验符 = 按 §4.1 求解
同一 URL 恒得同一标识符。无需分配服务器,无需注册表,无需任何协调。
128 位归约到 17¹³(约 53 位)产生的模偏差量级为 10⁻⁷,可忽略。
实现 MAY 改用注册表分配而非派生;两种方式产生的标识符在格式与校验上无区别。
10. 版式建议(非规范性)
标识符印在标签上时:
- SHOULD 使用等宽字体。等宽让 OCR 的字符切分不依赖字距估计。
- SHOULD 保留 §3.1 的
-分组。分组给 OCR 提供锚点,也让人念得出来。 - SHOULD NOT 使用点阵/LED 风格字体渲染中文摘要(画不出汉字);
用于本标识符行 MAY 可行——该行按定义全是 ASCII。实测见
docs/decisive-test.md。 - MUST NOT 截断。被截断的标识符不可恢复,且 OCR 不会提示它被截断过。 文本过长时 MUST 缩小字号而非省略。
11. 测试向量
完整机读版本:spec/test-vectors.json。
符合规范的解析器 MUST 对全部向量给出标注的状态与结果。
| 输入 | 期望状态 | 期望结果 |
|---|---|---|
AIQR:0ATA-XJH3-YXYC-PCC5 |
valid |
0ATA-XJH3-YXYC-PCC5 |
0ata-xjh3-yxyc-pcc5 |
valid |
0ATA-XJH3-YXYC-PCC5 |
0ATAXJH3YXYCPCC5 |
valid |
0ATA-XJH3-YXYC-PCC5 |
AIQR: 0ATA XJH3 YXYC PCC5 |
valid |
0ATA-XJH3-YXYC-PCC5 |
the sticker reads AIQR:0ATA-XJH3-YXYC-PCC5 underneath |
valid |
0ATA-XJH3-YXYC-PCC5 |
OATAXJH3YXYCPCC5(首位 0 被读成 O) |
valid |
0ATA-XJH3-YXYC-PCC5 |
| 单字符替换(下标 3) | repaired |
0ATA-XJH3-YXYC-PCC5 |
1 个字符不可读(I) |
repaired |
0ATA-XJH3-YXYC-PCC5 |
| 3 个字符不可读 | repaired |
0ATA-XJH3-YXYC-PCC5 |
| 4 个字符不可读 | unrepairable |
— |
| 两处替换 | unrepairable |
— |
| 相邻换位 | unrepairable |
— |
AIQR:23AC-5H8M(长度不足) |
malformed |
— |
AIQR: hello world not an identifier |
malformed |
— |
12. 未决问题
C应当被移除。 v1.1 的实测给出了明确信号:C在字符集内正确率最低(52%), 且与4、9、3、G都混。字符集内部还残留C/9C/3J/3P/3T/YX/3Y/2七对混淆——§2.1 的排版学分簇漏掉了它们。 未在 v1.1 动手的原因:移除C会让字符集变成 16(非质数),必须补一个符号, 而补哪个不能再靠猜。每字符 48 次的样本量只够观测 2% 量级的事件,不足以支撑重新推导。 前置条件是把样本量提到每字符 ≥500 次。- 折叠表 12 条零观测。 3 条被数据否定(
4→A实为4←C、G→6实为G←C、S→5实为S←3),12 条既未证实也未否定。之所以保留,是因为实测显示条目的 存在比正确更重要(见 §5.5 注);但这不等于它们是对的。 - 合成劣化 ≠ 真实印刷。 尚未测:真实纸张、反光、曲面(贴在圆柱形机身上)、 低光、远距离、部分遮挡。这些是工业现场的常态。
- 17 是否最优。 19 或 23 个符号能提高密度,13 个能提高安全余量。当前取值是权衡判断, 不是优化结果。等 (1) 的数据到位后应重算。
- 更短的档位。 16 符对小型标签(药品、元件)可能偏长。可以定义一个共用字符集与校验 算法、但数据符更少的短档;代价是容量与碰撞风险。在有真实需求前不定义。
- 签名。 标识符本身不携带真实性证明。防标签替换最终需要来源签名,属 Manifest 层,
见
aiqr-v0.2.md§10。 - 英文版。 公开发布前 MUST 提供
aiqr-id-v1.1.en.md。
附录 · 参考实现
src/aiqr-id.mjs(CC0,零依赖)
node bin/aiqr.mjs id https://aiqr.cc/m/7F92A # 派生
node bin/aiqr.mjs id --verify "0ATA-XJH3-YXYC-PCC5" # 校验 / 修复
node scripts/test-id.mjs # 产出 §8.1 全部数字
node scripts/ocr-confusion.mjs # 产出 §8.3 光学层数字