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