最近我一直在开发 GaryCLI。

它想做的事情,是让 AI 不只是“帮你写几行嵌入式代码”,而是真正进入嵌入式研发现场:

一开始我觉得,这件事应该没有那么复杂。

毕竟现在的 Coding Agent 已经可以:

读代码
改文件
执行 Shell
跑测试
看 Diff

那给它再接几个工具:

esptool
idf.py
串口
BLE
RS485
Modbus

不就行了吗?

后来发现:

完全不是这么回事。

真正开始让 AI Agent 接触 ESP32、串口、BLE、RS485 和 Modbus 之后,我越来越确定一件事:

嵌入式 AI Agent 最难的,可能根本不是写代码。

而是:

它到底操作的是哪台设备?
命令到底执行到了哪一层?
日志到底是不是这一次运行的?
Timeout 后到底能不能重试?
Build 成功以后,功能真的正常了吗?

这些问题如果处理不好,AI 会出现一种非常危险的状态:

事情只做成功了一半,但它会非常自信地告诉你:“已经修好了。”

这篇文章就聊聊,我在做 GaryCLI 过程中越来越重视的 10 个坑。

有些坑看起来很小。

但真正到了嵌入式现场,可能足够让 AI Agent 查半天,甚至把问题越修越复杂。


第一个让我印象很深的坑:AI 对着错误的串口认真分析了半天

有一次,电脑上同时插着几个设备。

GaryCLI 扫描到了:

COM3
COM5
COM8

Agent 选中了:

COM5

然后开始烧录:

esptool.py --port COM5 ...

失败。

接下来 AI 开始非常认真地分析。

可能 ESP32-S3 没有进入 Download Mode。

建议:
1. 按住 BOOT
2. 点击 RESET
3. 重新烧录

看起来非常合理。

我低头看了一眼设备管理器。

ESP32-S3 在 COM8。

COM5 是旁边那个 USB 转 RS485。

也就是说:

这种情况特别符合我后来对 Embedded Agent 的一个认识:

如果最底层的上下文错了,后面推理得越认真,反而离答案越远。


对人来说是设备,对 AI 来说只是字符串

工程师看到:

COM8

脑子里可能自动关联:

桌子右边那块 ESP32-S3

但 AI 看到:

COM3
COM5
COM8

它没有这种物理环境上下文。

更麻烦的是,串口还会变化。

今天:

ESP32-S3 -> COM8

重新插拔以后:

ESP32-S3 -> COM11

Linux 下也一样:

/dev/ttyUSB0
/dev/ttyUSB1
/dev/ttyACM0

如果 Agent 只是记:

上次设备在 COM8

那这个记忆其实没有多大价值。


这个坑之后,我开始重视 Device Identity

GaryCLI 真正应该识别的不是:

COM8

而是:

这是哪一块设备?

例如:

device:
  name: esp32s3-dev-01
  chip: ESP32-S3
  vid: 0x303A
  pid: 0x1001
  serial_number: ABCD1234
  last_port: COM8

选择目标设备的时候,不应该:

ports = scan_ports()
return ports[0]

而应该:

VID / PID
    ↓
USB Serial Number
    ↓
芯片握手
    ↓
设备历史
    ↓
最后才参考 COM 口

必要时甚至可以执行:

esptool.py chip_id

确认:

Chip is ESP32-S3

之后再继续烧录。


第一个结论

Port 不等于 Device。

代码文件可以通过路径定位。

但真实硬件必须先回答一个更基础的问题:

我现在到底在操作谁?


第二个坑:Build Success,然后 AI 就宣布“问题已经修复”

这个我觉得是 Coding Agent 进入嵌入式以后最容易出现的问题。

比如 GaryCLI 修改了 BLE 代码。

执行:

idf.py build

返回:

Project build complete.

AI 很容易给出:

问题已经修复。

但工程师重新拿手机一扫:

还是搜不到 BLE。

这时候就会出现一个非常经典的场景:

AI:编译已经成功。

我:然后呢?

AI:代码没有编译错误。

我:但是 BLE 还是连不上。

AI:……

这里真正的问题不是 AI 不会写代码。

而是它把:

Build Success

错误地理解成:

Problem Solved

嵌入式里的“成功”其实分很多层

对于一个 BLE Bug 来说:

所以真正的验证链路应该是:

到了这里,才能比较有底气地说:

BLE 问题已经修复并验证。

GaryCLI 不应该只有一个 success

如果工具返回:

{
  "success": true
}

这个信息对 Agent 来说太模糊了。

到底什么成功?

更合理的是:

{
  "build": "passed",
  "flash": "passed",
  "boot": "passed",
  "runtime_check": "passed",
  "functional_test": "passed"
}

如果现在只有:

build = passed

那 Agent 最多只能说:

当前代码已经通过编译。

而不能说:

问题已经修复。

这个坑让我越来越确定一件事

嵌入式 AI Agent 的验证能力,可能比代码生成能力更重要。

现在模型写代码已经越来越强。

但真正进入硬件场景以后,区别会逐渐变成:

你能不能证明自己真的改对了?

第三个坑:BLE 明明显示 Write Success,设备其实根本没执行成功

这个坑也非常有代表性。

GaryCLI 向设备发送:

{
  "cmd": "set_config",
  "slave": 1,
  "poll_ms": 1000
}

BLE API 返回:

Write Success

Agent:

配置成功。

但设备串口里面可能正在输出:

JSON Parse Error

或者:

Invalid Parameter

甚至:

Command Queue Full

这个时候你就会发现:

AI 说的“成功”和你理解的“成功”,根本不是一回事。


Write Success 到底代表什么?

BLE 里的:

Write Success

很多时候只能证明:

数据已经成功写到了 Characteristic。

但完整的业务流程可能是:

所以:

Transport Success

和:

Business Success

完全不是一个概念。


更合理的方式是 request_id

例如 GaryCLI 发:

{
  "request_id": "req_1024",
  "cmd": "set_config",
  "slave": 1,
  "poll_ms": 1000
}

设备真正执行完成以后再返回:

{
  "request_id": "req_1024",
  "status": "ok"
}

只有这里 GaryCLI 才能告诉用户:

配置已经成功执行。

否则应该明确说:

BLE 数据写入成功,但尚未确认设备业务执行结果。

这个问题其实不只是 BLE

同样的坑还存在于:

串口 write success
HTTP 200
MQTT publish success
WebSocket send success

AI 必须知道:

数据发出去了

和:

事情做成了

之间还有多少层。


第三个结论

Transport ACK 不等于 Business ACK。

这是 GaryCLI 这种硬件 Agent 必须明确区分的一层。


第四个坑:Modbus 地址只差 1,AI 能陪你查半天硬件

做过 Modbus 的应该很容易有感觉。

例如寄存器文档里面写:

30101

Agent 直接发:

FC04
Address = 101

然后:

Timeout

接下来 AI 的排查过程可能是:

AI:建议检查 RS485 A/B 是否接反。

我:刚才别的寄存器还能读。

AI:建议确认波特率。

我:9600,没改。

AI:建议确认从机地址。

我:1。

AI:建议检查终端电阻。

我:……

最后发现:

Address 应该是 100。

只差一个。


为什么 Modbus 特别容易让 Agent 翻车?

因为一个地址背后可能同时存在:

文档参考地址
寄存器编号
功能码
PDU Address

比如:

30101

可能表达的是:

3xxxx
→ Input Register
→ FC04

101
→ 第 101 个寄存器

Modbus PDU
→ 从 0 开始

最终 Address
→ 100

同样:

10012

可能对应:

FC02
Address = 11

如果每一次都让 LLM 自己算:

30101 应该怎么转换?

实际上是在把一个确定性问题交给概率模型。


GaryCLI 里我更倾向于这样做

Agent 只表达:

我要读取 30101。

然后交给确定性工具:

resolve_modbus_reference(30101)

返回:

{
  "reference": 30101,
  "function_code": 4,
  "address": 100
}

再由底层 Driver 负责:

CRC
Frame
Length
Address
Timeout

也就是说:

AI
负责理解“我要干什么”

程序
负责保证“协议怎么算”

这个坑给我的启发非常大

凡是可以通过确定性代码解决的问题,就尽量不要让 LLM 自由发挥。

例如:

CRC
寄存器地址换算
长度
字节序
类型转换
帧编码

这些东西并不需要“智能”。

它们需要的是:

永远算对。

第五个坑:Timeout 自动重试,结果继电器被切回去了

这个是我觉得最值得警惕的一类问题。

软件系统里我们非常习惯:

Timeout
↓
Retry

网络请求失败了?

重试一下。

接口超时了?

重试一下。

但硬件不是这么玩的。


举一个最简单的例子

Agent 发:

toggle relay

设备第一次其实已经执行成功:

OFF
→
ON

但是 ACK 丢了。

GaryCLI 看到:

Timeout

于是非常自然地:

Retry 1

设备又执行:

ON
→
OFF

然后第二次 ACK 成功回来。

GaryCLI:

执行成功。

实际继电器:

OFF

用户:

?

这背后其实是幂等性问题

例如:

set_relay(ON)

执行:

1 次
10 次
100 次

最终状态都是:

ON

这种命令比较容易重试。

但下面这些:

toggle()
increment()
pulse()
move_relative(+10)
reset_counter()

就不能简单重试。

因为第二次执行可能真的会再次影响物理世界。


所以 Tool 应该描述副作用

例如:

{
  "tool": "set_relay",
  "idempotent": true,
  "retry_policy": "safe"
}

而:

{
  "tool": "toggle_relay",
  "idempotent": false,
  "retry_policy": "verify_before_retry"
}

Timeout 后应该:

读取真实状态
    ↓
判断第一次是否已经执行
    ↓
决定是否需要再次执行

而不是:

Timeout
↓
无脑 Retry

这个坑让我非常警惕“自动化”

AI Agent 自动化程度越高,并不天然代表系统越可靠。

因为软件世界里的:

Retry

到了物理世界,可能变成:

再执行一次真实动作。

前面 5 个,是我觉得最有“现场感”的坑

下面 5 个,我快速讲。

它们没有那么戏剧性,但实际上同样重要。


第六个坑:00 EB 到底是 235,还是 23.5℃?

Modbus 已经读成功:

00 EB

转换成十进制:

235

AI:

当前温度 235℃

但寄存器定义:

type: int16
scale: 0.1
unit: C

正确值:

23.5℃

再复杂一点:

12 34 56 78

可能还有:

ABCD
BADC
CDAB
DCBA

不同字节序。

所以 GaryCLI 更适合让协议层直接返回:

{
  "name": "ambient_temp",
  "raw": 235,
  "value": 23.5,
  "unit": "C"
}

而不是把:

00 EB

扔给 LLM 猜。

结论:

协议层负责字节变语义,AI 负责理解语义。


第七个坑:AI 一边 Poll,一边 Write,RS485 被自己搞乱

后台正在:

每 1000ms Poll 一次

用户又说:

把 40010 写成 1。

如果两个任务直接同时操作 UART:

TX Poll
TX Write
RX Poll
RX Write

最后:

CRC Error
Timeout
Wrong Slave Response

AI 可能分析:

疑似现场电磁干扰。

但实际:

干扰来自自己。

所以 Modbus:

Polling
Read
Write
Diagnostic
Agent Test

都应该进入统一:

Modbus Queue
    ↓
Bus Worker
    ↓
RS485

结论:

AI 可以并发思考,但硬件总线不能并发乱发。


第八个坑:AI “优化”了接口,然后把 Raw Transport 优化没了

例如 RS485 原始帧:

01 04 00 64 00 02 ...

某个接口的目的只是:

收到什么
就转发什么

这是:

Raw Transport

但 Agent 觉得结构化更漂亮。

于是改成:

{
  "slave": 1,
  "fc": 4,
  "address": 100
}

结果原来依赖 Raw 数据的调试工具全部挂了。

所以工具名称应该明确:

raw_rs485_send()
modbus_read()
modbus_write()
modbus_diagnostic()

而不是一个:

send_rs485()

让 AI 自己猜。

结论:

Tool 越模糊,Agent 越容易聪明地做错事。


第九个坑:AI 分析了 5000 行日志,结果分析的是上一次启动

比如日志文件一直 append:

10:01 第一次启动
10:05 第二次启动
10:08 第三次启动

AI 看到:

E BLE: init failed

然后继续修改 BLE。

再次烧录以后,它又看到同一句:

E BLE: init failed

于是判断:

修复无效。

最后发现:

两次分析的都是第一次启动的旧日志。

所以 GaryCLI 需要:

Run ID
Boot ID
Firmware Hash
Device ID

例如:

runs/
└── 20260831-102500-A71F/
    ├── plan.json
    ├── build.log
    ├── flash.log
    ├── serial.log
    ├── test.json
    └── result.md

这样 Agent 才知道:

这是谁的日志?
哪一版固件?
哪一次烧录?
哪一次启动?

结论:

没有上下文的日志,只是一堆字符串。


第十个坑:我让 AI 修 BLE,它最后改了 17 个文件

用户:

BLE 偶尔断线,帮我看看。

Agent:

修改 ble_manager.c

然后发现:

event handler 可以一起整理

继续。

然后:

queue 也可以优化

继续。

然后:

日志风格也不统一

继续。

最终:

17 files changed
+1264
-738

我只是让它:

修一下 BLE 断线。

结果差点变成:

BLE Architecture V2

这也是 Coding Agent 很典型的问题:

Scope Creep

所以 GaryCLI 可以有:

problem:
  BLE reconnect occasionally fails

expected_files:
  - main/ble_manager.c

max_files_changed: 2
max_lines_changed: 80

如果超过:

Patch Budget Exceeded

就重新评估。

最终再:

git diff
↓
build
↓
flash
↓
test

失败就:

rollback

结论:

AI 能改代码,不代表应该无限制地改代码。


做 GaryCLI 以后,我对 AI Agent 的理解变了

最开始,我觉得:

LLM + Tools = Agent

现在我觉得,至少对于嵌入式来说,真正完整的公式更像:

Embedded AI Agent

=
LLM
+
Tool System
+
Device Identity
+
Device State
+
Protocol Semantics
+
Resource Scheduler
+
Execution Context
+
Safety Boundary
+
Verification Loop

这里面任何一个环节缺失,都可能出现一种状态:

AI 看起来完成了很多操作
↓
但其实没有真正解决问题

我现在越来越觉得,GaryCLI 真正应该解决的是这 5 件事

1. AI 到底在操作谁?

不是:

COM8

而是:

这是一块 ESP32-S3
这是 dev-board-01
这是我刚才烧录的那台设备

这叫:

Device Identity

2. 设备现在到底是什么状态?

例如:

Disconnected
Connected
Bootloader
Running
BLE Ready
Modbus Polling
Fault

这叫:

Device State

3. Agent 调用的这个 Tool 到底是什么意思?

不是模糊的:

run()
send()
write()

而应该是:

flash_firmware()
collect_boot_log()
ble_scan()
ble_connect()
modbus_read()
modbus_write()

每个 Tool 都应该明确:

输入
输出
副作用
权限
是否可重试

4. 现在到底成功到了哪一层?

需要明确区分:

Tool Success

Transport Success

Protocol Success

Business Success

Functional Success

例如:

idf.py build success

只能证明:

Tool Success

而:

BLE Write Success

可能只证明:

Transport Success

它们都不能直接等于:

问题已经解决。

5. AI 怎么证明自己真的修好了?

这才是我现在最在意的部分。

例如:

修改 BLE
    ↓
重新 Build
    ↓
Flash
    ↓
读取 Boot Log
    ↓
Scan
    ↓
Connect
    ↓
Read / Write
    ↓
验证结果

Modbus 也是:

修改 Modbus
    ↓
重新运行
    ↓
读取真实寄存器
    ↓
校验返回值

最终所有问题都应该回到:

真实硬件验证

这也是 GaryCLI 和普通 Coding Agent 最想拉开的地方

普通 Coding Agent 的闭环通常是:

代码
↓
运行
↓
测试

而 GaryCLI 想尝试继续往下延伸:

需求
 ↓
代码
 ↓
Build
 ↓
Firmware
 ↓
Flash
 ↓
MCU
 ↓
Serial
 ↓
BLE / RS485 / Modbus / Ethernet
 ↓
真实设备
 ↓
真实结果
 ↓
AI 再分析

也就是:

从代码闭环,走向硬件闭环。


我理想中的 GaryCLI 工作方式

比如用户只输入一句:

为什么我的 ESP32-S3 BLE 连不上?

理想情况下,GaryCLI 不应该只回复一篇排查建议。

而应该真正执行:

这里面我觉得最重要的是两个词:

Observe

和:

Verify

没有 Observe:

AI 不知道真实世界发生了什么。

没有 Verify:

AI 不知道自己的修改有没有真的生效。

如果这两个环节缺失,中间的大模型再强,也很容易变成:

根据有限信息做一个看起来合理的猜测。

10 个坑,最后再快速总结一下

编号现场问题本质
01AI 选错串口Port 不等于 Device Identity
02Build 成功就宣布修复编译成功不等于功能成功
03BLE Write Success 被认为配置成功Transport ACK 不等于 Business ACK
04Modbus 地址差 1Reference 不等于 PDU Address
05Timeout 后无脑 Retry非幂等硬件操作风险
0623.5℃ 被解析成 235℃Scale / Type / Byte Order
07Poll 和 Write 同时访问 RS485AI 并发不等于硬件可以并发
08Raw 接口被“优化”掉Tool 和协议边界不清
09AI 分析旧日志缺少 Run / Device / Firmware Context
10修一个 Bug 改 17 个文件缺少 Patch Budget 和 Rollback

写在最后

做 GaryCLI 以后,我越来越觉得:

嵌入式 AI Agent 最危险的,不是它告诉你“我不知道”。

真正危险的是:

它连接成功了
↓
命令发出去了
↓
代码也编译了
↓
然后告诉你:

“问题已经解决。”

但真实设备可能根本没有按预期工作。

所以我现在对 GaryCLI 的目标也越来越明确。

不是单纯做一个:

AI + CLI

也不是再做一个:

帮你生成 C/C++ 代码的 AI 助手

我更希望它最终变成:

AI
+
Embedded Toolchain
+
Device Context
+
Hardware Verification Loop

让 AI 真正进入:

整个嵌入式研发流程。

换句话说:

GaryCLI 想做的,不是让 AI “会写嵌入式代码”,而是让 AI 真正有能力参与嵌入式研发。

从:

Coding Assistant

走向:

Embedded AI Engineer

这条路肯定还有很多坑。

但现在看来,真正有价值的东西,可能恰恰就藏在这些坑里面。


关于 GaryCLI

GaryCLI 是我目前正在开发的一个面向嵌入式研发场景的 AI CLI Agent。

希望逐步把这些原本比较割裂的动作连接起来:

代码分析
+
代码修改
+
Build
+
Flash
+
Serial
+
BLE
+
RS485
+
Modbus
+
设备诊断
+
真实硬件验证

形成:

需求
→
分析
→
修改
→
执行
→
验证
→
再分析

的完整闭环。

后面我也准备继续整理 GaryCLI 开发过程中的一些真实问题,比如:

AI 怎么自动识别嵌入式设备?

Agent Tool 到底应该怎么设计?

AI 怎么安全操作串口和 RS485?

怎么让 AI 自动分析 ESP32 Crash / Backtrace?

AI 怎么理解 Modbus 寄存器表?

怎么构建真正的 Hardware Verification Loop?

嵌入式 Agent 怎么避免“自信地判断错误”?

如果你也在做:

Embedded AI
AI Agent
ESP32
BLE
RS485
Modbus
嵌入式自动化测试
AI Coding Tool

这些方向,欢迎一起交流。

代码写出来只是第一步,真实硬件验证通过,才算真正完成。

更多推荐