Projects · · 5 分钟阅读

把 Agent 装进桌面宠物:Orange Pi + ESP32 实验

用 Orange Pi 运行 Agent 与语音服务,让 ESP32 专注屏幕、麦克风、扬声器和动作控制。

Orange Pi ESP32 Hermes AI Agent 桌面宠物

最近我在做一个桌面智能宠物。

它有一块圆形屏幕,可以显示表情、字幕、时间和服务状态;它可以听见我说话、用语音回复,也可以在未来通过旋转、点头和摇头表达动作。

但我真正想做的,并不是一个缩小版的智能音箱,而是 Agent 在现实世界中的一个身体

从聊天窗口走到桌面上

现在的大多数 Agent 都生活在聊天窗口里:输入一段文字,等待搜索、分析或执行,再看结果。这种方式适合复杂内容,却不适合所有场景。

例如:

  • 一个长时间运行的任务刚刚完成
  • 博客服务器出现异常
  • GitHub 构建失败
  • 收到一封需要尽快处理的邮件
  • 下一场会议即将开始
  • Codex 或其他 Agent 正在执行某个步骤
  • 家里的设备状态发生变化

这些信息不值得专门打开电脑,却适合通过桌面上的常驻设备轻量呈现。

于是,这个桌面宠物逐渐从「会说话的硬件」变成了一个现实世界中的 Agent 终端。

为什么使用两块计算设备

核心问题是:所有事情都交给谁做?

如果全部交给 ESP32,它既要处理屏幕、麦克风和扬声器,又要连接云端 ASR、TTS 和大模型,还要维护复杂会话状态,固件会迅速变得难以维护。

如果全部交给 Orange Pi,屏幕刷新、实时音频、触摸和电机控制都要直接依赖 Linux,设备结构会更复杂。

最终采用两层架构:

text
云端 ASR / TTS / 模型 / 外部服务
                │
                ▼
      Orange Pi Zero 3W
  Agent、会话、插件、设备服务
                │
         Wi-Fi / WebSocket
                │
                ▼
            ESP32-S3
 屏幕、触摸、麦克风、扬声器、电机

边界很明确:Orange Pi 负责理解、决策和连接;ESP32 负责感知、表达和执行。

Orange Pi:Agent 的运行时

Orange Pi Zero 3W 运行 Linux,承担相对复杂的部分:

  • 连接网络
  • 运行 Hermes 或其他 Agent Runtime
  • 对接豆包 Seed-ASR 2.0 / Seed-TTS 2.0
  • 保存会话上下文
  • 执行工具和插件
  • 管理 ESP32 设备连接
  • 提供 Web 管理后台
  • 管理长期记忆、定时任务和安全策略

这里有一个重要判断:

Agent Runtime 不等于完整服务端。

Hermes 或 Pi SDK 可以负责 Agent Loop、工具决策和上下文,但设备管理、WebSocket、ASR/TTS、用户系统、插件管理和安全,仍然应由单独的服务端模块承担。

因此 Orange Pi 既是「大脑所在的设备」,也是本地服务节点,内部仍需清晰的模块边界。

ESP32:保持简单的受控终端

ESP32-S3 不负责理解用户意图,主要完成:

  • I2S 麦克风采集与扬声器播放
  • 圆形屏幕刷新、字幕与表情
  • 按键和触摸事件
  • 电量、温度等状态采集
  • 电机和灯光控制
  • 本地唤醒词
  • 断网时的基础状态展示

服务端下发语义化指令,例如:

json
{
  "type": "display.expression",
  "payload": {
    "name": "thinking",
    "loop": true
  }
}

或:

json
{
  "type": "motion.perform",
  "payload": {
    "name": "nod",
    "intensity": 0.6
  }
}

ESP32 再把语义动作转换为帧动画、电机角度或音频操作。固件不必知道背后的模型来自哪里,也不必关心 Agent 用了什么工具。

一次完整的语音对话

当前确定的语音链路:

  1. 用户点击宠物,或说出唤醒词
  2. ESP32 开启 I2S 麦克风
  3. 每 100ms 将 PCM 发给 Orange Pi
  4. Orange Pi 转发至豆包 Seed-ASR 2.0
  5. ASR 持续返回增量识别结果
  6. Orange Pi 将 partial 字幕推给 ESP32
  7. 检测到约 800ms 静音
  8. 等待最终识别文本,交给 Hermes
  9. Hermes 返回 silentexecutereply
  10. 若是 reply,文本流式发送给 Seed-TTS 2.0
  11. Orange Pi 将回复字幕和音频同步推给 ESP32
  12. 播放完成后恢复待机

最重要的不是「回答一句话」,而是让用户始终知道设备处于什么阶段:

text
idle → listening → recognizing → thinking
     → executing → speaking → idle

说话时显示实时字幕;调用工具时显示「正在检查服务器」;等待 TTS 时显示思考动画;开始播放再切到说话状态。语音交互就不该是一个不可见的黑盒。

为什么优先使用 Wi-Fi 直连

我希望 Orange Pi 一边连外部 Wi-Fi,一边为 ESP32 提供独立热点:

text
互联网
  │
家庭或办公室 Wi-Fi
  │
Orange Pi(STA + AP)
  │
ESP32

ESP32 不必直接暴露在家庭网络中,所有通信都经过 Orange Pi。蓝牙可用于首次配网或备用连接,但不适合作为持续传输 PCM、字幕和状态的主链路。

桌面宠物不应该一直说话

语音助手里容易被忽略的问题是:什么时候不回复?

如果随口一句话也每次正式回答,设备很快会变得吵闹。因此 Hermes 最终需要三类决策:

json
{ "action": "silent" }
json
{
  "action": "execute",
  "tool": "device.motion",
  "arguments": { "name": "turn_right" }
}
json
{
  "action": "reply",
  "text": "服务器运行正常,当前温度是 52 摄氏度。"
}

silent 不是失败,而是一种产品能力。有陪伴感的设备,不只学会回答,也要学会安静地待在旁边。

未来的插件化方向

管理后台应以插件方式接入外部服务。插件不直接操作屏幕,而是上报结构化状态:

json
{
  "source": "github",
  "level": "warning",
  "title": "部署失败",
  "message": "blog-main 构建未通过"
}

设备策略再决定是否立刻展示、是否播放提示音、用哪种表情、是否需要确认、是否允许语音追问。

未来可接入:GitHub Actions、博客服务器、Codex 任务、邮件与日历、天气、家庭设备、Agent 长任务进度。

到这一步,桌面宠物就不再只是语音设备,而是可观察、可控制的 Agent 现实终端。

最难的部分其实不是模型

真正困难的并不是调用 ASR、TTS 或大模型,而是大量细节:

  • 网络抖动时音频不能断
  • 实时字幕不能频繁跳动
  • 设备重连后状态需要恢复
  • 指令必须支持 ACK 和超时
  • 电机动作不能与语音播放冲突
  • Agent 执行工具时必须有明确反馈
  • 屏幕空间有限,不能堆太多文字
  • 不同硬件版本需要能力发现
  • API 密钥不能下发到 ESP32
  • 断网时也应保留基本交互

模型只是能力来源,系统设计才决定它是否真的可用。

写在最后

我想做的,并不是一个「可以接入大模型」的硬件外壳。

我更关心:当 Agent 从聊天窗口走到桌面之后,它应该如何被看见、被理解和被信任——什么时候听见了我,什么时候在思考,什么时候正在执行,又什么时候选择不打扰我。

屏幕、语音、动作和状态,最终都在回答同一个问题:

一个真正存在于现实环境中的 Agent,应该是什么样子?