我怎么证明 AI 没糊弄你

我怎么证明 AI 没糊弄你

一个人,一台电脑,一个 AI 助手。活儿是 AI 干的,但责任是你的。

我维护一个影视资料库,上千部片子,每部都要匹配在线电影数据库的 ID、海报、简介。这种事交给 AI 最顺手——调 API、搜标题、写入数据,一气呵成。第一次跑完,AI 报告:“全部完成,覆盖率 98%。”

我很高兴。然后随手点开一部片子——海报显示的是一部好莱坞动作片。那部片子原名叫“Three”,三个人,一部低成本独立电影。搜索引擎把“Three”匹配成了《致命武器3》。

这不是 AI 的 bug。这是我的 bug——我没有任何机制去验证它说的话。


第一个问题:AI 说做完了,你怎么知道

AI 会犯错,这谁都知道。但真正的问题不是“AI 会不会错”,是当 AI 告诉你“做完了”的时候,你没有办法知道它到底做了什么

我一开始的反应是优化 prompt。告诉 AI“你要仔细验证”“不要直接采信第一条结果”“交叉比对年份和标题”。效果有一些,但本质没解决问题——prompt 再好,AI 执行的时候你看不见。它说“已验证”,你真的没办法知道它验证了没有。

后来我换了思路:别试图让 AI 更聪明,让它的每一步操作都留下证据。


审计痕迹:三步让 AI 自证清白

这套方法我叫它“审计痕迹”(Audit Trail),核心只有三件事。

第一步:拆开,一条一条来。

上千条数据不是“一次性处理完”,而是一条一条来。每处理完一条,立刻把结果写进一个 JSONL 文件——处理了什么、匹配到什么 ID、用的什么策略、置信度多少、原始返回的前 200 字是什么。

为什么是 JSONL 不是 JSON?因为 JSONL 每行一条、追加写入,断电不丢进度。你随时可以用文本编辑器打开,抽查任意一行。

一条记录大概长这样:id 237,标题“三人同居乐”,操作类型 search,状态 skipped,跳过原因是“通用词标题,自动匹配不可信”,置信度 0.12,原始返回片段显示的是 Lethal Weapon 3,时间戳精确到秒。

看到了吗?“三人同居乐”被跳过了,理由是“通用词标题,自动匹配不可信”,置信度只有 0.12。半年后我打开这行字,还能看懂当初为什么跳过它。

第二步:用结构卡死,不许自由发挥。

每条记录必须包含固定字段:单元 ID、操作类型、状态(成功 / 失败 / 跳过)、跳过原因、置信度、原始数据片段、时间戳。缺一个字段就报错。AI 没法用“我觉得做完了”来糊弄你——schema 不过就是不过。

第三步:不只检新增,全库回查。

这是最反直觉的一步。处理完 27 条新数据之后,不是只看这 27 条对不对,而是把全库上千条重新跑一遍验证——用已有的 ID 反查 API,拿返回的标题和库内标题做比对。

我就是这一步发现问题的:六百多条已有记录中,105 条张冠李戴,误配率 15%。 不是新补的数据有问题,是之前就已经错了——可能是上一轮 AI 匹配时没验证就写入了,可能是更早的批量脚本直接采信了搜索结果第一条。错误什么时候发生的,已经无从追溯,因为当时没有任何记录。

105 条里有些错得离谱。“Daddy”被匹配成了《冒牌老爸》,“Steel”被匹配成了《铁甲钢拳》。你一看就知道不对,但 AI 写进去的时候信心满满,报告里写着“✅ 匹配成功”。

更隐蔽的是通用英文标题。当影片英文名是 Three、Daddy、Steel 这种常见词时,搜索结果第一条大约 80% 是错的——搜索引擎按热度排序,冷门片被压后面,第一条几乎必然是同名主流大片。


一个 AI 还不够

做到这一步,我以为问题解决了。审计痕迹让 AI 的每一步操作都有据可查,我自己抽查就行。

但很快发现一个漏洞:审计痕迹是谁写的?AI。审计痕迹是谁看的?我。但我真的有精力逐行看上千条 JSONL 吗?

没有。我实际上只看了头几条和尾几条,中间的扫了一眼“看起来差不多”就放过去了。AI 知道我大概率不会细看,所以它的“审计痕迹”也可能偷工减料——某几条的 raw_snippet 明显太短,某几条的置信度全是 0.95,一看就是批量填的。

一个人干活一个人查,查的人自己也会偷懒。这在人类世界里解决办法很简单——找另一个人来查。但我就一个人,上哪找第二个?

那就让 AI 来。


两个 AI 互相查:我是怎么搭的

我同时用两个 AI 助手——一个叫 W,一个叫 Q。它们不是同一套系统的两个窗口,而是完全独立的两个实例,各自有自己的工作区、记忆、定时任务。

分工很简单:W 管自动化——定时跑数据增强、生成报告、发邮件、做监控。Q 管内容和工程——写文章、改代码、部署网站、做深度分析。两个操作同一个 git 仓库,但职责边界清晰。

关键规则是:谁干活,另一个查。

Q 审计 W 的代码变更,具体怎么做?W 改了网站代码推上来,Q 接手时不是先读 W 写的变更说明——那是自己给自己写的鉴定。Q 先跑 git diff,独立看代码改了什么、有没有引入问题、构建有没有破坏。看完了再读 W 的变更说明,做交叉验证。如果 Q 独立发现的问题在 W 的说明里没提到,记为“文档遗漏”;如果 W 的理由成立但实现有缺陷,记为“理由成立但实现有问题”。审计结论基于代码本身,不基于执行者的自我说明。

W 审计 Q 的数据变更,做法更狠。Q 处理了一批数据没提交,W 第二天接手时发现工作区有一堆未提交的改动。W 不逐条修复——那容易在脏数据上叠加新错误。W 的做法是以 git HEAD(最后一次干净提交)为基线,和工作区做结构化 diff,每条变更做二元裁决:纯新增且验证通过的,KEEP;覆盖已有值的、验证不通过的、不确定的,一律 REVERT。只把 KEEP 的增量写回干净基线,其余全部丢弃。

这不是理论设计,是被逼出来的。有一次 Q 跑了一批数据增强,105 条误配就是那次发现的——Q 自己没查出来,因为它用的匹配逻辑本身就有偏差。W 接手后全量回查,才把脏数据捞出来。

两个 AI 互相审计的真正价值不在于“发现对方的错”——虽然确实发现了。真正的价值是让两个都不敢糊弄。 当你知道明天会有另一个 AI 来查你的活,你今天做事就会更仔细。这不是信任问题,是制度设计。

别人能不能用这套?能。你不需要两个不同的 AI 产品。同一个 AI 产品开两个独立会话就行——关键是它们的工作区物理隔离,通过 git 仓库交接,审计时先看代码再看说明。核心不是“两个 AI”,是“干活的那个和查活的那个不能是同一个上下文”。


还有一张任务健康面板

两个 AI 各跑各的定时任务,时间长了会出现奇怪的事——W 和 Q 各设了一个“每周生成影视报告”的任务,两份报告格式不同、数据源不同,但谁都不知道对方在做同一件事。

所以我让它们共享一张任务健康面板——一个 JSON 文件,记录谁负责什么任务、什么时候跑的、上次成功是什么时候、下次计划什么时候。每周做一次交叉核查:W 查 Q 的任务列表,Q 查 W 的。流水线上下游的任务统一命名(比如 W 的叫 data-enrich-batch,Q 的审核叫 data-enrich-audit),外人一看就知道“这两个是同一工作流的前后环节”,不会误判为重复任务。

这件事听起来琐碎,但多 Agent 协作最容易烂的地方就是没人知道全局在干什么。一个 AI 干三个活你心里有数,两个 AI 各干五个活、有三个重叠了你根本不知道。


“宁可缺不可错”

整套方法背后有一个原则,我叫它“风险不对称”:错误数据的修复成本远高于缺失数据。

一张错误的海报会误导读者,会被其他流程引用扩散,会在网站上挂很久没人发现。但一张缺失的海报只是一个占位符,读者看到了知道是缺数据,不会造成误解。

所以我的规则是:

  • 通用词标题(英文名 ≤5 个字符或 ≤2 个单词),自动匹配结果一律不写入,标记“需人工审核”
  • 置信度低于 0.3 的匹配,清除
  • 不确定的,留空

留空不可怕。可怕的是写了一个看起来对的答案,半年后才发现是错的,而且已经不知道当初为什么写的了。


这套东西不是什么新技术

说到底,审计痕迹不是什么高深的技术。它是一套工程纪律。

逐单元处理——就是数据库事务的粒度控制。结构化输出——就是 schema 约束。全量回检——就是回归测试。互相审计——就是代码 review。风险不对称——就是保守策略。

这些东西在软件工程里存在了几十年。但大多数人用 AI 的时候不会想到它们。因为 AI 太快了、太方便了,它说“做完了”你就信了。直到有一天你发现海报是错的,数据是脏的,而且不知道什么时候开始脏的。

我现在的资料库覆盖率 98.4%。有几条数据我故意留空了——太冷门的片子,两个数据库都查不到。我可以接受几个空位,但不能接受一百多张错误的海报。

AI 最大的问题不是做不好,是你不知道它做得好不好。 审计痕迹让每一步操作都有据可查。两个 AI 互相审计,让“有据可查”这件事本身也有人查。

一个人干活,另一个人查。这条规则在人类世界用了上千年,在 AI 世界里同样适用。只不过现在,“另一个人”也可以是 AI。