从“会写代码”到“能跑硬件”:我们如何做一个真正闭环的嵌入式 AI Agent——GaryCLI 技术实践
项目:GaryCLI
方向:AI Agent × 嵌入式开发 × 真实硬件执行
本文版本基线:2026-08-12
关键词:AI Agent、STM32、ESP32、ESP8266、RP2040、自动编译、自动烧录、硬件调试、GaryProbe、RAG、工程知识库
如果把过去两年 AI 编程工具的发展压缩成一句话,大概就是:模型越来越会写代码了。
它能补全函数、解释错误、重构项目、生成单元测试,甚至可以自己调用终端、跑测试、修改代码、重新执行。对于 Web、Python、后端服务、前端应用,这套路径已经越来越成熟。
但当我们把同样的方法搬到嵌入式开发上,很快就会碰到一个很现实的问题:
“代码写对了”,和“板子真的按预期工作了”,中间隔着一整个现实世界。
一段 STM32 的 GPIO 代码编译通过,不代表 LED 一定亮。
ESP8266 固件烧录成功,不代表扫描出来的 RSSI、信道、SSID 就一定解析正确。
电机控制程序能够运行,不代表实际机械运动没有卡住、过冲或者反向。
串口出现日志,也不代表通信协议、波特率、数据格式和目标外设真的匹配。
甚至有时候:
- 编译成功;
- 烧录成功;
- 串口也有输出;
- 程序没有崩溃;
但现实表现仍然是错的。
这也是我们做 GaryCLI 时最核心的出发点:不要再把 AI 只放在代码编辑器里,而是让 AI 真正进入嵌入式工程的执行链路。
它应该能够理解用户的自然语言目标,识别芯片和工程,调用确定性的工程工具,生成或修改代码,编译,烧录,读取串口、调试器、寄存器和其他证据,然后基于这些现实反馈继续修复。
换句话说,我们希望 GaryCLI 做的不是“AI 帮你写代码”,而是:
让 AI 尽可能完成一次真实的嵌入式开发任务。
本文想完整讲清楚我们目前是怎么做这件事的、为什么这样设计、哪些地方已经有效、哪些地方仍然很难,以及我们认为嵌入式 AI Agent 接下来真正值得投入的方向是什么。
一、为什么普通 Coding Agent 到了嵌入式领域会突然变难?
软件世界最大的优势,是“程序自身可以观察程序”。
比如一个后端项目:
- 修改代码;
- 运行测试;
- 测试失败;
- 读取堆栈;
- 修改代码;
- 再运行;
- 测试通过。
这一整套验证过程,本质上都发生在同一台计算机里。
模型只要拥有文件系统、Shell、编译器、测试框架,就能够得到非常明确的反馈。
嵌入式不是这样。
嵌入式系统横跨至少五层:
- 自然语言目标
- 软件工程
- 工具链
- 目标芯片
- 现实物理行为
你让 AI 做一个最简单的任务:
“让 STM32 的 PC13 每秒闪烁一次。”
表面上只有一句话,但 Agent 真正需要解决的问题很多:
- 当前是什么芯片?
- 是 STM32F103C8T6,还是 STM32G431C8T6?
- PC13 是否存在?
- PC13 能不能配置成目标模式?
- 当前工程是 HAL、LL、裸机、CubeMX 生成工程,还是一个空文件夹?
- 系统时钟是多少?
- 延时函数是什么?
- 编译器有没有装?
- linker script 对不对?
- startup 文件是否存在?
- ST-Link/WCH-Link 是否在线?
- SWD 是否能连接?
- Flash 是否解锁?
- 烧录到底有没有成功?
- PC13 在这个板子上是否接 LED?
- LED 是高电平点亮还是低电平点亮?
- 即使逻辑正确,板子上是否存在硬件故障?
这已经不是简单的“代码生成”问题。
它更接近一个由 AI 驱动的自动化工程师。
二、GaryCLI 的定位:AI-native embedded development agent
GaryCLI 当前对自己的定义是:
面向 STM32、RP2040 / Pico、ESP32 / ESP8266 等板卡的 AI 原生开发与调试智能体。
这里我们刻意用了“智能体”,而不是“代码助手”。
因为两者最大的区别在于:
助手输出内容,智能体执行动作。
传统 AI 编程助手的输出通常到这里结束:
HAL_GPIO_WritePin(GPIOC, GPIO_PIN_13, GPIO_PIN_RESET);
GaryCLI 希望继续做后面的事情:
理解任务
→ 检查当前工程
→ 识别目标芯片
→ 查询引脚与芯片资料
→ 修改代码
→ 补齐工程文件
→ 编译
→ 诊断错误
→ 烧录
→ 读取串口/调试信息
→ 判断是否达到目标
→ 未达到则继续修改
这个差异看起来只多了几个工具调用,实际上它改变了整个产品的技术结构。
GaryCLI 现在大体可以拆成几层:
1. 交互层
目前我们同时在做:
- CLI
- Desktop GUI
- 小程序
- 后续围绕 GaryProbe 的硬件入口
用户不需要先学习复杂命令。
最理想的交互仍然是直接说:
“扫描附近所有 Wi-Fi,把 SSID、RSSI、信道和加密方式从串口输出,每 15 秒扫一次。”
或者:
“让 PA0 一直输出高电平,编译并烧录到板子。”
甚至:
“为什么我这个程序已经编译和烧录成功了,但是电机实际没有达到目标位置?”
自然语言是入口,但它绝不是执行层。
2. Agent / Controller 层
这一层负责:
- 理解用户目标;
- 维护任务状态;
- 决定下一步该调用哪个工具;
- 控制工具调用是否已经陷入循环;
- 判断证据是否足够;
- 决定是否继续执行、修复、升级模型或者停止。
这里最重要的一点是:
模型不应该直接“想象”硬件世界,它必须尽量通过工具得到证据。
3. 确定性工程工具层
这是 GaryCLI 目前投入非常大的部分。
我们希望把大量重复、脆弱、容易被模型写错的动作,从 Prompt 中拿出来,做成确定性工具。
比如:
- 工程识别;
- 工程创建;
- 芯片识别;
- 串口枚举;
- 调试器连接;
- 引脚资源规划;
- SDK 配置;
- 编译;
- 烧录;
- 串口监视;
- Flash 检测;
- 安全状态检测;
- 代码语义修改;
- 编译错误诊断;
- 硬件测试;
- 调试读取。
模型决定“做什么”,工具负责“怎么稳定地做”。
这也是我们认为嵌入式 Agent 和纯 Coding Agent 差异最大的地方。
三、为什么我们不让模型直接拼命写 Shell 命令?
理论上,一个足够强的大模型拿到 Shell 后,也可以自己做:
arm-none-eabi-gcc ...
openocd ...
pyocd flash ...
idf.py build
idf.py flash
pio run
esptool.py ...
但我们实际做下来发现,这种方式有三个明显问题。
第一,Token 浪费巨大
模型每次都需要重新理解:
- 当前操作系统;
- 路径;
- 工具版本;
- 芯片参数;
- 工程结构;
- 命令参数;
- 失败输出。
很多信息并不是“智能问题”,而是工程 API 问题。
如果把这些东西都塞给模型,不仅慢,而且贵。
第二,稳定性很差
命令行工具经常存在非常细的差异:
- Windows 和 macOS 路径不同;
- 一个版本支持某个参数,另一个版本不支持;
- pyOCD target 名称和芯片完整料号并不总是一致;
- PlatformIO 和原生 ESP-IDF 工程结构不同;
- 串口可能突然断开;
- 调试器可能只读连接成功、写入会话失败;
- 设备被其他进程占用;
- 烧录后端口号可能变化。
如果每次都让模型“临场发挥”,错误率会非常高。
第三,Agent 很容易形成工具循环
典型循环是:
编译失败
→ 模型修改
→ 编译失败
→ 模型又修改相似位置
→ 编译失败
→ 又执行相似命令
如果没有工具语义、状态机和收敛规则,Agent 很容易把 Token 消耗在“看起来很努力但没有进展”的动作上。
所以 GaryCLI 逐渐形成了一个明确原则:
能做成确定性工具的,就不要让模型重复推理。
这并不是削弱模型,而是在给模型减负。
真正需要模型能力的地方应该是:
- 理解任务;
- 选择策略;
- 判断冲突证据;
- 处理未知工程;
- 分析复杂根因;
- 在多个可能路径中做决策。
四、GaryCLI 的核心不是“自动烧录”,而是闭环
很多人第一次看到 GaryCLI 会说:
“不就是 AI 生成代码以后自动编译烧录吗?”
如果只是这样,其实并不难。
难的是:烧录之后怎么办?
我们现在越来越强调一个概念:
Verification Gate —— 验证门
意思是:
没有足够证据,不允许 Agent 轻易宣布任务完成。
比如一个任务:
“让 PA0 输出高电平。”
如果系统只是:
编译成功
烧录成功
那么严格来说,Agent 能确认的是:
- 代码成功构建;
- 固件成功写入 Flash。
但它不能自动确认:
- PA0 真的输出高电平;
- 芯片实际运行到了这段程序;
- 时钟/复位没有问题;
- PA0 没有被其他外设重映射;
- 板子没有硬件问题。
这个边界非常重要。
因为很多 AI Agent 最容易犯的错误,不是代码错,而是结论过度自信。
五、编译成功、烧录成功、硬件验证成功,其实是三种完全不同的证据
我们把验证粗略分成三层。
1. Build Verified
证明:
- 源码能够被目标工具链接受;
- 头文件、链接、符号等基本正确;
- 产生了可以部署的固件。
这是最基础的一层。
2. Flash Verified
证明:
- 调试器或 Bootloader 与目标板建立了可写连接;
- 固件写入成功;
- 可能包含校验、Reset 等动作。
但这里仍然不代表程序功能正确。
3. Hardware / Runtime Verified
这是最难的。
它可能来自:
- 串口协议输出;
- SWD/JTAG 读取变量;
- 寄存器状态;
- GPIO 输入回读;
- ADC 测量;
- 外部测试夹具;
- GaryProbe;
- 用户人工确认;
- 其他传感器或测试设备。
不同任务需要不同证据。
比如:
“扫描附近 Wi-Fi”
如果串口输出了真实 AP 列表,我们可以得到很强的软件运行证据。
但:
“电机转到 35.0°”
如果系统没有编码器、外部传感器或调试接口,只看代码和日志就不能可靠确认。
所以我们后来越来越认可一个现实:
判断现实世界里的任务到底有没有完成,本身就是嵌入式 Agent 最困难的问题之一。
这不是再换一个更强模型就能完全解决的。
你必须增加现实世界的可观测性。
而 GaryProbe 就是在这个背景下出现的。
六、GaryProbe:给 AI 一双真正能够碰到硬件的“手和眼睛”
我们第一阶段更希望把 GaryCLI 做成:
GaryProbe + GUI
GUI 是主要交互入口。
GaryProbe 是真实硬件执行与验证节点。
GaryProbe 的意义不只是“又一个下载器”。
如果只是 SWD 烧录,现在已经有 ST-Link、J-Link、WCH-Link 等大量成熟工具,完全没必要重新造一个。
GaryProbe 真正应该承担的是:
- 板卡识别;
- SWD/JTAG/串口等基础连接;
- 固件下载;
- Reset;
- 运行状态采集;
- 串口数据转发;
- 与云端/桌面 Agent 建立统一协议;
- 后续扩展 GPIO/ADC/逻辑采样;
- 给 AI 提供结构化硬件证据。
当前仓库已经有 GaryProbe/1 的协议定义和模拟器,协议层考虑了:
- requestId;
- CRC32;
- BLE 分包;
- MTU;
- 命令/响应;
- 设备身份;
- 网关接入。
GaryProbe 未来如果做好,真正有价值的不是“AI 能烧录”。
而是:
AI 能够知道自己刚刚对现实世界做了什么,以及现实世界给出了什么反馈。
这是完全不同的产品价值。
七、为什么我们仍然保留本地烧录,而不是全部搬到云端?
这是一个我们内部也反复讨论的问题。
既然 AI、知识库、模型路由都可以放在云端,为什么不让整个嵌入式开发也云端化?
答案是:硬件就在用户桌面上。
云端可以做:
- 模型推理;
- 工程分析;
- 知识检索;
- 编译;
- 构建缓存;
- 团队协作;
- 历史记录;
- 复杂分析。
但真正的目标 MCU、串口、调试器、电机、传感器,仍然位于用户现场。
所以我们更倾向于一个混合架构:
Cloud
├── AI Model
├── Gary Knowledge
├── 项目/会话状态
├── 构建能力
└── 复杂任务分析
│
│ Internet
▼
Local / GaryProbe
├── USB
├── SWD/JTAG
├── UART
├── GPIO/ADC
└── Target Board
这样做有两个好处:
第一,云端可以持续升级智能能力。
第二,硬件执行仍然留在设备附近,避免把现实 I/O 强行抽象成纯云端问题。
我们认为“云端烧录”不是把物理板子搬到云端,而是:
云端完成决策与部分构建,本地节点负责最终硬件执行。
八、我们如何处理不同芯片和不同工程?
嵌入式最大的问题之一,是碎片化。
同样叫“单片机开发”,实际工具链完全不同。
STM32
典型涉及:
- HAL / LL;
- CMSIS;
- startup;
- linker script;
- GCC;
- pyOCD / OpenOCD;
- SWD;
- 芯片精确料号;
- GPIO AF;
- RCC;
- Flash 大小;
- 不同系列外设差异。
ESP32 / ESP8266
可能是:
- ESP-IDF;
- PlatformIO;
- MicroPython;
- esptool;
- USB Serial/JTAG;
- Bootloader 模式;
- 分区表;
- sdkconfig;
- Wi-Fi / BLE;
- PSRAM / Flash。
RP2040 / Pico
又会涉及:
- Pico SDK;
- UF2;
- MicroPython;
- USB mass storage;
- raw REPL;
- PIO;
- 双核。
所以 GaryCLI 的策略不是建立一个“万能 Prompt”,而是把工程能力拆成平台工具。
目前 README 中已经明确覆盖:
| 平台 | 当前代表能力 |
|---|---|
| STM32 F0/F1/F3/F4 等 | HAL C、GCC 编译、pyOCD/SWD 烧录、寄存器级调试 |
| RP2040 / Pico / Pico W | MicroPython / 工程同步 / USB 串口调试 |
| ESP32 系列 | ESP32、S2、S3、C3、C6 等开发板工作流 |
| ESP8266 系列 | NodeMCU、D1 Mini、ESP-01 等工作流 |
实际开发中我们也在继续扩展更多原生 C 工程、芯片族和测试工具。
九、一个真实例子:ESP8266 Wi-Fi 扫描,为什么“程序在跑”仍然是错的?
这是一个很典型的案例。
任务目标很简单:
扫描附近 Wi-Fi,并通过串口输出:
- SSID
- RSSI
- channel
- auth
- hidden
程序编译成功。
固件烧录成功。
ESP8266 也确实每隔一段时间扫描一次。
串口甚至能看到:
Gary:WIFI_SCAN_START
scandone
Gary:WIFI ...
Gary:WIFI ...
Gary:WIFI_SCAN_DONE count=17
如果一个 Agent 只验证到这里,很容易宣布:
“任务完成。”
但进一步看输出,会发现:
rssi=0
channel=1073681290
hidden=1
ssid="*s"
这明显不对。
最初我们怀疑 struct bss_info 的字段布局可能和 SDK 不匹配。
继续排查后发现,真正的问题是:
%.*s
当前 ESP8266 RTOS SDK 使用的轻量 printf 并不完整支持动态精度格式。
结果是参数发生错位,后续 RSSI、信道等字段全部被错误解析。
最后改成兼容格式:
%s
问题才真正消失。
修复后能够得到类似:
Gary:WIFI ssid="TP-LINK_2864" rssi=-72 channel=1 auth=OPEN hidden=0
Gary:WIFI ssid="DIRECT-1F-HP Smart Tank 580-590" rssi=-66 channel=6 auth=WPA2_PSK hidden=0
Gary:WIFI ssid="319_2G" rssi=-54 channel=6 auth=WPA_WPA2_PSK hidden=0
这个例子非常有代表性。
因为:
- 编译器没有报错;
- 烧录器没有报错;
- MCU 没有崩溃;
- 扫描 API 确实执行;
- 串口也确实输出了数据;
但任务仍然没有完成。
真正的验证需要理解:
输出的数据是否符合现实语义。
这就是为什么我们认为未来的 Embedded Agent 必须拥有比“测试是否通过”更丰富的验证机制。
十、另一个例子:STM32 编译成功,但写入会话打不开
另一个真实任务是让 STM32 的某个 GPIO 输出高电平。
代码生成和编译都正常。
调试探针也能识别目标:
STM32G43x/G44x
64 KiB Flash
但 pyOCD 无法打开真正可写的 Flash Session,最终得到:
write_session_unavailable
这种情况下,一个“会说好听话”的 Agent 很容易把前面的识别成功理解成烧录成功。
GaryCLI 更希望坚持:
代码:已完成
编译:已完成
目标芯片识别:已完成
Flash 写入:未完成
硬件功能:无法声明已验证
这个体验有时候看起来“不够智能”。
用户会觉得:
“你不是都识别到了吗?为什么还不能说成功?”
但长期来看,这种边界必须守住。
AI 工程工具最危险的不是失败,而是假成功。
十一、Gary Knowledge:我们为什么又做了一个嵌入式知识库?
大模型本身已经读过海量技术资料,为什么还要做知识库?
因为嵌入式开发存在几个很特殊的问题。
1. 芯片资料高度碎片化
同一个问题的答案可能散落在:
- Datasheet;
- Reference Manual;
- Errata;
- HAL 文档;
- SDK example;
- GitHub issue;
- 论坛;
- 厂商 FAQ;
- 用户实际踩坑记录。
2. “相关”不等于“可执行”
向量检索最容易犯的问题是:
找到了语义相似内容,但工程上不能直接用。
比如用户问 ESP8266 的扫描问题,检索到了 ESP32 IDF 的示例。
文本语义非常接近。
工程上却可能完全不同。
3. 真实验证经验非常值钱
Datasheet 会告诉你某个寄存器怎么用。
但它不会告诉你:
“这个 SDK 的轻量 printf 不支持
%.*s,会导致后续参数错位。”
这种信息是实际工程执行后才产生的。
所以我们现在做 Gary Knowledge 时,目标并不是简单堆资料,而是逐渐把内容变成工程证据单元。
我们会关注一些额外信息:
- 芯片型号;
- 芯片系列;
- 工具链;
- SDK 版本;
- 外设;
- 任务类型;
- 错误签名;
- 修复方式;
- 是否编译验证;
- 是否硬件验证。
例如一条知识不只是:
ESP8266 Wi-Fi scan example
而更希望变成:
platform: ESP8266
task: wifi_scan
sdk: ESP8266 RTOS SDK
symptom:
- rssi=0
- channel abnormal
- ssid="*s"
root_cause:
lightweight_printf_dynamic_precision_unsupported
fix:
replace "%.*s" with compatible "%s"
evidence:
compile_verified: true
hardware_verified: true
这种知识对于 Agent 的价值远高于普通网页搜索结果。
十二、知识库不能“搜到就用”,还必须做工程相关性门控
我们早期做知识库时遇到一个很典型的问题:
召回很多,但不一定有用。
比如当前任务:
“ESP8266 扫描 Wi-Fi”
向量库可能返回:
- ESP32 Wi-Fi scan;
- Linux
iwlist; - 某款路由器 RSSI;
- ESP8266 Arduino 示例;
- ESP8266 RTOS SDK 示例。
从 embedding 相似度来看,它们都很接近。
但真正应该优先的是:
和当前芯片、当前 SDK、当前工程类型、当前错误现象最一致的材料。
所以 Gary Knowledge 后来逐渐增加:
- 工程相关性过滤;
- 芯片约束;
- 任务约束;
- 直接工程可用性判断;
- 不相关结果拒绝;
- evidence semantics;
- 重复知识抑制。
知识库真正难的不是“存进去”。
而是:
在 Agent 最需要的时候,给它最值得信任的那几条。
这和互联网搜索完全不是一回事。
十三、Gary Pro:为什么第四档模型不是简单换一个更大的模型?
GaryCLI 现在的模型档位是:
- Gary Low
- Gary
- Gary High
- Gary Pro
前三档仍然是独立的单模型路线。
Gary Pro 不一样。
它不是“直接把默认模型换成一个更慢更贵的大模型”。
而是我们正在做的一种混合智能体机制:
Luna + Sol
这里的思路很简单。
大多数嵌入式任务其实不需要每一步都进行超长推理。
比如:
- 打开文件;
- 查询工程;
- 调工具;
- 编译;
- 烧录;
- 读取日志;
- 修改明显错误。
这些工作更需要:
- 速度;
- 稳定工具调用;
- 连续执行能力。
所以 Gary Pro 默认让 Luna 负责执行。
但 Controller 会持续观察任务是否出现异常:
- 同类错误连续出现;
- 多轮没有明显进展;
- 工具调用形成循环;
- 编译成功但硬件验证持续失败;
- 软件预期与硬件测量矛盾;
- 当前问题复杂度明显过高;
- Luna 自己无法可靠判断。
一旦出现这些情况,系统才会把一个压缩后的问题上下文交给 Sol 做深度分析。
Sol 的职责不是自己重新执行所有工具。
它更像一个高级工程分析器:
输入:
- 当前任务目标
- 当前硬件
- 已执行动作
- 关键错误
- 重复失败模式
- 当前证据
- 软件预期 vs 硬件实测
输出:
- 根因假设
- 证据分析
- 修复策略
- 下一步验证动作
然后重新回到 Luna。
这样做的目的是同时获得:
- 快模型的吞吐;
- 深模型的复杂问题分析能力;
- 更低 Token;
- 更少无意义长推理。
十四、为什么不能简单把全部历史上下文都扔给 Sol?
这是 Agent 系统很容易犯的另一个问题。
任务执行 20 分钟以后,历史可能包含:
- 上百条工具结果;
- 编译日志;
- 串口日志;
- 重复错误;
- 已经失效的假设;
- 大量代码 diff。
如果直接把所有历史重新发送给深模型:
- 成本非常高;
- 推理变慢;
- 噪声极多;
- 深模型反而更难抓住真正的异常。
所以 Gary Pro 的一个关键设计是:
升级时先压缩问题,而不是复制历史。
理想的升级上下文应该像一次工程事故报告:
目标:
让 PWM 输出 20kHz / 50%
当前硬件:
STM32G431C8T6
当前状态:
编译成功
烧录成功
串口正常
异常:
逻辑计算为 20kHz
外部测量约 10kHz
已尝试:
1. 修改 ARR
2. 修改 PSC
3. 重新初始化 TIM
均无效果
关键证据:
TIM 时钟可能是 APB timer x2
这种上下文远比把整个聊天记录重放一遍有价值。
十五、Gary Low / Gary / Gary High / Gary Pro 四档真正应该有什么区别?
我们不希望模型分档只是:
“越贵,模型越大。”
更合理的是让每一档对应不同工程需求。
Gary Low
适合:
- 查询;
- 简单代码修改;
- 常见 GPIO;
- 常见串口;
- 基础工程操作。
特点:
- 低成本;
- 快;
- 适合频繁使用。
Gary
默认档。
目标是覆盖大多数常见开发任务。
Gary High
更适合:
- 较复杂代码;
- 多文件修改;
- 更难的编译问题;
- 更复杂的工程理解。
Gary Pro
重点不是“更会聊天”。
而是:
- 长任务持续执行;
- 自动判断是否卡住;
- 深度模型按需升级;
- 更强的根因分析;
- 更严格的验证闭环。
如果未来这个方向成立,那么 Agent 产品的模型分级可能不再按照纯参数规模定义,而是按照:
任务执行策略 + 工具能力 + 推理资源调度
来定义。
十六、GUI 为什么重要?嵌入式 Agent 不能只做 CLI
GaryCLI 最早天然适合 CLI。
因为:
- 工程师习惯终端;
- 工具调用本身就是命令式;
- 调试信息适合文本输出。
但我们后来越来越明确地意识到,真正面向更大用户群体时,GUI 非常重要。
原因不是“好看”。
而是嵌入式任务中存在很多同时状态:
- 当前芯片;
- 当前工程;
- 当前模型;
- 当前硬件连接;
- 调试器;
- 串口;
- 编译状态;
- 烧录状态;
- 工具调用;
- 运行日志;
- 最近对话;
- 计划;
- 任务结果。
如果全部挤在一个 Terminal 里,用户会非常难判断:
Agent 现在到底在干什么?
所以现在 Desktop GUI 中会把这些状态拆开,例如:
- 执行概览;
- 工具调用;
- 烧录日志;
- 串口日志;
- 模型档位;
- 任务状态;
- 运行耗时。
我们认为 Agent UI 有一个非常重要的原则:
用户可以不理解所有底层步骤,但必须知道 AI 在对真实硬件做什么。
尤其是:
- 烧录;
- 擦除;
- 修改工程;
- 执行命令;
- 连接硬件。
透明度很重要。
十七、小程序的意义:让硬件开发入口从电脑延伸出去
我们也在同步 GaryCLI GUI 和小程序能力。
小程序不一定适合完成所有重型开发。
但它非常适合:
- 查看任务;
- 远程发起简单操作;
- 查看硬件状态;
- 查看编译/烧录结果;
- 控制 GaryProbe;
- 读取历史会话;
- 轻量项目操作。
如果 GaryProbe 能成为常驻在工作台上的硬件节点,那么未来用户甚至可以:
手机
↓
Gary Cloud
↓
GaryProbe
↓
开发板
直接完成一部分远程操作。
这也是为什么我们要把 GUI、小程序、Agent、云端和 GaryProbe 的能力尽量对齐,而不是做四套互不相关的产品。
十八、Agent 最容易犯的错误:把“可观测”误认为“已验证”
嵌入式领域有一个非常危险的幻觉来源:
日志。
日志很有用,但日志也是软件自己打印出来的。
例如:
Motor reached target angle: 35.0°
如果这个角度只是程序内部计算出来的,而没有外部编码器验证,那么这并不能证明电机实际转到了 35°。
同理:
PWM = 20kHz
可能只是程序按寄存器配置算出来的。
实际波形到底是多少,需要:
- 定时器输入捕获;
- 外部逻辑分析;
- 示波器;
- GaryProbe 采样;
- 其他真实测量。
所以我们正在逐渐把任务结果分成:
Software Expected
Hardware Observed
User Confirmed
它们不应该混在一起。
这是未来 GaryCLI 很重要的一条原则。
十九、为什么我们认为“本地硬件执行工具”是长期壁垒,而不只是过渡方案?
有人会问:
“未来大模型本身越来越强,它们自己也会调用工具,GaryCLI 会不会被模型升级直接覆盖?”
这是我们非常认真思考过的问题。
我们的答案是:
如果 GaryCLI 的价值只是 Prompt + 一个模型,那一定会被覆盖。
如果只是:
用户问题
→ 调 GPT
→ 返回代码
没有任何长期壁垒。
但我们现在真正投入的方向不是模型本身,而是:
1. 真实硬件工具
- 探针;
- 串口;
- SWD;
- Flash;
- GPIO;
- 调试;
- 测量。
2. 芯片工程底座
把各类芯片的工程差异变成确定性能力。
3. 验证机制
模型是否知道任务真的完成。
4. 工程知识数据
尤其是实际踩坑、编译验证、硬件验证后的知识。
5. Agent 状态机
控制:
- 工具收敛;
- 重试;
- 错误升级;
- 复杂任务切换;
- 证据门控。
这些东西即使未来基础模型能力提升,也不会失去价值。
相反,模型越强,工具层价值可能越大。
因为更强的模型会更好地使用更高质量的工具。
二十、我们内部为什么会出现 GaryCLI 比通用 Coding Agent 更快的情况?
我们做过一些同类型硬件任务对比。
在部分内部测试中,同样一个嵌入式任务:
- 通用 Coding Agent 可能需要十几分钟;
- GaryCLI 大约四到五分钟就能完成。
这不是一个正式 benchmark,也不能代表所有任务。
但这个现象非常值得分析。
原因并不一定是 GaryCLI 使用的模型“更聪明”。
很多时候恰恰是因为它少想了很多不必要的东西。
例如通用 Agent 需要自己发现:
- 编译器;
- 烧录器;
- 端口;
- 芯片;
- 命令;
- 项目结构。
而 GaryCLI 已经把这些封装成工具。
于是模型可以直接说:
识别设备
→ 修改工程
→ build
→ flash
→ verify
而不是花大量 Token 去重新发明一套工具链。
这也是我们越来越确信的观点:
AI 产品性能不只取决于模型速度,也取决于你给模型设计了什么行动空间。
二十一、越强的模型,不一定意味着越好的 Agent
很多 AI 产品天然会走向:
模型不够好 → 换更强模型。
但 Agent 的性能往往由多个因素共同决定:
最终体验 =
模型能力
× 工具质量
× 状态管理
× 上下文质量
× 验证能力
× 重试策略
× 延迟
× 成本
一个推理非常强但工具调用非常慢的模型,在硬件开发中未必比一个快模型更好。
尤其是一个真实任务可能需要:
- 20 次文件读取;
- 10 次工具调用;
- 3 次编译;
- 2 次烧录;
- 多轮串口读取。
如果每一步都使用最重模型做长推理,整体体验会非常差。
这也是 Gary Pro 做 Luna + Sol 的根本原因。
二十二、Agent 如何判断自己已经“卡死”?
这是当前我们非常关注的问题。
AI 工具最烦的一种失败不是直接报错,而是:
一直重复。
例如:
调用 A
失败
修改
调用 A
失败
修改
调用 A
失败
或者:
读文件 A
读文件 B
再读 A
再读 B
这种循环如果不控制,会浪费大量 Token 和时间。
Gary Pro Controller 现在会关注:
- repeated_error_count;
- repeated_tool_count;
- tool_cycle_count;
- 是否恢复;
- 是否出现同类硬件/烧录错误;
- 是否出现结果完全不变化。
当系统认为执行没有收敛时,就应该改变策略:
继续重试
不是一个策略。
正确动作可能是:
- 换诊断路径;
- 请求深模型;
- 使用另一种工具;
- 检查硬件连接;
- 明确告诉用户当前缺少现实证据。
Agent 的“聪明”很大一部分其实来自:
知道什么时候不要再做同一件事。
二十三、为什么我们开始记录“失败知识”?
最开始大家做知识库都倾向于保存:
- 正确代码;
- 示例项目;
- 文档;
- API。
但后来我们发现,真正最有价值的往往是:
失败。
例如:
错误:
write_session_unavailable
环境:
STM32G43x/G44x
pyOCD
target 可识别
64KiB Flash
含义:
识别连接成功 ≠ Flash 写入会话成功
Agent 行为:
不能声称烧录完成
或者:
错误:
SSID="*s"
RSSI=0
channel异常
环境:
ESP8266 RTOS SDK
原因:
轻量 printf 不支持动态精度 %.*s
修复:
使用兼容 %s
这类内容在第二次遇到类似任务时极其有价值。
它能让 Agent 从:
“猜一个可能原因”
变成:
“历史上在相同平台出现过同样错误签名,优先检查这个原因。”
这才是真正能够积累的系统能力。
二十四、知识库为什么要去重?
如果 Agent 每完成一次任务,就把整个过程无脑写入知识库,很快就会出现:
ESP8266 Wi-Fi 扫描成功
ESP8266 Wi-Fi 扫描成功
ESP8266 Wi-Fi 扫描成功
ESP8266 Wi-Fi 扫描成功
...
向量库会被大量同类型记录污染。
所以我们也在处理:
- 相同任务重复保存;
- 相似知识合并;
- 内容指纹;
- 工程证据更新;
- 新证据覆盖旧证据;
- 低价值重复结果拦截。
一个真正长期运行的 Agent Knowledge Base 不可能只做 append。
它必须逐渐变成:
会整理自己的工程记忆。
二十五、AI 能不能完全自主判断现实任务完成?
短期来看,我认为不能。
至少不是所有任务。
我们可以把硬件任务分成几类。
第一类:软件可直接观测
例如:
- 串口输出指定内容;
- Wi-Fi 扫描;
- BLE 广播;
- Flash 内容;
- 寄存器值。
这类比较容易自动闭环。
第二类:芯片内部可测
例如:
- PWM;
- GPIO;
- ADC;
- Timer;
- 中断频率。
如果 MCU 自己有输入捕获、回读或者调试能力,也可以部分验证。
第三类:需要外部物理测量
例如:
- LED 实际亮度;
- 电机真实角度;
- 机械运动;
- 电压波形;
- 电流;
- 温度;
- 声音;
- 图像。
这些必须依赖外部传感器、仪器或者人工确认。
所以未来 GaryProbe 如果只是一个 SWD 下载器,是不够的。
它最终应该逐渐成为一个通用的硬件观测节点。
二十六、我们为什么要把“软件预期”和“硬件实测”同时给模型?
因为真正复杂的问题往往来自两者矛盾。
比如:
软件计算:
PWM = 20kHz
硬件实测:
PWM = 10kHz
这条信息非常强。
它会迫使模型重新检查:
- Timer Clock;
- APB 分频;
- x2 时钟规则;
- Prescaler;
- ARR;
- Center-aligned;
- 输出模式。
如果只有软件信息,模型可能永远在代码内部自洽。
而硬件实测会打破这种自洽。
这也是 Gary Pro 自动升级最适合触发 Sol 的场景之一:
软件预期与硬件现实发生明确矛盾。
二十七、GaryCLI 未来真正应该做成什么?
如果只看今天,它还是一个快速迭代中的嵌入式 AI Agent。
但我们希望它最终形成一个更完整的层级。
┌───────────────────────────────┐
│ User Intent │
│ 自然语言:我要这个硬件实现什么 │
└───────────────┬───────────────┘
│
▼
┌───────────────────────────────┐
│ Gary Agent OS │
│ Planning / Tool / Memory / AI │
└───────────────┬───────────────┘
│
┌────────┴────────┐
▼ ▼
Gary Knowledge Gary Cloud
│ │
└────────┬────────┘
▼
┌───────────────────────────────┐
│ GaryProbe Layer │
│ SWD / UART / GPIO / ADC / IO │
└───────────────┬───────────────┘
▼
┌───────────────────────────────┐
│ Physical Hardware │
│ MCU / Sensor / Motor / Device │
└───────────────────────────────┘
也就是说:
GaryCLI 不只是写代码。
GaryProbe 不只是烧录器。
Knowledge 不只是 RAG。
模型也不只是 Chat。
这些组件组合到一起,才可能形成:
一个能够真正参与现实硬件开发的 AI 工程系统。
二十八、现阶段我们最关注的几个方向
1. 更严格的硬件验证
让“完成”这个词越来越可信。
2. GaryProbe 的真实观测能力
不仅能写,还能读,还能测。
3. 多芯片确定性工具链
尤其继续强化:
- STM32;
- ESP32;
- ESP8266;
- RP2040;
- 后续更多 MCU。
4. Gary Knowledge
构建:
- 精确芯片资料;
- 官方示例;
- troubleshooting;
- compile_verified;
- hardware_verified;
- 实际错误签名。
5. Gary Pro
让复杂任务能够:
- 自主升级;
- 自主降级;
- 避免无限工具循环;
- 在长任务中持续保持目标。
6. GUI + GaryProbe 产品化
让 Agent 从“开发者工具”变成更多工程师能直接上手的产品。
二十九、我们不想做什么?
我们不希望 GaryCLI 变成:
1. 一个聊天壳
只是换个 UI 调 API,没有意义。
2. 一个代码模板生成器
生成 GPIO、UART、PWM 示例很容易被通用模型覆盖。
3. 一个只会自动烧录的脚本
烧录本身不是壁垒。
4. 一个“永远说任务成功”的 AI
这在硬件领域非常危险。
我们真正想做的是:
把嵌入式工程中可确定执行的部分工具化,把需要判断的部分交给模型,把现实反馈重新送回模型。
三十、如果未来通用大模型也能够自己烧录、自己调试呢?
我认为这是一定会发生的。
未来的大模型:
- 工具调用更强;
- 视觉更强;
- 代码更强;
- 长任务更稳定;
- Agent 能力更好。
但这不会自动让所有专业工具失去价值。
就像大模型会写 SQL,不代表数据库消失。
大模型会写代码,不代表编译器消失。
大模型会调用浏览器,也不代表浏览器消失。
真正有价值的专业系统会变成:
大模型可以可靠调用的高质量环境。
GaryCLI 如果能做到这一点,它就不应该害怕模型升级。
相反:
模型每升级一次,GaryCLI 的 Agent 上限也跟着提高。
三十一、我们更关心“任务完成成本”,而不是单次模型分数
很多模型 Benchmark 关注:
- coding score;
- reasoning score;
- SWE-bench;
- token/s。
这些当然重要。
但对于真实嵌入式 Agent,我们更关心一个更工程化的指标:
完成一个真实任务,需要多少时间、多少 Token、多少人工介入?
假设两个 Agent 都最终完成任务:
Agent A
15 分钟
150k Token
人工插手 3 次
Agent B
5 分钟
40k Token
人工插手 0 次
用户实际感知完全不同。
所以 GaryCLI 后续也应该逐渐建立自己的 Embedded Agent Benchmark。
不是只测:
“会不会写代码?”
而是测:
任务成功率
真实硬件成功率
平均工具调用次数
重复工具调用次数
平均 Token
平均耗时
人工介入次数
错误恢复能力
错误结论率
特别是最后一个:
错误结论率
我认为它在硬件 Agent 中可能比代码正确率还重要。
三十二、什么才是真正的“AI 原生嵌入式 IDE”?
过去 IDE 的中心是:
文件
用户打开工程、编辑文件、编译。
未来 Agent IDE 的中心可能变成:
任务
用户说:
“把这个传感器接起来,每 100ms 读取一次数据,通过 BLE 发出去,如果连接断开自动重连。”
系统自己完成:
- 读工程;
- 查芯片;
- 查引脚;
- 查传感器;
- 修改驱动;
- 配 BLE;
- 编译;
- 烧录;
- 读取日志;
- 验证;
- 保存知识。
用户仍然可以随时进入代码层。
但代码不再是唯一入口。
这是我们理解中的 AI-native embedded development。
三十三、GaryCLI 当前仍然有哪些明显不足?
写到这里,也必须把现在的问题说清楚。
1. 现实验证仍然不够强
没有接入测量设备时,很多物理行为无法自动确认。
2. 芯片生态太碎
不可能一次性覆盖所有 MCU。
3. 厂商工具很难统一
ST、Espressif、WCH、Raspberry Pi 等工具链风格差异巨大。
4. 长任务 Agent 仍然可能卡住
虽然已经做工具循环检测和 Gary Pro,但仍然需要不断改。
5. Knowledge 的质量比数量重要
“1 万条知识”并不天然比“1000 条高质量工程知识”更强。
6. 硬件世界存在 AI 无法修的软件外问题
比如:
- PCB 错误;
- 电源问题;
- 焊接;
- IO 短路;
- 时钟;
- 线没接;
- 探针没接;
- 电机机械卡死。
AI 必须知道什么时候承认:
这个问题已经超出当前观测能力。
三十四、我现在越来越相信:嵌入式 Agent 的核心竞争不是“模型”,而是“闭环质量”
一开始做 AI 产品,很容易把所有注意力放在:
“哪一个模型更强?”
后来真正把 Agent 跑到硬件上以后,会发现:
模型只是整个系统的一部分。
影响体验的可能是:
- 一个串口自动重连;
- 一个正确的 target mapping;
- 一个错误拦截;
- 一个知识去重;
- 一个工程目录识别;
- 一个工具循环检测;
- 一条“证据不足不能宣布成功”的规则。
这些看起来都没有大模型那么性感。
但真正决定用户能不能完成任务的,往往就是这些细节。
所以我们现在对 GaryCLI 的理解已经从:
“AI 写嵌入式代码”
逐渐变成:
“构建一个以 AI 为决策核心、以工具为行动能力、以真实硬件为验证对象的嵌入式工程系统。”
三十五、一次完整的 GaryCLI 任务,理想情况下到底是怎么跑的?
前面讲了很多架构,如果把它落到一次真实开发任务里,流程会更直观。
假设用户拿来一块新的 ESP32-S3 开发板,打开一个工程目录,然后只输入一句:
“把这个工程改成每秒扫描一次附近 Wi-Fi,只输出信号最强的 5 个热点,串口打印 SSID、RSSI 和信道,编译、烧录并确认实际输出正常。”
一个理想的 GaryCLI 执行过程,不应该直接从“写代码”开始。
第一步:确认当前工作空间
Agent 首先应该知道用户绑定的是哪个工程目录,并且所有读取、修改、构建和生成文件都必须限制在这个目录中。
它需要判断:
- 这是空目录还是已有工程;
- 是 ESP-IDF、PlatformIO,还是其他工程;
- 是否已经存在
CMakeLists.txt、platformio.ini、sdkconfig; - 是否有用户自己的业务代码,避免无脑覆盖;
- 目录是否可写。
这一步看似普通,但它决定了 Agent 到底是在“创建项目”,还是在“修改项目”。
第二步:识别硬件和工具链
接下来系统检查:
- 当前有哪些串口;
- USB 设备是什么;
- 能否确认 ESP32-S3;
- Flash 容量;
- 是否存在 USB Serial/JTAG;
- ESP-IDF / PlatformIO 是否可用;
- 工程目标芯片与真实芯片是否一致。
如果工程配置写的是 ESP32,而现场实际上是 ESP32-S3,那么再漂亮的业务代码也没有意义。
第三步:先查知识,再决定怎么改
如果 Gary Knowledge 中已经存在:
- ESP32-S3 Wi-Fi 扫描;
- 当前 IDF 版本示例;
- 已验证的扫描回调;
- 串口输出格式;
- 历史编译错误;
Agent 应该优先利用这些材料,而不是每次都从语言模型记忆里重新生成一份“差不多”的代码。
这一步的目的不是让 RAG 代替模型,而是减少无意义的不确定性。
第四步:修改最小必要代码
好的 Agent 不应该为了完成一个简单任务重写整个项目。
它应该:
- 尽量保留现有工程结构;
- 只修改真正需要的文件;
- 对已有业务逻辑做最小侵入;
- 明确知道自己改了什么;
- 在修改后立即进入构建验证。
对于一个 Wi-Fi 扫描任务,真正需要的可能只是初始化、扫描配置、结果排序和串口格式化,而不是重新生成整个 SDK 工程。
第五步:编译不是结束,而是第一道门
编译通过以后,系统应该记录:
Build Verified = true
如果失败,则根据错误类型决定:
- 缺头文件;
- API 版本不匹配;
- target 配置错误;
- 链接错误;
- C/C++ 类型错误;
- SDK 配置问题。
这里最忌讳的是模型只看错误最后一行,然后盲改。
更好的方式是让工具先把几十 KB 的编译日志压缩成结构化诊断,再把关键部分交给模型。
第六步:烧录并确认目标板重新运行
烧录阶段需要确认的不是“命令退出码是不是 0”这么简单。
还包括:
- 端口是否仍然存在;
- 是否进入正确 Bootloader;
- 写入是否完整;
- 是否校验成功;
- Reset 后板子是否重新启动;
- 串口是否因为重启而改变设备节点。
如果现场硬件不可写,那么系统必须停在:
Flash Verified = false
而不是为了让最终回复更完整,自动把任务描述成“已经部署”。
第七步:读取运行证据
这时才进入真正关键的一步。
系统监听串口,应该看到类似:
Gary:WIFI ssid="Office_5G" rssi=-41 channel=44
Gary:WIFI ssid="Lab_AP" rssi=-52 channel=6
Gary:WIFI ssid="Router_2G" rssi=-58 channel=1
...
然后继续判断:
- 是否真的每秒刷新;
- 是否只输出 5 个;
- RSSI 是否排序;
- SSID 是否正常;
- 信道值是否合理;
- 是否持续稳定运行,而不是只启动一次。
只有这些条件满足,才有资格把任务提升到 Runtime Verified。
第八步:如果现实结果和软件预期矛盾,就重新打开问题
假设代码里明明排序了,串口输出却不是从强到弱。
这时系统不应该因为“编译和烧录都成功”而结束。
它必须重新进入诊断:
目标未满足
→ 分析输出
→ 检查 RSSI 类型/排序
→ 修改
→ 重新编译
→ 重新烧录
→ 再验证
如果连续多次修改都没有进展,Gary Pro Controller 才有理由把问题升级给 Sol。
这就是 GaryCLI 和普通“一次性代码生成器”的本质区别。
第九步:把有价值的结果沉淀,而不是把整段聊天塞进知识库
任务完成后,真正值得保存的不是:
“用户让我扫描 Wi-Fi,我调用了 37 次工具,最后成功了。”
而是提炼成:
目标平台:ESP32-S3
任务:Wi-Fi scan top 5
工具链:ESP-IDF x.x
关键实现:按 RSSI 排序、每秒扫描
已验证:compile / flash / runtime
特殊问题:……
有效修复:……
下一次 Agent 再遇到相同任务,可以直接复用经过验证的工程经验。
我认为这才是 AI Agent 真正会随着使用次数增加而变强的方式。
它不是把聊天记录越积越多,而是把成功和失败逐渐压缩成可再次执行的工程知识。
三十六、从这个流程也能看出:未来的嵌入式开发不是“自动生成代码”,而是“自动经营状态”
传统 IDE 最主要的状态是:
当前文件有没有保存?
工程能不能编译?
Agent 时代需要管理的状态多得多:
用户真正的目标是什么?
当前芯片是什么?
当前工程是什么?
已经修改了哪些文件?
哪一步验证成功?
哪一步只是推测?
硬件现在是否在线?
哪些错误已经试过?
哪些动作重复过?
当前结论有多少现实证据?
是否应该继续?
是否应该升级模型?
是否应该让用户接线或人工确认?
这些状态如果全部交给语言模型的上下文记忆,很容易漂移。
所以 GaryCLI 后面一个非常重要的方向,就是继续把这些内容从“自然语言聊天记录”变成结构化任务状态。
因为真正可靠的 Agent,并不是每一步都显得很聪明。
而是:
它不会忘记自己正在完成什么,也不会把尚未验证的事情误认为已经完成。
三十七、结语:AI 真正进入现实世界,可能先从工程工具开始
机器人、自动驾驶、具身智能都在讨论:
AI 如何理解和作用于现实世界?
其实嵌入式开发本身就是一个非常典型的现实世界接口。
MCU 连接的是:
- 电机;
- 传感器;
- 屏幕;
- 电源;
- 通信模块;
- 工业设备;
- IoT;
- 机器人。
如果 AI 只能生成代码,它仍然停留在“数字世界”。
如果 AI 能够:
- 改代码;
- 编译;
- 烧录;
- 连接板子;
- 读取状态;
- 判断结果;
- 修复问题;
它才真正开始跨过软件与现实之间的边界。
这就是我们做 GaryCLI 最想探索的事情。
不是让 AI 取代嵌入式工程师。
而是让工程师第一次拥有一个:
能够和他一起碰真实硬件、执行真实动作、读取真实反馈的 AI Agent。
GaryCLI 现在还远没有走到终点。
但至少我们越来越确定,方向并不是把 ChatGPT 再套一层 UI。
真正值得做的是:
把 AI 接到工程系统里。
把工程系统接到硬件上。
再把硬件的真实结果送回 AI。
当这个闭环真正跑通以后,嵌入式开发的交互方式可能会发生非常大的变化。
更多推荐



所有评论(0)