AI向善头像

SQLite适配验证踩坑:12条清单落库实测

配图

上周我把一份端侧智能体的提交声明丢给自己写的校验脚本,跑完 12 条判据,屏幕上跳出 verdict=BLOCK。缺的只是电子签名一个字段,另外一条高危权限没写用途说明。其实这份声明人工抽查时看着挺齐,表格每行都填了字,机器一读就把两处漏洞摆到台面上。

我把脚本连跑三遍,16 项自检全绿之后,才把结果写进下面的表格。文中每个数字都能用第五节的命令复现,没跑出来的数我一个也没写。

本文是实操文,随文脚本 agent_compliance_probe.py(纯标准库,只用 json 与 sqlite3),零外部依赖。它把文件里的文字要求翻译成可执行判据,不是法律意见,条款以官方原文为准。

一、为什么把要求写成可跑的清单

配图

11 月 9 日的预研包把工信厅信管〔2026〕42 号列进 TOP3,这份 10 月 24 日印发施行的文件,给预置智能体的手机、平板、可穿戴设备进网加了几道前置动作:智能体适配验证、涉诈风险安全评估、反诈能力前置性评测;OTA 现网升级要提交方案与报告并经专家评估;应用分发平台要公示适配验证列表;开发者要电子签名声明是否同意智能体调用(来源:本仓 output/gen/2026-11-09/daily_brief.md 第 ① 节 TOP3 摘述,经 8 路 WebSearch 交叉验证)。

同一周 OpenRouter 周榜是 210万亿 Token 量级,人工逐份翻表格,翻到第三十份就会凭印象打勾(来源:每日经济新闻据 OpenRouter 测算,经预研包交叉验证)。

我做的不是逐条解读政策,而是把这些要求翻译成 12 个能被机器判真假的检查项。比如「电子签名声明」落成签名长度与签名主体比对,「适配验证列表」落成列表长度与逐项证据检查;再例如「OTA 升级方案」落成非空判断与专家评估批准位。这样一份声明交上来,机器给出的是可归档的三态结果,不是一句「看起来没问题」。

二、声明文件长什么样,台账存什么

配图

输入是一个 JSON 声明文件,工程上可以把它当成提交包里的 agent_declaration.json。演示用的字段长这样:

{
  "app_id": "com.example.assistant",
  "version": "3.2.0",
  "agent_declared": true,
  "consent": "granted",
  "signature": "MEUCIQD7x9f2k0aB4n6v8c1e3g5",
  "signer": "example-dev",
  "developer": "example-dev",
  "signed_at": "2026-11-09T02:10:00+08:00",
  "ota_plan": {"channel": "stable", "gray_percent": 5},
  "permissions": [{"name": "READ_SMS", "justification": "验证码自动填充"}]
}

这里的灰度 5% 是脚本里的示例值,不是任何厂商的实际配置(来源:随文脚本 good_manifest())。

台账用 SQLite 存两张表:submissions 一行代表一份声明的一次归档,主键是内容哈希;checks 一行代表一条判据的结果。拆成两张表,是为了让「整份声明的结论」与「逐条判据的明细」都能被单独复核。

三、12 条判据逐条对应

配图

编号检查项对应的要求
C01预置智能体声明为真声明是否预置智能体
C02调用同意等于 granted电子签名声明是否同意调用
C03签名字符串长度达标签名非空、可追溯
C04签名主体等于开发者签名责任可归属
C05时间戳可解析签名时间可核验
C06验证列表不少于 3 项适配验证列表存在
C07每项带结果与证据验证结果可复核
C08涉诈评估有风险项涉诈风险安全评估
C09前置评测通过且有报告反诈能力前置性评测
C10OTA 升级方案非空现网升级提交方案
C11专家评估已批准升级经专家评估
C12高危权限带用途说明权限声明可解释

每条结果是 PASS、FAIL、UNKNOWN 三态之一。字段没交,记 UNKNOWN,不记 PASS,也不记 0。整份声明只在 12 条全为 PASS 时给 verdict=PASS,其余情况一律 BLOCK。

四、核心代码:三态判据与幂等归档

配图

下面这段是脚本的骨架,可以直接复制成 probe.py 运行。完整版(含 12 条判据与 16 项自检)已落盘在同目录 agent_compliance_probe.py。

import hashlib, json, sqlite3
from datetime import datetime, timezone

MISSING = object()                      # 缺失哨兵,不用 0 或空串

def field(d, k):
    return d.get(k, MISSING)

def judge(d, key, ok):
    v = field(d, key)
    return "UNKNOWN" if v is MISSING else ("PASS" if ok(v) else "FAIL")

def canonical_hash(d):
    raw = json.dumps(d, sort_keys=True, ensure_ascii=False,
                     separators=(",", ":"))
    return hashlib.sha256(raw.encode()).hexdigest()

def archive(conn, m, results):
    h = canonical_hash(m)
    n_pass = sum(r["status"] == "PASS" for r in results)
    verdict = "PASS" if n_pass == len(results) else "BLOCK"
    with conn:                          # 事务包住两张表,避免半截台账
        conn.execute("INSERT OR IGNORE INTO submissions VALUES (?,?,?,?,?)",
                     (h, m.get("app_id", ""), m.get("version", ""),
                      datetime.now(timezone.utc).isoformat(), verdict))
        conn.executemany(
            "INSERT OR REPLACE INTO checks VALUES (?,?,?,?)",
            [(h, r["code"], r["status"], r["detail"]) for r in results])
    return h, verdict

judge() 把三态收在一处,canonical_hash() 用 sort_keys=True 保证键顺序变化不产生新版本,archive() 用 INSERT OR IGNORE 保证同一份声明复验不新增行。

五、跑起来:三条命令与实测数字

配图

python agent_compliance_probe.py --selftest
python agent_compliance_probe.py --demo
python agent_compliance_probe.py --demo --db ./agent_compliance.db
python agent_compliance_probe.py --verify agent_declaration.json --db ./agent_compliance.db

--selftest 输出 16 项检查全绿,含 9 个正控与 7 个负控。负控专门盯着「缺失被当成通过」这类假阳:N1 缺签名不是 PASS,是 UNKNOWN、N2 denied 不能靠 truthy 蒙混过关、N5 空声明 12 条全不是 PASS。

--demo 故意删掉签名字段、把 READ_SMS 的用途说明留空,输出是:PASS 10 / FAIL 1 / UNKNOWN 1 / verdict BLOCK。10 条 PASS 占 83.3%,那 1 条 UNKNOWN 占 8.3%,但结论照样是拦下(来源:本地脚本 --demo 实测输出,2026-11-09)。

--demo --db 会在同目录生成 agent_compliance.db,本机实测 20480 字节,内容哈希前 12 位是 35af06cdef56。

复验同一份声明两次,submissions 仍是 1 行、checks 仍是 12 行,重复写入 0%(来源:--selftest P3 与 --demo --db 落盘产物)。

六、工程取舍:为什么用 SQLite 落台账

其一,单文件可归档。审核方要的是一份能随 OTA 报告一起提交的附件,.db 文件拷走即可,不要求对方装数据库服务。

其二,约束交给数据库。PRIMARY KEY (manifest_hash) 与 INSERT OR IGNORE 直接把幂等写进存储层,比在应用层维护一个「已处理集合」可靠。

其三,事务天然够用。with conn: 把两张表的写入包成一个原子操作,中途抛异常不会留下「有汇总没明细」的半截台账。

其四,离线可跑。sqlite3 是标准库,断网也能复验。

不过 SQLite 也有边界:多节点并发写入时写锁会顶住,到了分发平台那种吞吐,需要警惕把本地台账当权威结论,换 PostgreSQL 更合适。

七、踩坑记录:五处都是真跑出来的

坑一,or "" 把缺失抹平。 早版写 sig = d.get("signature") or "",缺字段和交空串变成一个值,后续长度判断直接放行。改成 MISSING 哨兵后才分得开。

坑二,truthy 判断把拒绝当同意。 if d.get("consent") 遇到字符串 "denied" 照样为真。布尔判断要显式写成 == "granted",这条被 N2 负控锁住。

坑三,键顺序抖动造出重复归档行。 直接用 json.dumps(d) 求哈希,同一个对象换一次插入顺序就是新哈希。sort_keys=True 之后,P5 键顺序不影响内容哈希 才转绿。

坑四,拿时间戳当主键。 早版用 datetime.now() 做去重键,同一份声明复验一次就多一行。换成内容哈希,submissions 才稳在 1 行。

坑五,批量写入半途失败。 executemany 不在事务里,异常会留下 12 条里只写了 5 条的台账。用 with conn: 包住后,要么全进要么全退。

八、换成真实系统的替换点

演示脚本做的是静态声明校验,接真实系统要换三处。

把 evaluate() 里的签名长度检查换成公钥验签,把 C09 的报告存在性换成报告哈希与实验室回执比对。--verify 指向提交包里真实的声明文件,别再读演示字典。--db 指向共享台账目录,并在 CI 里加一步:构建产物打完包就跑一次 --verify,verdict=BLOCK 直接让流水线失败。

把「进网前才发现漏材料」提前到打包时刻,返工成本差着量级。

九、边界:清单能挡住什么,挡不住什么

清单能挡住的是漏项、格式错、签名主体不一致这类硬伤。它挡不住伪造的证据,字段齐了也不代表反诈能力真的达标。另一个角度是规则本身会随实施细则更新,所以 version 字段和内容哈希都要留痕,方便日后回答「当时按哪一版判的」。

我把 UNKNOWN 留成独立状态,没并进 FAIL。「没交」和「交了但不合格」要走的补件流程不一样,混成一类后续对账就分不清。

留三个问题给你:

  • 你会把「字段缺失」判成 UNKNOWN,还是直接判 FAIL?
  • 适配验证列表该由开发者自报,还是由分发平台逐项复验?
  • OTA 专家评估意见,台账里该存哈希还是存全文?

但缺一项,就该拦在进网之前。

别把缺失当成通过。

数据与事件来源

  • 工信厅信管〔2026〕42 号(2026-10-24 印发施行)要点:转引自本仓 output/gen/2026-11-09/daily_brief.md 第 ① 节 TOP3 摘述,经预研包 8 路 WebSearch 交叉验证;本文只做工程映射,条款以官方原文为准。
  • 12 条判据、16/16 自检、PASS 10 / FAIL 1 / UNKNOWN 1、83.3% 与 8.3% 占比、内容哈希 35af06cdef56、复验幂等结果:来源:本地脚本 agent_compliance_probe.py --selftest 与 --demo 实测输出(2026-11-09)。
  • 台账 agent_compliance.db(20480 字节):来源:--demo --db 落盘产物,可用 sqlite3 命令行直接打开复核。
  • 参考来源:210万亿 Token 周调用量背景,每日经济新闻据 OpenRouter 测算,经预研包交叉验证。

这份清单我会随细则更新继续改,欢迎在评论区聊聊你踩过的适配验证坑,顺手收藏备用。

转载自 CSDN-专业IT技术社区

原文链接:https://blog.csdn.net/superdangbo/article/details/170359583

文章来源转载

评论

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--