本仓库的目标是把 XiaoZhi(ESP32 语音入口) 与 OpenClaw(可审计的工具执行/多端节点/权限控制的大脑) 连起来,形成“语音入口 → 工具执行 → 结果回传”的闭环。
当前仓库内已有两类组件:
- shim(Node):把 XiaoZhi 的 MCP/WebSocket 适配为 OpenClaw 的 node.invoke(见
/src/bridge/shim.ts) - xiaozhi compatible server(最小原型):用于在本地替代
xiaozhi.me做联调(见/src/xiaozhi-server/server.ts)
更完整的架构/去云设计见:
- 官方默认数据流架构图:
doc/plans/2026-02-09-xiaozhi-official-architecture.md - 去除官方云设计:
doc/plans/2026-02-09-decloud-xiaozhi-design.md
前置:
- Node.js ≥ 22(OpenClaw 也要求 Node≥22)
在仓库的 src/ 目录:
npm install
npm run build
npm test该测试使用 Mock OpenClaw Gateway + Mock XiaoZhi Server 验证桥接闭环(见 /src/tests/shim.e2e.test.ts)。
cd src
npm run build
MYR2D2_XIAOZHI_SERVER_PORT=18080 npm run xiaozhi:server这会在 ws://127.0.0.1:18080 启动一个最小 WebSocket server,支持:
hellolisten start/stop(会回 mock 的stt/llm/tts状态)mcp(支持initialize、tools/list、tools/call的最小返回)
这一节描述“把 myr2d2 shim 当作 OpenClaw 的 node”来接入 Gateway,然后由 operator 调用 node.invoke。
安装并启动(示例):
npm install -g openclaw@latest
openclaw onboard --install-daemon
openclaw gateway --port 18789如果 Gateway 需要对非回环地址暴露,建议显式指定 token(OpenClaw 文档说明非回环 bind 必须 token):
见 OpenClaw 文档:gateway/network-model.md
在本仓库 src/ 目录:
cd src
npm install
npm run build
MYR2D2_GATEWAY_URL="ws://127.0.0.1:18789" \
MYR2D2_GATEWAY_TOKEN="<your-token>" \
MYR2D2_NODE_ID="myr2d2.xiaozhi" \
MYR2D2_XIAOZHI_WS_URL="ws://127.0.0.1:18080" \
MYR2D2_DECLARED_COMMANDS="mcp.initialize,mcp.tools.list,mcp.tools.call,edge.audio.stream.start,edge.audio.stream.stop" \
npm start说明:
MYR2D2_XIAOZHI_WS_URL可以指向你的真实 XiaoZhi 入口(自建 server / 设备侧 ws 入口),也可以先指向上面的本地兼容 server。- 你也可以用
MYR2D2_GATEWAY_PASSWORD替代 token(取决于 Gateway 配置)。
在运行 Gateway 的主机上:
openclaw nodes status
openclaw nodes list --connected如遇到“pending/需要 approve”的情况(更严格的配对模式下常见):
openclaw nodes pending
openclaw nodes approve <requestId>列出 MCP 工具:
openclaw nodes invoke --node "myr2d2.xiaozhi" --command "mcp.tools.list" --params "{}"调用 MCP 工具(示例):
openclaw nodes invoke --node "myr2d2.xiaozhi" --command "mcp.tools.call" \
--params '{"name":"self.get_device_status","arguments":{}}'控制音频流开关(先验收“控制 + session_id 绑定”,不要求真实音频质量):
openclaw nodes invoke --node "myr2d2.xiaozhi" --command "edge.audio.stream.start" --params "{}"
openclaw nodes invoke --node "myr2d2.xiaozhi" --command "edge.audio.stream.stop" --params "{}"路线 B 的核心是:ESP32 直接作为 OpenClaw node 连接 Gateway(控制面),同时通过 Audio Plane(数据面)上传/接收音频流。\n当前仓库提供 Phase-1 的 Mock 版本用于冻结协议与验收链路(无需写固件即可联调)。
openclaw gateway --port 18789如需对非回环暴露,请按 OpenClaw 文档配置 token:见 gateway/network-model.md
在本仓库 src/ 目录:
cd src
npm install
npm run build
MYR2D2_AUDIO_PLANE_PORT=18880 \
MYR2D2_AUDIO_PLANE_MOCK_TRANSCRIPT="你好" \
MYR2D2_AUDIO_PLANE_MOCK_TTS="收到" \
npm run audio:plane新开一个终端(仍在 src/):
MYR2D2_GATEWAY_URL="ws://127.0.0.1:18789" \
MYR2D2_NODE_ID="myr2d2.esp32" \
MYR2D2_AUDIO_PLANE_BASE_URL="ws://127.0.0.1:18880" \
MYR2D2_DECLARED_COMMANDS="audio.stream.start,audio.stream.stop,audio.play.start,audio.play.stop,device.wake.mock" \
npm run routeb:nodeopenclaw nodes list --connected
openclaw nodes invoke --node "myr2d2.esp32" --command "device.wake.mock" --params "{}"
openclaw nodes invoke --node "myr2d2.esp32" --command "audio.stream.start" --params '{"codec":"opus","sampleRate":16000,"channels":1}'说明:
audio.stream.start会返回session_id/uplink_url/downlink_url/token。\n- Phase-1 里 Audio Plane 会在收到 uplink 的首个二进制帧后,向 downlink 广播 mock 的asr.mock/tts.mock(以及一段 mock 二进制音频)。\n- 真实固件实现时:ESP32 在本地唤醒后连接 uplink 并推送音频帧,同时连接 downlink 播放 TTS 音频。
为了避免把“官方默认”和“本仓库当前实现/规划”混在一起,这里给出四张图:
- 图 A:官方默认(设备 ↔ xiaozhi.me ↔ 云模型):这条链路不依赖 OpenClaw。
- 图 B:接入 OpenClaw 的两种方式:\n - B1(当前仓库已实现):OpenClaw ↔ myr2d2 shim ↔(自建/直连的)XiaoZhi WebSocket/MCP\n - B2(规划中):xiaozhi.me 通过 MCP 调用 OpenClaw(需要额外实现 MCP Server Adapter)
- 图 C:路线 A(去云):设备仍跑 XiaoZhi 协议,但把
xiaozhi.me换成你自建的 XiaoZhi Compatible Server;OpenClaw 作为 Brain/工具执行层。 - 图 D:路线 B(更彻底):设备不再跑 XiaoZhi 协议,直接实现 OpenClaw Gateway WS,成为一个 OpenClaw node。
flowchart TB
subgraph Device["XiaoZhi 设备(ESP32)"]
Mic["采音/唤醒"]
Spk["播放"]
end
subgraph XiaoZhiCloud["xiaozhi.me 官方服务"]
Access["设备接入层\nWS/MQTT+UDP"]
ASR["ASR(流式)"]
Orchestrator["对话/工具编排"]
TTS["TTS(流式)"]
Access --> ASR --> Orchestrator --> TTS --> Access
end
subgraph Model["云模型供应商"]
LLM["LLM API\n(Qwen/DeepSeek/...)"]
end
Mic -->|音频帧| Access
Access -->|TTS 音频帧| Spk
Orchestrator -->|请求/上下文| LLM
LLM -->|结果| Orchestrator
flowchart TB
subgraph OpenClaw["OpenClaw(myr2d2 侧)"]
Gateway["OpenClaw Gateway\n(权限/审计/skills/nodes)"]
end
subgraph B1["B1:当前已实现(直连/自建 XiaoZhi 入口)"]
Shim["myr2d2 shim(node)"]
XLocal["XiaoZhi WS/MCP\n(自建 server / 直连入口)"]
Gateway <-->|WS control plane| Shim
Shim <-->|MCP tools/* + listen| XLocal
end
subgraph B2["B2:规划中(xiaozhi.me 通过 MCP 调用 OpenClaw)"]
CloudOrch["xiaozhi.me Tool Orchestrator"]
McpAdapter["myr2d2 MCP Server Adapter\n(计划实现)"]
CloudOrch -->|tools/list tools/call| McpAdapter --> Gateway
end
flowchart TB
subgraph Device["XiaoZhi 设备(ESP32)"]
Mic["采音/唤醒"]
Spk["播放"]
end
subgraph SelfHost["自建:XiaoZhi Compatible Server(你维护)"]
Access["设备接入层\nWS/MQTT+UDP(兼容 XiaoZhi)"]
AudioPipeline["媒体面\nOpus↔PCM + 缓冲/背压"]
ASR["ASR(可自建或云)"]
TTS["TTS(可自建或云)"]
Access <--> AudioPipeline
AudioPipeline --> ASR
TTS --> AudioPipeline
end
subgraph Brain["OpenClaw(Brain/工具执行)"]
Gateway["OpenClaw Gateway\n(会话/权限/审计/skills/nodes)"]
Nodes["Nodes(可选)\nmacOS/iOS/Android/Headless"]
Gateway <-->|node.invoke / node.event| Nodes
end
Mic -->|音频帧| Access
Access -->|TTS 音频帧| Spk
ASR -->|transcript| Gateway
Gateway -->|reply text / tool intents| TTS
flowchart TB
subgraph OpenClaw["OpenClaw(Brain)"]
Gateway["OpenClaw Gateway WS\n(控制面/权限/审计)"]
Operator["Operator/Channel\n(WebChat/CLI/IM)"]
Operator --> Gateway
end
subgraph ESP32Node["ESP32(实现 OpenClaw Node 协议)"]
WSClient["WS client + reconnect"]
Commands["node.invoke handlers\n(例如 gpio/motor/display/audio)"]
Events["node.event\n(状态/遥测/音频状态)"]
WSClient <--> Commands
WSClient <--> Events
end
Gateway <-->|connect + req/res/event| WSClient
Gateway -->|node.invoke.request| Commands
Events -->|node.event| Gateway
备注:
- 你要“去除 xiaozhi.me”,建议走 B1 并逐步把
XLocal演进为“XiaoZhi Compatible Server + 真实音频媒体面”;设计细节见doc/plans/2026-02-09-decloud-xiaozhi-design.md。