Update documentation (An MCP-based Chatbot)

This commit is contained in:
Xiaoxia
2025-06-04 04:17:49 +08:00
parent 336eff728d
commit 16ec95f6d5
25 changed files with 484 additions and 267 deletions

BIN
docs/mcp-based-graph.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

115
docs/mcp-usage.md Normal file
View File

@@ -0,0 +1,115 @@
# MCP 协议物联网控制用法说明
> 本文档介绍如何基于 MCP 协议实现 ESP32 设备的物联网控制。详细协议流程请参考 [`mcp-protocol.md`](./mcp-protocol.md)。
## 简介
MCPModel Context Protocol是新一代推荐用于物联网控制的协议通过标准 JSON-RPC 2.0 格式在后台与设备间发现和调用"工具"Tool实现灵活的设备控制。
## 典型使用流程
1. 设备启动后通过基础协议(如 WebSocket/MQTT与后台建立连接。
2. 后台通过 MCP 协议的 `initialize` 方法初始化会话。
3. 后台通过 `tools/list` 获取设备支持的所有工具(功能)及参数说明。
4. 后台通过 `tools/call` 调用具体工具,实现对设备的控制。
详细协议格式与交互请见 [`mcp-protocol.md`](./mcp-protocol.md)。
## 设备端工具注册方法说明
设备通过 `McpServer::AddTool` 方法注册可被后台调用的"工具"。其常用函数签名如下:
```cpp
void AddTool(
const std::string& name, // 工具名称,建议唯一且有层次感,如 self.dog.forward
const std::string& description, // 工具描述,简明说明功能,便于大模型理解
const PropertyList& properties, // 输入参数列表(可为空),支持类型:布尔、整数、字符串
std::function<ReturnValue(const PropertyList&)> callback // 工具被调用时的回调实现
);
```
- name工具唯一标识建议用"模块.功能"命名风格。
- description自然语言描述便于 AI/用户理解。
- properties参数列表支持类型有布尔、整数、字符串可指定范围和默认值。
- callback收到调用请求时的实际执行逻辑返回值可为 bool/int/string。
## 典型注册示例(以 ESP-Hi 为例)
```cpp
void InitializeTools() {
auto& mcp_server = McpServer::GetInstance();
// 例1无参数控制机器人前进
mcp_server.AddTool("self.dog.forward", "机器人向前移动", PropertyList(), [this](const PropertyList&) -> ReturnValue {
servo_dog_ctrl_send(DOG_STATE_FORWARD, NULL);
return true;
});
// 例2带参数设置灯光 RGB 颜色
mcp_server.AddTool("self.light.set_rgb", "设置RGB颜色", PropertyList({
Property("r", kPropertyTypeInteger, 0, 255),
Property("g", kPropertyTypeInteger, 0, 255),
Property("b", kPropertyTypeInteger, 0, 255)
}), [this](const PropertyList& properties) -> ReturnValue {
int r = properties["r"].value<int>();
int g = properties["g"].value<int>();
int b = properties["b"].value<int>();
led_on_ = true;
SetLedColor(r, g, b);
return true;
});
}
```
## 常见工具调用 JSON-RPC 示例
### 1. 获取工具列表
```json
{
"jsonrpc": "2.0",
"method": "tools/list",
"params": { "cursor": "" },
"id": 1
}
```
### 2. 控制底盘前进
```json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "self.chassis.go_forward",
"arguments": {}
},
"id": 2
}
```
### 3. 切换灯光模式
```json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "self.chassis.switch_light_mode",
"arguments": { "light_mode": 3 }
},
"id": 3
}
```
### 4. 摄像头翻转
```json
{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "self.camera.set_camera_flipped",
"arguments": {}
},
"id": 4
}
```
## 备注
- 工具名称、参数及返回值请以设备端 `AddTool` 注册为准。
- 推荐所有新项目统一采用 MCP 协议进行物联网控制。
- 详细协议与进阶用法请查阅 [`mcp-protocol.md`](./mcp-protocol.md)。

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

Before

Width:  |  Height:  |  Size: 94 KiB

After

Width:  |  Height:  |  Size: 94 KiB

View File

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 17 KiB

View File

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

Before

Width:  |  Height:  |  Size: 19 KiB

After

Width:  |  Height:  |  Size: 19 KiB

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 46 KiB

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

BIN
docs/v1/esp-hi.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

View File

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 35 KiB

View File

Before

Width:  |  Height:  |  Size: 47 KiB

After

Width:  |  Height:  |  Size: 47 KiB

View File

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

View File

@@ -1,4 +1,6 @@
以下是一份基于代码实现整理的 WebSocket 通信协议文档,概述客户端(设备与服务器之间如何通过 WebSocket 进行交互。该文档仅基于所提供的代码推断,实际部署时可能需要结合服务器端实现进行进一步确认或补充。
以下是一份基于代码实现整理的 WebSocket 通信协议文档,概述设备与服务器之间如何通过 WebSocket 进行交互。
该文档仅基于所提供的代码推断,实际部署时可能需要结合服务器端实现进行进一步确认或补充。
---
@@ -17,12 +19,15 @@
- 设置若干请求头(`Authorization`, `Protocol-Version`, `Device-Id`, `Client-Id`
- 调用 `Connect()` 与服务器建立 WebSocket 连接
3. **发送客户端 “hello 消息**
3. **设备端发送 "hello" 消息**
- 连接成功后,设备会发送一条 JSON 消息,示例结构如下:
```json
{
"type": "hello",
"version": 1,
"features": {
"mcp": true
},
"transport": "websocket",
"audio_params": {
"format": "opus",
@@ -32,22 +37,38 @@
}
}
```
- 其中 `"frame_duration"` 的值对应 `OPUS_FRAME_DURATION_MS`(例如 60ms
- 其中 `features` 字段为可选,内容根据设备编译配置自动生成。例如:`"mcp": true` 表示支持 MCP 协议
- `frame_duration` 的值对应 `OPUS_FRAME_DURATION_MS`(例如 60ms
4. **服务器回复 hello**
4. **服务器回复 "hello"**
- 设备等待服务器返回一条包含 `"type": "hello"` 的 JSON 消息,并检查 `"transport": "websocket"` 是否匹配。
- 服务器可选下发 `session_id` 字段,设备端收到后会自动记录。
- 示例:
```json
{
"type": "hello",
"transport": "websocket",
"session_id": "xxx",
"audio_params": {
"format": "opus",
"sample_rate": 24000,
"channels": 1,
"frame_duration": 60
}
}
```
- 如果匹配,则认为服务器已就绪,标记音频通道打开成功。
- 如果在超时时间(默认 10 秒)内未收到正确回复,认为连接失败并触发网络错误回调。
5. **后续消息交互**
- 设备端和服务器端之间可发送两种主要类型的数据:
1. **二进制音频数据**Opus 编码)
2. **文本 JSON 消息**用于传输聊天状态、TTS/STT 事件、IoT 命令等)
2. **文本 JSON 消息**用于传输聊天状态、TTS/STT 事件、MCP 协议消息等)
- 在代码里,接收回调主要分为:
- `OnData(...)`:
- 当 `binary` 为 `true` 时,认为是音频帧;设备会将其当作 Opus 数据进行解码。
- 当 `binary` 为 `false` 时,认为是 JSON 文本,需要在设备端用 cJSON 进行解析并做相应业务逻辑处理(见下文消息结构)。
- 当 `binary` 为 `false` 时,认为是 JSON 文本,需要在设备端用 cJSON 进行解析并做相应业务逻辑处理(如聊天、TTS、MCP 协议消息等)。
- 当服务器或网络出现断连,回调 `OnDisconnected()` 被触发:
- 设备会调用 `on_audio_channel_closed_()`,并最终回到空闲状态。
@@ -63,9 +84,9 @@
在建立 WebSocket 连接时,代码示例中设置了以下请求头:
- `Authorization`: 用于存放访问令牌,形如 `"Bearer <token>"`
- `Protocol-Version`: 固定示例中为 `"1"`
- `Device-Id`: 设备物理网卡 MAC 地址
- `Client-Id`: 设备 UUID可在应用中唯一标识设备
- `Protocol-Version`: 固定示例中为 `"1"`,与 hello 消息体内的 `version` 字段保持一致
- `Device-Id`: 设备物理网卡 MAC 地址
- `Client-Id`: 软件生成的 UUID擦除 NVS 或重新烧录完整固件会重置
这些头会随着 WebSocket 握手一起发送到服务器,服务器可根据需求进行校验、认证等。
@@ -75,15 +96,18 @@
WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及其对应业务逻辑。若消息里包含未列出的字段,可能为可选或特定实现细节。
### 3.1 客户端→服务器
### 3.1 设备端→服务器
1. **Hello**
- 连接成功后,由客户端发送,告知服务器基本参数。
- 连接成功后,由设备端发送,告知服务器基本参数。
- 例:
```json
{
"type": "hello",
"version": 1,
"features": {
"mcp": true
},
"transport": "websocket",
"audio_params": {
"format": "opus",
@@ -95,7 +119,7 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
```
2. **Listen**
- 表示客户端开始或停止录音监听。
- 表示设备端开始或停止录音监听。
- 常见字段:
- `"session_id"`:会话标识
- `"type": "listen"`
@@ -124,7 +148,8 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
- `reason` 值可为 `"wake_word_detected"` 或其他。
4. **Wake Word Detected**
- 用于客户端向服务器告知检测到唤醒词。
- 用于设备端向服务器告知检测到唤醒词。
- 在发送该消息之前,可提前发送唤醒词的 Opus 音频数据,用于服务器进行声纹检测。
- 例:
```json
{
@@ -135,69 +160,86 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
}
```
5. **IoT**
- 发送当前设备的物联网相关信息:
- **Descriptors**(描述设备功能、属性等)
- **States**(设备状态的实时更新)
- 例:
5. **MCP**
- 推荐用于物联网控制的新一代协议。所有设备能力发现、工具调用等均通过 type: "mcp" 的消息进行payload 内部为标准 JSON-RPC 2.0(详见 [MCP 协议文档](./mcp-protocol.md))。
- **设备端到服务器发送 result 的例子:**
```json
{
"session_id": "xxx",
"type": "iot",
"descriptors": { ... }
}
```
```json
{
"session_id": "xxx",
"type": "iot",
"states": { ... }
"type": "mcp",
"payload": {
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{ "type": "text", "text": "true" }
],
"isError": false
}
}
}
```
---
### 3.2 服务器→客户
### 3.2 服务器→设备
1. **Hello**
- 服务器端返回的握手确认消息。
- 必须包含 `"type": "hello"` 和 `"transport": "websocket"`。
- 可能会带有 `audio_params`,表示服务器期望的音频参数,或与客户端对齐的配置。
- 成功接收后客户端会设置事件标志,表示 WebSocket 通道就绪。
- 可能会带有 `audio_params`,表示服务器期望的音频参数,或与设备端对齐的配置。
- 服务器可选下发 `session_id` 字段,设备端收到后会自动记录。
- 成功接收后设备端会设置事件标志,表示 WebSocket 通道就绪。
2. **STT**
- `{"type": "stt", "text": "..."}`
- `{"session_id": "xxx", "type": "stt", "text": "..."}`
- 表示服务器端识别到了用户语音。(例如语音转文本结果)
- 设备可能将此文本显示到屏幕上,后续再进入回答等流程。
3. **LLM**
- `{"type": "llm", "emotion": "happy", "text": "😀"}`
- `{"session_id": "xxx", "type": "llm", "emotion": "happy", "text": "😀"}`
- 服务器指示设备调整表情动画 / UI 表达。
4. **TTS**
- `{"type": "tts", "state": "start"}`:服务器准备下发 TTS 音频,客户端进入 speaking 播放状态。
- `{"type": "tts", "state": "stop"}`:表示本次 TTS 结束。
- `{"type": "tts", "state": "sentence_start", "text": "..."}`
- `{"session_id": "xxx", "type": "tts", "state": "start"}`:服务器准备下发 TTS 音频,设备端进入 "speaking" 播放状态。
- `{"session_id": "xxx", "type": "tts", "state": "stop"}`:表示本次 TTS 结束。
- `{"session_id": "xxx", "type": "tts", "state": "sentence_start", "text": "..."}`
- 让设备在界面上显示当前要播放或朗读的文本片段(例如用于显示给用户)。
5. **IoT**
- `{"type": "iot", "commands": [ ... ]}`
- 服务器向设备发送物联网的动作指令,设备解析并执行(如打开灯、设置温度等)。
5. **MCP**
- 服务器通过 type: "mcp" 的消息下发物联网相关的控制指令或返回调用结果payload 结构同上。
- **服务器到设备端发送 tools/call 的例子:**
```json
{
"session_id": "xxx",
"type": "mcp",
"payload": {
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "self.light.set_rgb",
"arguments": { "r": 255, "g": 0, "b": 0 }
},
"id": 1
}
}
```
6. **音频数据:二进制帧**
- 当服务器发送音频二进制帧Opus 编码)时,客户端解码并播放。
- 若客户端正在处于 listening (录音)状态,收到的音频帧会被忽略或清空以防冲突。
- 当服务器发送音频二进制帧Opus 编码)时,设备端解码并播放。
- 若设备端正在处于 "listening" (录音)状态,收到的音频帧会被忽略或清空以防冲突。
---
## 4. 音频编解码
1. **客户端发送录音数据**
1. **设备端发送录音数据**
- 音频输入经过可能的回声消除、降噪或音量增益后,通过 Opus 编码打包为二进制帧发送给服务器。
- 如果客户端每次编码生成的二进制帧大小为 N 字节,则会通过 WebSocket 的 **binary** 消息发送这块数据。
- 如果设备端每次编码生成的二进制帧大小为 N 字节,则会通过 WebSocket 的 **binary** 消息发送这块数据。
2. **客户端播放收到的音频**
2. **设备端播放收到的音频**
- 收到服务器的二进制帧时,同样认定是 Opus 数据。
- 设备端会进行解码,然后交由音频输出接口播放。
- 如果服务器的音频采样率与设备不一致,会在解码后再进行重采样。
@@ -206,7 +248,7 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
## 5. 常见状态流转
以下简述设备端关键状态流转,与 WebSocket 消息对应:
以下为常见设备端关键状态流转,与 WebSocket 消息对应:
1. **Idle** → **Connecting**
- 用户触发或唤醒后,设备调用 `OpenAudioChannel()` → 建立 WebSocket 连接 → 发送 `"type":"hello"`。
@@ -223,12 +265,52 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
5. **Listening** / **Speaking** → **Idle**(遇到异常或主动中断)
- 调用 `SendAbortSpeaking(...)` 或 `CloseAudioChannel()` → 中断会话 → 关闭 WebSocket → 状态回到 Idle。
### 自动模式状态流转图
```mermaid
stateDiagram
direction TB
[*] --> kDeviceStateUnknown
kDeviceStateUnknown --> kDeviceStateStarting:初始化
kDeviceStateStarting --> kDeviceStateWifiConfiguring:配置WiFi
kDeviceStateStarting --> kDeviceStateActivating:激活设备
kDeviceStateActivating --> kDeviceStateUpgrading:检测到新版本
kDeviceStateActivating --> kDeviceStateIdle:激活完成
kDeviceStateIdle --> kDeviceStateConnecting:开始连接
kDeviceStateConnecting --> kDeviceStateIdle:连接失败
kDeviceStateConnecting --> kDeviceStateListening:连接成功
kDeviceStateListening --> kDeviceStateSpeaking:开始说话
kDeviceStateSpeaking --> kDeviceStateListening:结束说话
kDeviceStateListening --> kDeviceStateIdle:手动终止
kDeviceStateSpeaking --> kDeviceStateIdle:自动终止
```
### 手动模式状态流转图
```mermaid
stateDiagram
direction TB
[*] --> kDeviceStateUnknown
kDeviceStateUnknown --> kDeviceStateStarting:初始化
kDeviceStateStarting --> kDeviceStateWifiConfiguring:配置WiFi
kDeviceStateStarting --> kDeviceStateActivating:激活设备
kDeviceStateActivating --> kDeviceStateUpgrading:检测到新版本
kDeviceStateActivating --> kDeviceStateIdle:激活完成
kDeviceStateIdle --> kDeviceStateConnecting:开始连接
kDeviceStateConnecting --> kDeviceStateIdle:连接失败
kDeviceStateConnecting --> kDeviceStateListening:连接成功
kDeviceStateIdle --> kDeviceStateListening:开始监听
kDeviceStateListening --> kDeviceStateIdle:停止监听
kDeviceStateIdle --> kDeviceStateSpeaking:开始说话
kDeviceStateSpeaking --> kDeviceStateIdle:结束说话
```
---
## 6. 错误处理
1. **连接失败**
- 如果 `Connect(url)` 返回失败或在等待服务器 hello 消息时超时,触发 `on_network_error_()` 回调。设备会提示无法连接到服务或类似错误信息。
- 如果 `Connect(url)` 返回失败或在等待服务器 "hello" 消息时超时,触发 `on_network_error_()` 回调。设备会提示"无法连接到服务"或类似错误信息。
2. **服务器断开**
- 如果 WebSocket 异常断开,回调 `OnDisconnected()`
@@ -244,16 +326,18 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
- 如果令牌过期或无效,服务器可拒绝握手或在后续断开。
2. **会话控制**
- 代码中部分消息包含 `session_id`,用于区分独立的对话或操作。服务端可根据需要对不同会话做分离处理WebSocket 协议为空
- 代码中部分消息包含 `session_id`,用于区分独立的对话或操作。服务端可根据需要对不同会话做分离处理。
3. **音频负载**
- 代码里默认使用 Opus 格式,并设置 `sample_rate = 16000`,单声道。帧时长由 `OPUS_FRAME_DURATION_MS` 控制,一般为 60ms。可根据带宽或性能做适当调整。
- 代码里默认使用 Opus 格式,并设置 `sample_rate = 16000`,单声道。帧时长由 `OPUS_FRAME_DURATION_MS` 控制,一般为 60ms。可根据带宽或性能做适当调整。为了获得更好的音乐播放效果,服务器下行音频可能使用 24000 采样率。
4. **IoT 指令**
- `"type":"iot"` 的消息用户端代码对接 `thing_manager` 执行具体命令,因设备定制而不同。服务器端需确保下发格式与客户端保持一致
4. **物联网控制推荐 MCP 协议**
- 设备与服务器之间的物联网能力发现、状态同步、控制指令等,建议全部通过 MCP 协议type: "mcp")实现。原有的 type: "iot" 方案已废弃
- MCP 协议可在 WebSocket、MQTT 等多种底层协议上传输,具备更好的扩展性和标准化能力。
- 详细用法请参考 [MCP 协议文档](./mcp-protocol.md) 及 [MCP 物联网控制用法](./mcp-usage.md)。
5. **错误或异常 JSON**
- 当 JSON 中缺少必要字段,例如 `{"type": ...}`客户端会记录错误日志(`ESP_LOGE(TAG, "Missing message type, data: %s", data);`),不会执行任何业务。
- 当 JSON 中缺少必要字段,例如 `{"type": ...}`设备端会记录错误日志(`ESP_LOGE(TAG, "Missing message type, data: %s", data);`),不会执行任何业务。
---
@@ -261,11 +345,14 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
下面给出一个典型的双向消息示例(流程简化示意):
1. **客户端 → 服务器**(握手)
1. **设备端 → 服务器**(握手)
```json
{
"type": "hello",
"version": 1,
"features": {
"mcp": true
},
"transport": "websocket",
"audio_params": {
"format": "opus",
@@ -276,63 +363,68 @@ WebSocket 文本帧以 JSON 方式传输,以下为常见的 `"type"` 字段及
}
```
2. **服务器 → 客户端**(握手应答)
2. **服务器 → 设备端**(握手应答)
```json
{
"type": "hello",
"transport": "websocket",
"session_id": "xxx",
"audio_params": {
"format": "opus",
"sample_rate": 16000
}
}
```
3. **客户端 → 服务器**(开始监听)
3. **设备端 → 服务器**(开始监听)
```json
{
"session_id": "",
"session_id": "xxx",
"type": "listen",
"state": "start",
"mode": "auto"
}
```
同时客户端开始发送二进制帧Opus 数据)。
同时设备端开始发送二进制帧Opus 数据)。
4. **服务器 → 客户端**ASR 结果)
4. **服务器 → 设备端**ASR 结果)
```json
{
"session_id": "xxx",
"type": "stt",
"text": "用户说的话"
}
```
5. **服务器 → 客户端**TTS开始
5. **服务器 → 设备端**TTS开始
```json
{
"session_id": "xxx",
"type": "tts",
"state": "start"
}
```
接着服务器发送二进制音频帧给客户端播放。
接着服务器发送二进制音频帧给设备端播放。
6. **服务器 → 客户端**TTS结束
6. **服务器 → 设备端**TTS结束
```json
{
"session_id": "xxx",
"type": "tts",
"state": "stop"
}
```
客户端停止播放音频,若无更多指令,则回到空闲状态。
设备端停止播放音频,若无更多指令,则回到空闲状态。
---
## 9. 总结
本协议通过在 WebSocket 上层传输 JSON 文本与二进制音频帧完成功能包括音频流上传、TTS 音频播放、语音识别与状态管理、IoT 指令下发等。其核心特征:
本协议通过在 WebSocket 上层传输 JSON 文本与二进制音频帧完成功能包括音频流上传、TTS 音频播放、语音识别与状态管理、MCP 指令下发等。其核心特征:
- **握手阶段**:发送 `"type":"hello"`,等待服务器返回。
- **音频通道**:采用 Opus 编码的二进制帧双向传输语音流。
- **JSON 消息**:使用 `"type"` 为核心字段标识不同业务逻辑,包括 TTS、STT、IoT、WakeWord 等。
- **JSON 消息**:使用 `"type"` 为核心字段标识不同业务逻辑,包括 TTS、STT、MCP、WakeWord 等。
- **扩展性**:可根据实际需求在 JSON 消息中添加字段,或在 headers 里进行额外鉴权。
服务器与客户端需提前约定各类消息的字段含义、时序逻辑以及错误处理规则,方能保证通信顺畅。上述信息可作为基础文档,便于后续对接、开发或扩展。
服务器与设备端需提前约定各类消息的字段含义、时序逻辑以及错误处理规则,方能保证通信顺畅。上述信息可作为基础文档,便于后续对接、开发或扩展。