AI Agent 说「已完成」,凭什么信——ChipCode 的证据链与产物门禁设计

简介: ChipCode 提出“可信完成”新范式:拒绝轻信AI自述,通过append-only证据账本、文件驱动的阶段状态计算、进程层门禁(而非语言层断言)三重设计,将“已完成”锚定在可验证、可追溯、不可篡改的工程事实中。

AI Agent 说「已完成」,凭什么信——ChipCode 的证据链与产物门禁设计

摘要:大模型写代码的能力早已不是瓶颈,瓶颈是「它说完成了,你凭什么信」。这篇讲我们在做 ChipCode 时反复撞上的三类「已完成」,模型自述不可信的机制性原因,以及最后落到产品里的三个设计:append-only 的证据账本、由磁盘文件算出的阶段状态、把验收压到进程层的门禁。文末附可直接跑的校验脚本,以及这套设计在哪些场景下不该用。


一、三种「已完成」

做 AI 辅助开发这两年,我们见过三种「已完成」。

第一种,Agent 在对话里回:「已修复,测试通过。」
你去跑,编译报错。

第二种,它确实执行了命令,但执行的是 echo "ok",或者一个早就通过的旧测试。工具调用记录里躺着一条成功的返回,结论是真的,但它没证明任何事。

第三种,它确实编译了、确实测了、功能也确实对。但两周后你想确认「这个模块到底验过什么」,发现证据只存在于那段已经翻不到底的会话里。

第一种是幻觉,第二种是自欺,第三种是工程债。三种指向同一个根因:我们把「状态」的判定权,交给了一个只会输出文字的东西。

这三种情况不是假设。ChipCode 早期版本里,AI 改完代码会在对话里说「已验证」,我们真的照着它说的去跑,失败的次数多到不得不记下来。后来统计了一下,问题不集中在「代码写得不对」——大部分时候代码是对的,出问题的是我们对「已经验证」这件事的理解。


二、模型的自述为什么不能当证据

不是「模型爱撒谎」这么简单。机制上至少有三个来源。

一是乐观偏差。 训练语料里,「我完成了」后面跟着的往往是成功案例。模型学到的是「完成」这个语域的形态和句式,而不是「完成」的实际条件。它很擅长生成一段看起来像验收报告的文字,因为那段文字在语料里大量存在。

二是抽样偏差。 它跑了 10% 的用例,写出覆盖 100% 的结论。你问「测了吗」,它答「测了」——两个「测」不是同一个意思。这不完全是编造,是它把「我执行了验证动作」和「我验证了全部条件」当成了同一件事。

三是状态遗忘。 上下文压缩、会话切换、跨轮工具输出裁剪,都会让「刚才验证过」这个事实从窗口里消失。但结论留在对话里,看起来仍然成立。于是同一个 Agent 在第三轮会自信地引用一个它已经记不清的验证结果。

这三条不是靠 prompt 能解决的。「请务必真实验证」「不要谎报通过」这类指令,能改变措辞,改变不了判定依据。要动的是判定依据本身。


三、可信度的三个层次

把「证据」按来源分三层,差别立刻清楚:

层次 证据形态 谁能让它成立 可伪造性
语言层 「我已经测试通过了」 模型自己 完全可伪造
工具层 工具返回 status: success 工具的实现方 可伪造,因为返回码是自己定的
进程层 编译器 exit code、标准输出、串口日志、Flash 占用 一个不关心你结论的外部程序 除非它真的通过了,否则伪造不了

关键差别在最后一列。语言层的结论由被验证者给出;进程层的结论由不关心结论的程序给出。

所以设计原则只有一句:能落到进程层的,绝不停留在语言层。

这句话听起来像废话,但大部分 AI 编程工具停在了前两层。它们把工具调用成功当成验证完成——而 bash 返回 0 只说明命令跑完了,不说明命令跑对了。

ChipCode 的验证设计都是围绕「把结论压到第三层」展开的。下面三个部分分别对应:证据怎么记、状态怎么算、人在哪里介入。


四、证据账本:从「我做了」到「记录了什么」

光有进程层输出还不够。输出会滚掉,会话会丢。得让它落盘,并且能被机器读。

ChipCode 用的是 append-only 的 JSONL,项目根目录下的 evidence.jsonl,一行一条,永不修改:

{
   "id":"ev-0007","at":"2026-11-02T13:20:00Z","claim":"水泵启停 50 次无卡死","result":"pass","covers":["c1","c3"],"by":"ai"}

四个字段是设计核心。

result 三态,不是布尔。
pass / fail / inconclusive。绝大多数系统只记成功失败,结果就是「没测」和「测失败」混成同一个值。硬件联调里这两者差别极大:示波器没接上,和测出纹波超标,是完全不同的两件事,后续动作也完全不同。三态让「未验证」有个地方待着,而不是被迫挤进失败。

covers 显式映射到验收条件。
不是「我测了」,而是「我测的这件事,覆盖了 c1、c3,没覆盖 c2」。条件来自需求阶段的 spec.json。没有这层映射,证据是孤立的,你只知道「有东西被验证过」;有了这层映射,你才能回答「还差什么」。

does_not_cover 写清楚没证明什么。
这条最反直觉,也最有用。一份只写「证明了什么」的报告,读起来永远像是完成了;把缺口写出来,它立刻显形。ChipCode 把「未证明」单独列成看板上的一栏,而不是藏在报告的脚注里。

撤销靠追加,不靠删除。

{
   "id":"ev-0008","invalidates":"ev-0007","reason":"换了传感器"}

改主意是正常的。但删除会让「曾经认为通过」这段历史消失,下一次有人问「为什么换传感器」,账本里没有答案。追加一条失效记录,既撤销了结论,也保留了因果。

这条规则有个容易忽略的连锁反应:撤销一条证据,它当初覆盖的所有条件会全部回退成未证明。 换传感器之后,原来那条「启停 50 次」的证据失效,c1 和 c3 一起退回。这不是在惩罚改主意,而是让「改主意」本身也留下代价——否则会出现「结论还在、依据已经没了」的账本。

为什么是 JSONL 而不是数据库

  • git diff 能逐行看出哪条证据是新加的,评审时不需要额外工具
  • 并发追加不冲突,多个 Agent 各写各的,不会互相覆盖
  • 一行一个 json.loads() 就能消费,CI 脚本不用装依赖
  • 写入失败只影响最后一行,不会像 JSON 数组那样整体损坏

代价也要说清楚:没有索引、没有事务、没有并发控制。几千条以内够用,上万条就该换方案了。

一个能直接跑的校验脚本

账本写出来是给人看的,但真正拦住问题的是机器读它。下面这个脚本检查「验收条件是否被 pass 证据完整覆盖」,可以直接挂进 CI 或 pre-commit:

#!/usr/bin/env python3
"""校验证据账本是否覆盖了全部验收条件。

用法:python check_evidence.py evidence.jsonl c1 c2 c3
退出码:0 = 全覆盖,1 = 存在未证明的条件,2 = 参数错误
"""
import json
import sys
from pathlib import Path

if len(sys.argv) < 3:
    print(__doc__)
    sys.exit(2)

path, required = Path(sys.argv[1]), set(sys.argv[2:])
records, invalidated = [], set()

for lineno, line in enumerate(path.read_text(encoding="utf-8").splitlines(), 1):
    if not line.strip():
        continue
    try:
        ev = json.loads(line)
    except json.JSONDecodeError as e:
        print(f"[账本损坏] 第 {lineno} 行不是合法 JSON:{e}")
        sys.exit(1)
    if "invalidates" in ev:
        invalidated.add(ev["invalidates"])
    else:
        records.append(ev)

covered = set()
for ev in records:
    if ev["id"] in invalidated:
        continue
    if ev.get("result") == "pass":      # fail / inconclusive 都不算覆盖
        covered.update(ev.get("covers", []))

missing = sorted(required - covered)
print(f"验收条件 {len(required)} 条,已覆盖 {len(required) - len(missing)} 条,"
      f"未证明 {len(missing)} 条")
if missing:
    print("未证明:" + ", ".join(missing))
    sys.exit(1)

注意脚本里 invalidates 的处理顺序:先收集失效集合,再遍历有效记录。这个顺序不能反,否则被撤销的证据仍然会贡献覆盖。这类细节是账本方案唯一容易写错的地方。


五、产物门禁:状态由文件算出,不由 Agent 宣布

有账本,还得有个东西去读它,并且拒绝让状态被口头推进。

ChipCode 里的「产品工厂」把做一款硬件拆成五步:创意、造型、硬件、软件、联调。每一步的产出写进项目里固定的文件:

阶段 落盘产物
创意 spec.json(条件与确认状态)、spec.md
造型 design/shape.json(dims / model / mesh / views[])
硬件 bom.json、constraints.json
软件 firmware/
联调 evidence.jsonl

关键在于:阶段状态不是存储的字段,而是根据这些文件算出来的纯函数。 同样的文件树,算出来永远是同一个状态。

实际生效的判定规则大概是这样:

磁盘上的情况 看板显示
验收条件未确认 停在「进行中」
shape.json 里写到的模型 / 网格 / 视图文件不存在 「阻塞」
有 2D 视图但没有 3D 网格(或反过来) 停在「进行中」
bom.json 里有一颗器件没有来源链接 「阻塞」
有效证据里存在 fail 「阻塞」

这套写法有三个直接后果:

Agent 无法通过「说」改变状态。 它只能写文件,而文件是能被检查的。想推进阶段,就得真的把产物写出来,把证据录进去。

状态可复现。 换台机器、重开项目、交给别人,算出来的结论一样。不依赖会话、不依赖缓存。

评审有了共同标的。 讨论「这个模块做完了吗」时,双方看的是同一棵树,而不是各自记忆里的对话。

「阻塞」不等于「出错」

这是我们在实现里最坚持的一点:门禁要把「阻塞」和「出错」区分开。

  • 出错 = 有东西坏了,要去修
  • 阻塞 = 有东西没落地,要去补

两者的心智完全不同,后续动作也不同。把它们混成同一个红色感叹号,人会很快对这个颜色脱敏,然后开始习惯性忽略——门禁就失效了。

所以 ChipCode 的「阻塞」必须写清楚缺什么:缺 3D 网格、缺器件来源链接、有一条证据 fail 且没被撤销。而不是一个笼统的警告图标。

其中一个细节值得单说:造型阶段要求三件套齐全——3D 源码(.scad / .step)、3D 网格(.stl / .obj / .3mf)、2D 视图。只写 .scad 不导网格,看板只能画线框,造型会一直停在「进行中」。

这个规则被人问过「是不是太严」。我们的判断是:结构图应该是能转着看的,不该靠用户在对话里提醒 AI「你还没导出网格」。门禁的价值就在于它不接受解释。


六、让人只出现在该出现的地方

门禁跑起来之后有个副作用:人被迫参与的次数变多了。每个阻塞点都在等人。所以必须收敛决策点。

ChipCode 把人的介入压到三类,其余全部自动:

  1. 确认验收条件——做什么算完成
  2. 确认关键选型与结构——用什么做
  3. 给物理结果——示波器、实机现象、手感

其余环节 Agent 自己推进。这样人不是「每一步都审批」,而是「在最贵的地方拍板」。第 1 条和第 3 条尤其不该外包:前者决定了后面所有的验证有没有意义,后者是唯一无法由进程给出的证据。

具体到交互上,AI 只提案,不替人确认。验收条件、结构、选型写进文件后都是未确认状态,确认按钮在人手上。这条约束看起来很笨——多一步操作,少一点流畅——但它把「谁批准了这件事」变成了可追溯的记录,而不是一句「我问过他了」。

配套的一条原则:失败必须可见。

工具调用失败,界面上要标红并写明原因——编译失败、插件报错、操作被拒绝,这些是不同的失败,不能折叠成一句「遇到一些问题」。Agent 太容易把失败叙述成「部分完成」,而「部分完成」是最危险的中间态:它听起来像成功。

ChipCode 里 AI 改完代码会跑一次只读的独立验证,结论是 PASS / FAIL / PARTIAL 三档;子代理与独立验证各用独立卡片,能看出是谁下的结论。命令行版则把每步事件写成一行 JSON,下游只认 failed 字段,不解析自然语言:

{
   "seq":3,"type":"tool.step","name":"compile","status":"completed","failed":true,"stderr":"main.c:12: error: ..."}

还有一条容易被忽略的:AI 说「通过」和流水线认为「通过」,必须指向同一个事实。 所以退出码要语义化,把「真失败」和「没人确认」分开——失败是 1,用法错误是 2,非交互环境下需要人工确认是 4。混成一个非零码,CI 就没法自动判断该重试还是该叫人。


七、这套设计的代价

写到这里该说不好的部分。这套设计有成本,而且不低:

多写一批文件。 每个阶段都有产物,项目里会多出 spec、bom、evidence 这些「不是代码的代码」。对一个人的小玩具项目,这是纯负担——为了管住 200 行代码,先写 4 个 JSON,不划算。

门禁会拦人。 有东西没落地就走不下去,这在探索阶段特别烦。探索本来就不该有验收条件,硬套只会让人学会绕过门禁。

JSONL 不是数据库。 前面说过,没有索引和事务。要查询、要并发、要权限,就该换正经存储,而不是给 JSONL 打补丁。

它只适合有明确交付物的任务。 「做个自动浇花器」能这么管,「帮我看看这段代码为什么慢」不能——后者没有可验收的条件,硬凑出来的「验收条件」只是形式主义。

覆盖面有边界。 产品工厂只管到固件与结构,不做 PCB 设计,也不管打样和采购。越界的部分我们没做,也不打算假装做了。

所以我们的判断是:任务越接近交付,这套越值;越接近探索,越不该用。 中间地带靠人判断,没有通用答案。这也是为什么 ChipCode 里保留了普通代码项目类型——不是所有活儿都值得走五步。


八、三条能搬走的原则

不管用不用 ChipCode,这三条可以搬到任何 AI 编程流程里:

一、状态外置。
状态写在文件里,界面是文件的函数。Agent 改不了状态,只能改文件——而文件可被审查、可被 diff、可被回滚。

二、验证下沉。
能拿到 exit code 就别看自然语言。能跑就别「说」,能编译就别「看起来没问题」。可信度是有层级的,往下沉一层,可信度就多一分。

三、失败留痕。
三态比布尔多一个「不知道」;append-only 比可编辑多一段历史;写「没证明什么」比写「证明了什么」更能防止自欺。


AI 写代码这件事,早就不是「写得对不对」的问题了。真正难的是:当它说「好了」的时候,你的工程里有没有一个东西,能替你回答「好在哪」。

如果没有,那它说的每一个「已完成」,你都只能选择相信。


作者为 ChipCode 开发团队成员。文中代码示例为通用示意,可直接移植到任意语言栈。

相关文章
|
7天前
|
JavaScript API iOS开发
DeepSeek Harness下载、安装、配置一篇搞定(2026整理,送常用插件)
DeepSeek Harness(DSH)是DeepSeek于2026年开源的MIT协议AI Agent框架,支持插件化组装模型、工具、沙箱与UI,兼容主流大模型。本文提供免Node.js的桌面端安装包(Win/macOS),含详细图文教程及热门插件推荐。
3453 33
|
7天前
|
Linux 开发工具 iOS开发
Codex保姆级教程(下载+安装+登录+使用,含所有版本,2026最新)
Codex是OpenAI推出的开源AI编程智能体,本地运行,可理解整个代码库,支持自然语言指令改代码、执行命令、跑测试、修Bug,兼容Win/macOS/Linux。(239字)
|
2天前
|
云安全 人工智能 安全
|
8天前
|
iOS开发 Docker MacOS
【2026实测】Docker Desktop下载、安装、汉化、配置教程(附官网安装包)
Docker Desktop 是 Docker 官方推出的桌面容器管理工具,集成引擎、CLI、图形界面与 Kubernetes,支持 Win/macOS/Linux。2026 最新版 4.88.1 强化 ARM 原生运行、镜像加速及资源可视化,附中文汉化与国内镜像配置指南。
1534 5
|
云安全 安全 网络安全
|
8天前
|
安全 算法 定位技术
《高性价比人生指南》完整原版:普通人用最少的资源,过好这一生的底层逻辑~How To Live Better
📖《高性价比人生指南》是一份基于实证、拒绝焦虑的生存手册,倡导以“人生时薪”为视角,重新计算时间、金钱与精力的成本与收益。它不承诺逆袭,只教人算清账:省下的钱若耗掉时间便亏;买体验比买物品更值;睡眠是最高杠杆的健康投资;关系贵在情绪价值而非表面热络;学习重在改变行为而非自我安慰。核心理念:用低成本、高回报的微小行动,换取长期的选择权——不是成为谁,而是掌控自己人生的主动权。
|
21天前
|
人工智能 API 开发工具
DeepSeek Harness 神插件:白嫖 13 家大厂模型
`dsh-our-free-model` 是 DeepSeek Harness 神级插件:零配置、零API Key、零账号,聚合13家大厂免费模型(含DeepSeek V4.1 Flash、Kimi K3等),支持本地网关、用量看板与自动降级,真正实现“白嫖即用”。
3699 54
|
13天前
|
人工智能 JSON Linux
【保姆级】ComfyUI部署教程(下载+安装+配置+工作流,2026最新)
ComfyUI 是一款开源节点式AI生成工具,支持图像、视频、音频及3D内容创作。通过拖拽连线组合模型加载、提示词、采样等节点,构建可复用、易修改的工作流,兼容SD1.5、SDXL、Flux、Qwen Image等主流模型。