做过单片机的人都懂那种碎:一个功能闭环要横跨五六个工具。代码在编辑器里改,编译切命令行,烧录开 STM32CubeProgrammer 或 probe-rs,看日志再开一个串口助手,最后想知道固件占了多少 Flash、哪个函数把栈撑爆了,还得翻 .map 或者手动加 -fstack-usage 重编一遍。
每一步都是独立的手工操作。而一个「AI 编码助手」如果只停在「帮你写代码」这一步,对嵌入式工程师的帮助是残缺的——因为固件开发的最后一公里恰恰是最容易翻车的部分,通用 coding agent 完全没覆盖。
这就是我做 Firment 的原因。
一句话:把整条链路装进同一个对话
Firment = Firmware + Agent(名字取自 firmament,故意少一个 a)。它是一个用 Rust 写的通用编码 Agent,但带着一个嵌入式优先的工具链闭环:
你说「给 UART1 加一个 DMA 接收的驱动,烧到板子上跑一下,看看日志」,它自己走完 写码 → 编译 → 烧录 → 串口观测 → ELF 分析,而不是丢给你一段代码就完事。
关键不是「会写代码」,是「对结果负责」
让模型写一段 main.c 不难,难的是让它诚实地判断这件事到底成没成。所以我没有依赖模型的自觉,而是搭了一套机械强制的证据阶梯,五级:
- 代码级 —— 文件按预期改了
- 构建级 —— 真实工具链编译通过
- 部署级 —— 固件烧进了目标
- 运行级 —— 串口输出 / 寄存器符合预期
- 物理级 —— 观测到真实的外部效果
规则很硬:高级不自动蕴含低级,编译通过也不自动等于物理正确。三层闸门把它落到机器上:
verify_command闸门:配了校验命令,回合结束前必须过,不过就继续干活;- ELF 回归闸门:每次编辑回合后自动重分析产物,flash/RAM 涨了、某函数栈深暴增这类「编译能过但其实是回归」的问题,超阈值就卡住完成;
- HIL 端到端套件:
build → flash → monitor(带断言)→ elf_analyze串成一次可回放、可 dry-run 的验证。
物理级证据:不靠视觉模型,靠确定性测量
这是 v0.7/v0.8 我花最多力气的地方。让 agent「看到」板子的真实行为,但不用大模型看图(不可复现、还贵),而是本地确定性算法:
observe—— 对目标板的照片做 CV:LED 亮没亮、连拍里有没有东西动、闪不闪且多快(频率只给区间不给伪精确值)、改动前后像素变没变。每个结论带置信度,HIL 步骤可以断言它——低置信度永远过不了断言。la(逻辑分析仪) —— 经 sigrok-cli 采集真实波形,测频率/占空比/脉宽/波特率,协议解码(uart/spi/i2c/CAN…)交给 sigrok 自带解码器。于是「这条线真的是 115200 吗」「这个 PWM 真的是 1 kHz 吗」从「模型说了算」变成「波形测出来」。
红队:让 agent 攻击自己写的固件
最新的一块。固件写完,另一个「攻击者」子 agent 拿确定性变异语料(种子固定,同种子同字节序列)往运行中的固件灌畸形输入,用崩溃哨兵(故障签名 / 启动横幅重现 / 心跳丢失)判定,产出必须引用捕获证据的漏洞报告——缺证据就封顶 low/UNVERIFIED。复现一个漏洞只需 seed + case id,不需要再问一次模型。
安全模型:嵌入式工程师对「AI 删东西/烧错固件」格外敏感
所以默认行为围绕这点设计:写/改/shell 默认要权限确认;rm/format/git reset --hard/强推一律拦截;edit_file 用 SHA-256 内容寻址锚定,同一处编辑只生效一次;路径沙箱;web_fetch 带 SSRF 防护。
三端一个内核
monorepo,CLI/TUI(Rust + ratatui)是事实标准,GUI 客户端(Tauri + React)共用同一个 Rust 内核,Web 端(Next.js)是 TS 重实现、靠一份提交的工具规格快照保持同步。MIT 开源,一行安装。
如果你也在琢磨「AI 到底能不能对硬件负责」,或者就是嵌入式 + agent 这个交叉方向感兴趣,欢迎去看看 Firment,或者拿你的板子试试。这个博客会持续记录我踩的坑和背后的设计取舍。