AI 写嵌入式代码,编译通过不等于做完——拆解 ChipCode 产品工厂的文件状态机与 evidence.jsonl 证据链

简介: 大模型写 C 代码早就不是问题了。真正卡住嵌入式 AI 开发的是另外三件事:需求没法验收、"已验证"是模型自己说的、项目状态只存在聊天记录里。本文以 ChipCode(原 TrieCode)的「产品工厂」为样本,拆解它如何用**外置到磁盘的文件状态机**、**只追加的证据账本 `evidence.jsonl`**、**以编译器退出码为准的验证门禁**,把「一句需求 → 需求条件 → 结构 → BOM → 固件 → 联调 → 产品包」串成一条可审计的链路。文末附可运行的证据覆盖率校验脚本、四条工具链的实际编译路径,以及这套方案当前的真实边界。

AI 写嵌入式代码,编译通过不等于做完——拆解 ChipCode 产品工厂的文件状态机与 evidence.jsonl 证据链

摘要:大模型写 C 代码早就不是问题了。真正卡住嵌入式 AI 开发的是另外三件事:需求没法验收、"已验证"是模型自己说的、项目状态只存在聊天记录里。本文以 ChipCode(原 TrieCode)的「产品工厂」为样本,拆解它如何用外置到磁盘的文件状态机、只追加的证据账本 evidence.jsonl、以编译器退出码为准的验证门禁,把「一句需求 → 需求条件 → 结构 → BOM → 固件 → 联调 → 产品包」串成一条可审计的链路。文末附可运行的证据覆盖率校验脚本、四条工具链的实际编译路径,以及这套方案当前的真实边界。

一、问题从来不是「AI 会不会写 C 代码」

用 AI 辅助做单片机项目,大部分人经历过同一个剧本:

在对话框里敲一句「做一个自动浇花器,土干了自动抽水,手机能看湿度」,几秒钟后拿到一份 main.c,看起来有模有样。然后呢?

断点 具体表现 后果
需求不可验收 「手机能看湿度」是自然语言,没有判定条件 无法回答「到底算不算做完了」,只能靠感觉
验证靠模型自述 AI 回一句「已编译通过、已测试」 可能根本没跑编译器,或只跑了条无关命令
状态在会话里 选了哪颗传感器、引脚怎么分、验收条件是什么,全在聊天记录 换个会话 / 换台电脑 / 换个人接手,上下文归零
工具链割裂 Arduino、ESP-IDF、STM32Cube、SDCC-51 四套命令、四种工程结构 生成的代码落不进一个能编译的工程

前两条是可信度问题,后两条是工程化问题。给 IDE 挂个大模型只能解决「写代码」这一段,解决不了这四条。

ChipCode 的做法有点反直觉:它没有继续在「让 AI 写得更准」上堆 prompt,而是把项目状态从对话里搬到了磁盘文件上,让 AI 只能写文件、不能自己宣布状态。下面拆开看。

说明:本文由 ChipCode 开发团队撰写,文中产品为团队自研。所引字段、命令与规则均来自公开官方文档与开源仓库,未做实机验证,已逐处标注来源与示意内容。


二、把状态搬到磁盘:五步文件映射

「产品工厂」把做一款嵌入式产品拆成五步,每一步的产出写进项目里固定的文件:

阶段 做什么 落盘产物
创意 把想法收敛成可验收的条件 spec.json(条件与确认状态)、spec.md(需求正文)
造型 外形尺寸、3D 模型、2D 视图 design/shape.json(dims / model / mesh / views[])、design/、design/views/
硬件 选型,引脚 / 总线 / 电源约束 bom.json、constraints.json
软件 写固件 firmware/
联调 跑通,并把结果记下来 evidence.jsonl

关键在阶段状态是从磁盘文件算出来的,不是 AI 说了算。官方文档给出的判定规则:

验收条件没确认                              → 创意停在「进行中」
shape.json 里写的模型/网格/视图文件不存在     → 造型「阻塞」
有视图但没 3D 网格(或反过来)               → 造型停在「进行中」
bom.json 里有一颗器件没有来源链接            → 硬件「阻塞」
任何一条有效证据的结果是 fail                → 联调「阻塞」

文档里特别解释了一句:「阻塞的意思是有东西没落地,不是出错。」

整个流程是这样:

一句话需求
   │
   ▼
[创意]  spec.json / spec.md          ← 条件未确认 → 进行中
   │  ✅ 人工确认验收条件
   ▼
[造型]  design/shape.json + model + mesh + views[]   ← 缺 mesh → 阻塞
   │  ✅ 人工确认结构
   ▼
[硬件]  bom.json / constraints.json  ← 器件缺来源链接 → 阻塞
   │  ✅ 人工确认选型
   ▼
[软件]  firmware/                    ← 编译通过才计入证据
   ▼
[联调]  evidence.jsonl               ← 有效证据含 fail → 阻塞
   │  ✅ 人工给物理结果
   ▼
hardware-package/  +  .manifest.json

这本质是一个以文件为唯一真相源(single source of truth)的有限状态机:AI 的产出是文件,看板是文件的纯函数,人只在三处拍板——确认验收条件、确认结构与选型、给物理结果。文档原话:「AI 只提案,不替你确认。」

两个设计细节值得单独拎出来:

  • 造型必须凑齐三件套:3D 源码(model,.scad / .step)、3D 网格(mesh,.stl / .obj / .3mf)、2D 视图(views[])。只写 .scad 不导网格,看板只能画线框、造型会一直停在「进行中」。文档说这是有意的:「结构图应该是能转着看的,不该靠用户在对话里提醒。」
  • 选型不许编型号:bom.json 每颗器件必须附来源链接(数据手册 / 官网 / 商城),查不到时 AI 会问人,不会自己造一个型号出来。对做过 BOM 的人,这条比什么花哨功能都实在。

三、evidence.jsonl:只追加的证据账本

这是整套设计里我最认可的一块。

evidence.jsonl 一行一条,只追加、不修改。官方文档给的例子:

{
   "id":"ev-0007","at":"2026-11-02T13:20:00Z","stage":"integration","claim":"水泵启停 50 次无卡死","result":"pass","covers":["c1","c3"],"by":"ai"}
字段 含义
id / at 证据编号、时间戳
stage 所处阶段
claim 这条证据声称验证了什么
result pass / fail / inconclusive,只有 pass 才算覆盖
covers 覆盖了 spec.json 里的哪几条验收条件
does_not_cover 写明没证明到什么,会进看板的「未证明」清单
by 谁给出的证据

三条规则把「AI 自称通过」这条路堵死了:

  1. result 三态,而不是布尔值。「没能验证」(inconclusive)不等于通过,也不等于失败——这一条对硬件联调特别关键,示波器没接上和测出波形是两回事。
  2. covers 把证据和需求条件显式关联。不是「我测了」,而是「我测的这件事,覆盖了 c1、c3,没覆盖 c2」。
  3. 撤销靠追加,不靠删除:
{
   "id":"ev-0008","invalidates":"ev-0007","reason":"换了传感器"}

为什么是 JSONL 而不是一个 JSON 数组或数据库?站在工程角度,好处很直接:git diff 能逐行看出哪条证据是新加的;多人 / 多智能体并发写只追加不冲突;jq、Python、CI 脚本一行就能消费;invalidates 保留了完整审计轨迹,谁在什么时候因为什么推翻了旧结论,一目了然。

一个能直接跑的覆盖率校验脚本

官方文档没有公开 spec.json 的完整字段表,所以下面这个脚本不解析 spec.json,直接把验收条件 id 作为参数传入。脚本是本文另写的示例,不是 ChipCode 内置功能:

#!/usr/bin/env python3
"""校验 evidence.jsonl 是否覆盖了全部验收条件(本文示例脚本,非产品内置)。

用法:python check_evidence.py evidence.jsonl c1 c2 c3
退出码:0 = 全覆盖,1 = 存在未证明的条件
"""
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)

把它挂进 CI,或者提交前跑一遍 pre-commit,就得到了一个很朴素但有效的门禁:条件没被 pass 证据覆盖,就不许宣布联调完成。这套思路不依赖任何特定 IDE,你自己项目里拿 Git + JSONL 也能搭。


四、编译是硬门禁:验证发生在进程层,不是语言层

「AI 说通过」不可信,那什么可信?编译器的退出码和 stdout。

ChipCode 把四条嵌入式工具链都收进了同一个界面,编译 / 烧录 / 串口日志走同一处:

工具链 编译 / 烧录路径 前置条件
Arduino arduino-cli 编译上传(板卡库、端口一站式) 需先选 FQBN 与端口
ESP-IDF idf.py build、idf.py -p {port} flash;图形化 Kconfig 只需端口,目标芯片在设备选项卡选,选中即自动 set_target
STM32Cube CMake 配置 + Ninja 构建,STM32_Programmer_CLI 经 SWD 烧录 SWD 烧录无需串口,串口只用于日志
51(8051) sdcc -mmcs51 main.c → packihx main.ihx > main.hex,stcgal 串口 ISP 内置 10 款常用 STC 芯片

官网给的 51 编译输出示例:

$ sdcc -mmcs51 main.c
main.c: 0 errors, 0 warnings
$ packihx main.ihx > main.hex
main.hex · 842 bytes · Flash 10.3% (1024/8192)

注意最后那行 Flash 占用——这类数字才是可以写进证据账本的东西。

配套的防谎报机制散落在几处:

  • AI 改完代码会跑只读独立验证,结论是 PASS / FAIL / PARTIAL;工具调用失败会标红并注明原因(编译失败、插件报错、操作被拒绝如实显示),子代理与独立验证各有独立卡片,能看出执行者与结论。
  • 桌面端 Ctrl+R 编译、Ctrl+U 上传(上传会先自动编译),输出实时进底部面板,编译错误带行号,点击可跳转。
  • CLI 的 --format json 输出 NDJSON(每行一个 JSON 事件),官方文档给出的片段:
{
   "version":1,"seq":1,"type":"session.status","status":"start"}
{
   "version":1,"seq":2,"type":"message.delta","content":"先看一下项目结构"}
{
   "version":1,"seq":3,"type":"tool.step","name":"read_file","status":"completed","args":{
   ...},"result":"..."}
{
   "version":1,"seq":9,"type":"message.done","content":"..."}

其中 tool.step 带 failed: true 才表示这一步确实失败——编译失败、插件返回失败、操作被拒绝都会如实标记,不会混在自然语言里被抹平。

  • CLI 退出码把「真失败」和「没人确认」分开:1 = 出现工具失败或异常,2 = 用法错误,4 = 非交互环境下遇到需确认的操作,130 = Ctrl+C。文档点明 CI 里最常见的是 4,改成 --yes 或调整任务描述即可。

一句话总结这个思路:把验证从语言层降到进程层。模型可以用自然语言把任何事说成"已完成",但它改不了 sdcc 返回的 exit code,也改不了 0 errors, 0 warnings 这行输出。


五、实操:20 分钟走一遍自动浇花器

按官方文档的「试一次」流程:

  1. 新建项目,类型选「产品项目」——项目骨架自动创建上面那批文件。
  2. 打开左侧产品工厂,点「开始描述需求」,输入:做一个自动浇花器:土干了自动抽水,手机能看湿度。
  3. 创意:AI 先问几个问题,把需求收敛成条件,输入框上方出现「待确认」卡,你确认后才继续。

    示意:比如收敛成 c1 湿度低于阈值启动水泵、c2 湿度恢复后停泵、c3 能读到当前湿度值。这是本文为说明流程写的示意条件,不是软件自动生成的原文。

  4. 造型:产出 design/shape.json 的 model / mesh / views[] 三件套。装了 OpenSCAD 插件时,AI 用插件的 render / preview 出网格和三视图(真几何内核);没装就由 AI 写脚本导出 STL。插件不必提前启用——AI 发现缺建模工具会先查本地插件、弹一次确认请求启用。
  5. 硬件:bom.json + constraints.json,每颗器件带来源链接。这里最容易卡住,缺链接就是「阻塞」。
  6. 软件:firmware/ 写固件,编译通过才计入证据。
  7. 联调:结果写进 evidence.jsonl,covers 指回 c1/c3 这类条件 id;没证明的写进 does_not_cover。
  8. 导出产品包 hardware-package/:
hardware-package/
├── requirements.md   需求与验收条件
├── bom.csv           器件清单与来源
├── evidence.md       验证了什么、没证明什么
├── README.md
├── firmware/         固件源码
├── design/           结构模型与视图
└── .manifest.json    输入指纹,用来判断是否过期

.manifest.json 记录的是输入指纹:之后改了需求或选型,看板会把产品包标成「已过期」。这一笔很工程——交付物和需求版本绑定,避免拿着旧 BOM 去打样。

对要交「代码 + 文档 + 资料包」的场景(课程设计、毕设、方案交付),requirements.md / bom.csv / evidence.md 恰好就是常被要求、又最常被临时补的三份东西。


六、给工程化留的口子:插件契约与 CLI

如果只是个封闭 IDE,上面这些也就是个好用的功能。真正让它可扩展的是插件 manifest 契约。

插件只需一个 plugin.json,通过 transport 声明七类通用通道,全部是宿主通用驱动,无需宿主预注册:

transport 用途
cli 一次性命令行(spawn 数组参数,不经 shell,天然防注入);编译、烧录、流式输出
http REST API,宿主全局 fetch 并尊重代理设置
grpc 插件自带 .proto,宿主动态加载,仅连接该插件自己声明的 backend 端口
mcp 调用插件声明的 MCP 服务器工具
mqtt 通用 MQTT 设备通道,publish / request / collect / read_state / status 五种 op
internal 复用宿主标准能力:串口、日志、状态、Arduino CLI、工具链检测
backend 调插件自声明的 stdio 长驻后端(JSON-RPC over stdin/stdout),适合有状态工具如 idf.py confserver

最小 manifest:

{
   
  "id": "my-toolchain",
  "name": "我的工具链",
  "version": "1.0.0",
  "developer": "你的名字",
  "icon": "🔧",
  "capabilities": {
   
    "compile": {
    "transport": "internal", "service": "arduinoCli", "method": "compile", "output": "avr-gcc" },
    "board-manage": {
    "transport": "internal", "service": "arduinoCli", "method": "board.search" }
  }
}

capabilities 是类型化能力绑定:UI 主按钮(编译 / 上传 / 板卡 / 库管理)与 AI 工具走同一条能力路由,不会出现「界面上能点、AI 不会调」的割裂。安装时 validateManifest 会校验能力 key、transport、命令 placement、scaffold 路径安全(拒绝绝对路径 / .. 穿越)等,非法 manifest 直接拒绝安装。

另外几个对二次开发有意义的点:

  • detection 声明式工具链检测:手动路径 → envVars → 官方 installerJson → pathGlobs → validateFile 按序探测;derive.exportPathFrom 从工具链自己的 tools.json 注册表合成 export PATH,复刻官方 export 脚本而不用跑慢命令。
  • devicePanel 数据驱动设备壳:宿主按 selectors 的 key(fqbn / board / port)自动派生编译 / 上传前置门控 needsBoard / needsPort——这解释了为什么 Arduino 要先选板和口,而 ESP-IDF 只需端口。
  • 9 个官方插件全部 Apache-2.0 开源:github.com/triecode-dev/triecode-plugins,含 arduino-cli、sdcc-51、esp-idf、stm32-cube、openscad、easyeda(原理图布局 + A* 绕障布线)、wokwi、browser、plugin-dev。可审计、可 Fork。

命令行侧还有一个与桌面端共用智能体引擎、模型层、权限内核与失败判据的 triecode CLI,支持 --format json 输出 NDJSON,适合脚本 / CI:

triecode run "把 main.c 编译并烧录到 uno" --yes --format json
triecode compile uno D:\proj --port COM3
triecode flash uno COM3 D:\proj
triecode monitor COM3 115200
triecode doctor

doctor 会打印 API Key、工具链、git、代理的自检结果,缺项给「引导」,任一失败退出码为 1。写 CI 时值得留意文档明说的差异:CLI 不读桌面配置(%APPDATA%\TrieCode\config.json 里的 Key / 代理 / 模型都不生效,只认参数和环境变量)、没有 --resume、工具集是独立的一份(桌面端的插件技能 / MCP / 记忆不经过 CLI),插件只用于 compile / flash 两步。


七、边界与踩坑:先说清楚,再决定用不用

这部分官方文档写得很实,我照搬,不替它遮:

项 现状
平台 仅 Windows 10 / 11 x64;macOS / Linux 开发中
体积 安装包 NSIS 约 144 MB,装后约 500 MB;首次编译 Arduino 另下 AVR 核心约 200 MB;建议内存 ≥ 4 GB
代码签名 暂未做签名,Windows SmartScreen / 部分杀软可能报「未知发布者」,需手动「仍要运行」或加白名单
产品工厂边界 不做 PCB 设计;不提供代购、打样与发货;选型自由但型号必须附来源链接
CLI 尚未发布到 npm,需从仓库构建(corepack pnpm --filter @triecode/cli build),运行时要求 Node ≥ 24(用到 node:sqlite)
计费 积分 = 系数 ×(输入 + 输出 token)÷ 2000,每模型系数不同;自填 Key 时请求直达厂商、不经服务端;也支持本地 Ollama
模型 内置模型随版本更新、退役的会下架;切换模型不影响项目、记忆、技能与检查点
更名 原名 TrieCode,2026 年 10 月起更名 ChipCode,官网迁至 chipcode.cn;只改名称与域名,项目、插件、账号、积分、会话历史全部沿用,无需迁移

还有一条容易被忽略:造型阶段没装 OpenSCAD 插件也能走,但只写 .scad 不导网格,看板会一直停在「进行中」——这不是 bug,是设计上的门禁。


八、我的判断:适合谁,不适合谁

适合:

  • 课程设计 / 毕设 / 方案交付这类本来就要交「需求 + BOM + 源码 + 验证记录」的场景——产品包直接把这四样生成出来,省的是最烦的那部分整理工作。
  • 一个人或小团队从零做一款小硬件,需要 AI 覆盖选型、结构、固件,而不只是补全几行代码。
  • 想借一套现成范式,把「可追溯」落到自己项目里的人——哪怕不用这个 IDE,evidence.jsonl + covers + invalidates + 退出码门禁这套组合也值得抄。

暂不建议:

  • 已经有成熟 CI / PLM / 需求追溯体系的团队:evidence.jsonl 会和现有体系重叠,先想清楚谁是唯一真相源,别搞成两处维护。
  • 只想要轻量代码补全:整套 IDE 太重,编辑器插件更合适。
  • macOS / Linux 用户:等对应版本。
  • 对供应链有严格签名校验的公司环境:当前安装包未签名,IT 大概率会拦。

最值得借鉴的其实不是某个功能,是三条原则:

  1. 状态外置到文件——让看板成为文件的纯函数,AI 无法用一句话改变项目状态;
  2. 验证以退出码为准——可信结论来自进程,不来自模型自述;
  3. 决策点显式留给人——验收条件、结构、选型由人确认,物理结果由人给,AI 只提案。

这三条不依赖任何具体工具,放在你现在的工程里也成立。


九、小结

嵌入式 AI 开发的瓶颈早已从「生成代码」移到了「生成可交付、可追溯的工程」。ChipCode 产品工厂给出的答案不算炫技,甚至有点笨:把每一步产出写成固定文件,把验证写成只追加的 JSONL,把阶段状态交给磁盘算,把关键决策留给人。

但恰恰是这种笨办法,解决了「AI 说做完了,我不敢信」这个最实际的问题。

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