Beetle(甲壳虫):把 AI Agent 搬到 ESP32 嵌入式芯片上

边缘设备上的 AI Agent,不只是概念

Beetle(甲壳虫)是一个开源的边缘 AI Agent 固件,跑在 ESP32-S3 芯片上。它不是一个玩具级的 demo——内部实现了完整的 ReAct 推理循环和工具调用,接了飞书、钉钉、Telegram 等六个即时通讯平台的消息收发,还做了一套持久化的长期记忆体系。整条链路已经跑通,可以 7×24 无人值守运行。

先说背景:为什么是 ESP32

想要一个 24 小时在线的 AI 助手,常见的做法是云服务器部署或者本地电脑常开。前者按月付费,数据托管在第三方;后者一台机器全天候运行,电费、风扇噪音、系统更新重启这些事都要操心。如果需求没那么重——不需要同时服务几百人,也不需要毫秒级响应——这两条路的成本其实都偏高。

ESP32-S3 是乐鑫的一颗 MCU,淘宝上带 16MB Flash、8MB PSRAM 的开发板大概三四十块钱。功耗不到一瓦,全年通电电费几块钱。体积比一张名片还小,插上 USB 供电就能跑,没有风扇、不怕灰、不用装操作系统。拿它来跑一个个人用的 AI Agent,从成本结构上就跟服务器方案完全不同。

当然,ESP32-S3 只有几 MB 可用内存,不可能在上面跑 LLM 推理。Beetle 的做法是把架构拆成两层:LLM 推理交给外部 API(支持 Anthropic、OpenAI 兼容接口、Ollama 本地模型等),芯片只跑 Agent 运行时——消息收发、上下文构建、工具调度、记忆存取、多通道分发这些逻辑。打个比方,LLM 是大脑,Beetle 是身体和神经系统。大脑可以换(切换不同的 LLM 供应商),身体一直在,低功耗待命,随时接收和执行。

六个平台,一个 Agent

Beetle 目前接了六个即时通讯通道:飞书、钉钉、企业微信、QQ 频道、Telegram,以及通用的 WebSocket。

这不是六个独立的机器人。所有通道的消息汇入同一条消息队列,由同一个 Agent 实例处理。飞书上有人问了一个问题,Agent 形成的记忆和上下文,在 Telegram 那边同样可以被调用。对于同时活跃在多个平台的人来说,这意味着不用在每个平台上分别配一套东西——一个 Beetle 覆盖所有入口。

各通道的接入方式各不相同:飞书走 WSS 长连接,钉钉用 Webhook,Telegram 支持 Long Polling 和 WebSocket 双模,QQ 频道接入需要 Ed25519 签名验证。这些协议差异全部在通道层内部消化,Agent 核心不感知也不关心消息从哪来。架构上新增一个平台只需要实现一个 MessageSink 接口然后注册进去,不动 Agent 和消息总线的代码。

不是聊天机器人,是 Agent

聊天机器人收到消息、调一次 LLM、返回结果,一问一答就结束了。Beetle 跑的是 ReAct 循环,处理逻辑不一样。

收到一条消息后,Agent 先构建完整上下文——系统提示、人格设定、用户偏好、长期记忆、当前会话摘要、历史消息——打包发给 LLM。LLM 返回的可能不是直接回复,而是一个工具调用请求。Agent 执行工具、拿到结果,把结果追加到上下文里再发给 LLM,LLM 再判断是继续调工具还是给出最终回复。这个循环可以跑多轮,直到任务完成。

内置的工具集覆盖面不小:

  • 信息获取类
    :网页抓取(fetch_url,自动截断到 4KB 防止内存溢出)、网络搜索(优先 Tavily,失败自动回退 Brave)、获取当前时间。

  • 任务执行类
    :Cron 定时任务(持久化存储,重启不丢)、定点提醒(最多 32 条)、HTTP POST 请求。

  • 数据存取类
    :键值存储(64 条 × 512 字节)、文件系统读写、会话摘要更新。

  • 设备控制类
    :GPIO 读写(可选编译),为后续接传感器和执行器留好了口。

  • 系统类
    :系统资源统计、图像分析。

一个具体场景:你跟 Beetle 说「帮我盯着某个招聘网站的职位列表页,每天上午九点查一次,如果有新增的岗位就把标题发到我飞书上」。Agent 收到后会调用 Cron 工具创建一个定时任务,每天九点触发时自动调用 fetch_url 抓取页面内容,跟上次的结果对比(存在键值存储里),有变化就通过飞书通道推送。整个流程不需要你写一行代码,全靠 Agent 自己拆解和编排。

记忆体系:不是每次从零开始

大多数聊天机器人的上下文是一次性的——关掉窗口就忘了。Beetle 在这上面花了不少功夫,做了一套多层次的持久化记忆体系。

SOUL(人格设定),最多 32KB,定义 Agent 的性格、说话方式、行为准则。你可以让它正经严肃,也可以让它活泼话多,全看你怎么写这份设定。

USER(用户偏好),最多 32KB,记录跟你相关的信息:你的工作习惯、常用工具、沟通偏好、忌讳话题。这些内容会作为系统上下文的一部分注入每次对话,Agent 不需要你反复交代「我喜欢简洁回复」或者「别给我发表情」。

MEMORY(长期记忆),最多 256KB,由 Agent 在对话过程中主动写入。它不是简单地把聊天记录存下来,而是 Agent 调用专门的工具,将它认为值得长期保留的信息提炼后写入。比如你提过一次「下个月要出差深圳」,Agent 判断这是有用信息就会主动记录,之后跟你聊其他事的时候可以关联上。

此外,每个聊天会话有独立的历史记录(JSONL 格式,最多 128 条,单条 4KB),还有会话摘要机制——Agent 会主动调用工具对当前会话生成摘要,下次对话时摘要作为上下文首条注入,即使历史消息被截断,核心信息也不丢。对话中被标记为重要的消息([MARK_IMPORTANT])在上下文截断时享有优先保留权。

这套记忆体系的实际效果是:你可以在飞书上跟它聊工作的事,在 Telegram 上聊生活的事,过了几周回来,它还记得之前的关键内容,不用你每次重新交代背景。

在嵌入式设备上保持稳定

功能做得再多,如果动不动挂掉,对一个无人值守的设备来说就没有意义。Beetle 在可靠性上下了不少功夫,这部分值得单独说说。

资源硬约束。所有关键资源都有明确上限:消息队列固定 8 条、单条消息最大 64KB、系统提示最大 32KB、会话历史最大 24KB、HTTP 响应体最大 512KB。这些不是随意拍的数字,而是根据 ESP32-S3 的内存规格反复调试后定下来的。有了硬约束,就不会因为某条异常消息撑爆内存导致整个设备重启。

故障隔离。如果某个通道(比如飞书)连续发送失败三次,Beetle 会对这个通道触发 60 秒熔断——暂停向它发送,但不影响其他通道正常工作。一个平台的 API 故障不会拖垮整个系统。

重试与持久化。Agent 处理消息失败时会指数退避重试,最多三次。如果此时内存紧张且队列已满,待重试的消息会落盘到闪存,设备恢复后重新加载。不会因为一次网络抖动就丢消息。

TLS 内存准入。HTTPS 请求需要 TLS 握手,这在 ESP32 上是个大内存消耗。Beetle 做了内存准入检查:当可用内存低于 45KB 时,拒绝发起新的 TLS 连接,防止内存不足导致整机崩溃。

LLM 多源回退。可以配置多个 LLM 来源,支持路由器(Router)加工作模型(Worker)的两阶段调用模式。路由器负责简单分流判断,工作模型处理正式推理,其中任何一个失败都会自动切换到备用源,而不是直接报错。

配置和管理

Beetle 的首次配置流程做了专门设计:上电后设备自动开启一个名为「Beetle」的 WiFi 热点(无密码),手机或电脑连上去,浏览器访问 beetle.local 进入配置页面。页面里可以填 WiFi 信息、LLM API 地址和密钥、各平台的接入凭证、配对码等。保存后设备自动重启并连接 WiFi,开始工作。

连上网之后,可以通过 mDNS 在局域网内用 beetle.local 访问管理页面,随时调整配置。所有写入操作都需要配对码保护,防止局域网内其他人随意修改。

后台还提供了一组 REST API:健康检查(队列深度、错误率、内存占用、WiFi 状态)、会话列表查看、SOUL / USER / MEMORY 三份记忆文件的在线编辑、OTA 固件更新检查。如果你是开发者想做二次集成,这些接口都是现成的。

接下来的方向

Beetle 的核心功能已经跑通,从项目规划来看,后续迭代集中在三个方向。

把上手门槛降到最低

现在刷固件需要搭建 Rust 工具链和 ESP-IDF 开发环境,对非嵌入式开发者来说门槛偏高。项目规划中的目标是:提供预编译好的 .bin 固件文件,配合浏览器端的 Web Serial API 实现在线烧录。最终体验应该是——拿到一块 ESP32-S3 开发板,打开一个网页,选择固件,点击写入,等几十秒,打开配置页填信息,完事。不需要安装任何开发工具。

硬件适配也在计划中。目前只支持 ESP32-S3(16MB Flash + PSRAM),后续会加入 ESP32-C3 等更低成本的芯片,以及针对 4MB Flash 小容量板子的精简编译方案,进一步拉低硬件门槛。

跨会话记忆召回

现有记忆体系已经够用,但有一个明显的短板:记忆的检索是线性的。当 MEMORY 文件积累到一定规模后,全量注入上下文不现实(会超长度限制),但不注入又可能错过有用信息。

项目文档里提到了两条探索路线:一条是云端方案——embedding 加向量数据库做语义检索;另一条是纯本地方案——自动提取关键词、维护一个轻量级的 TF-IDF 索引,根据当前对话内容匹配最相关的历史记忆片段注入上下文。后者更契合 Beetle 的边缘运行定位,但在芯片上做文本索引本身也是个不小的工程挑战。

如果这一步走通,Beetle 就不只是「记得住」,而是「想得起来」——三个月前在飞书上聊过的某个决定,能在今天 Telegram 的对话里被自动关联和引用。

从软件 Agent 到物理 Agent

跑在真实硬件上是 Beetle 跟所有纯软件聊天机器人方案最本质的区别。目前 GPIO 控制工具已经实现,Agent 可以读写板子上的引脚,这为接入物理世界打开了口子。

后续接上温湿度传感器,Agent 就能感知环境变化并主动推送提醒;接上继电器模块,就能控制台灯、风扇等小家电的开关;配合 Cron 定时任务,可以实现「每天下班时间自动开灯」「湿度低于 40% 提醒我开加湿器」这类场景。纯靠云端 API 或者电脑上的 Bot 程序,做不到这种跟物理环境直接交互的事。

再远一点,如果有多块 Beetle 板子部署在不同位置(客厅、办公室、车上),它们各自采集本地环境数据,通过同一个 LLM 源共享「大脑」,就构成了一个简单的分布式感知网络。这已经不只是「聊天助手」的范畴了。

总结

Beetle 做的事情可以用一句话概括:在一块几十块钱的芯片上,运行一个具备多平台接入、多工具调用和持久化记忆的 AI Agent。它不试图替代 ChatGPT 或任何云端方案,而是填了一个此前没太多人认真做的位置——低成本、低功耗、数据不出设备的私人 AI 助手。

项目代码已开源。如果你对边缘 AI、嵌入式 Agent、或者 Rust on ESP32 感兴趣,值得看一看。

《Beetle(甲壳虫):把 AI Agent 搬到 ESP32 嵌入式芯片上》有1条评论

发表评论