403 lines
12 KiB
Markdown
403 lines
12 KiB
Markdown
|
|
# xiaozhi-esp32-server 全模块本地部署指南
|
|||
|
|
|
|||
|
|
> **目标平台:** MacBook Pro M1 Pro (ARM64, 32GB)
|
|||
|
|
> **系统:** macOS Darwin 24.6.0 (arm64)
|
|||
|
|
> **适用范围:** 从当前"仅 server 模块"升级为"server + manager-api + manager-web + MySQL + Redis"全模块
|
|||
|
|
> **生成时间:** 2026-04-06
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 0. 背景与目标
|
|||
|
|
|
|||
|
|
**当前问题:** xiaozhi-server 的 LLM 配置被 xinnan-tech 云端 manager-api 覆盖,实际使用了默认的 `glm-4-flash` 而非 MiniMax,导致 401 错误。
|
|||
|
|
|
|||
|
|
**解决思路:** 升级到全模块部署,智控台配置保存在本地 MySQL 数据库,MiniMax API Key 由智控台管理,不再被云端覆盖。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. 环境概览
|
|||
|
|
|
|||
|
|
### 当前状态
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
/Users/bigemon/WorkSpace/xiaozhi-server/
|
|||
|
|
├── docker-compose.yml ← 单模块(server only)
|
|||
|
|
├── data/
|
|||
|
|
│ ├── .config.yaml ← MiniMax 配置(被云端覆盖)
|
|||
|
|
│ └── .mcp_server_settings.json
|
|||
|
|
└── models/
|
|||
|
|
├── SenseVoiceSmall/model.pt/ ← 目录,模型在 nested 路径
|
|||
|
|
├── snakers4_silero-vad/
|
|||
|
|
└── mcp-endpoint-server/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 部署后架构
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
┌─────────────────────────────────────────────────────────┐
|
|||
|
|
│ MacBook Pro M1 Pro (32GB) │
|
|||
|
|
│ │
|
|||
|
|
│ xiaozhi-esp32-server 8000,8003/tcp (server) │
|
|||
|
|
│ xiaozhi-esp32-server-web 8002/tcp (智控台) │
|
|||
|
|
│ xiaozhi-esp32-server-db 3306/tcp (MySQL) │
|
|||
|
|
│ xiaozhi-esp32-server-redis 6379/tcp (Redis) │
|
|||
|
|
│ │
|
|||
|
|
│ docker.for.mac.host.internal ← 容器访问宿主机 │
|
|||
|
|
└─────────────────────────────────────────────────────────┘
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ARM64 兼容性确认
|
|||
|
|
|
|||
|
|
| 镜像 | 状态 | 备注 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| `server_latest` | ✅ ARM64 | `ghcr.nju.edu.cn/xinnan-tech` 提供 |
|
|||
|
|
| `web_latest` | ✅ ARM64 | Spring Boot + Vue,无平台依赖 |
|
|||
|
|
| `mysql:latest` | ⚠️ 基本支持 | 建议改用 `mysql:8.0` |
|
|||
|
|
| `redis:8.0` | ✅ ARM64 | 官方多架构镜像 |
|
|||
|
|
| FunASR SenseVoiceSmall | ✅ 支持 | PyTorch CPU 模式 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. 前置检查
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1. 端口检查(8002 必须空闲)
|
|||
|
|
lsof -i :8002 2>/dev/null | grep LISTEN || echo "8002 空闲 ✅"
|
|||
|
|
|
|||
|
|
# 2. 确认 Docker 可用
|
|||
|
|
docker --version && docker compose version
|
|||
|
|
|
|||
|
|
# 3. 确认现有容器状态
|
|||
|
|
docker ps --format "{{.Names}}\t{{.Status}}" | grep xiaozhi
|
|||
|
|
|
|||
|
|
# 4. 确认模型文件存在
|
|||
|
|
ls /Users/bigemon/WorkSpace/xiaozhi-server/models/SenseVoiceSmall/model.pt/
|
|||
|
|
# 应看到 SenseVoiceSmall.pt 或 nested 目录
|
|||
|
|
|
|||
|
|
# 5. 备份现有配置
|
|||
|
|
cp /Users/bigemon/WorkSpace/xiaozhi-server/data/.config.yaml \
|
|||
|
|
/Users/bigemon/WorkSpace/xiaozhi-server/data/.config.yaml.bak.$(date +%Y%m%d%H%M%S)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. 详细部署步骤
|
|||
|
|
|
|||
|
|
### Step 1:建立目录结构
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /Users/bigemon/WorkSpace/xiaozhi-server
|
|||
|
|
|
|||
|
|
# MySQL 数据目录
|
|||
|
|
mkdir -p mysql/data
|
|||
|
|
|
|||
|
|
# 最终目录结构应为:
|
|||
|
|
# xiaozhi-server/
|
|||
|
|
# ├── docker-compose_all.yml ← 新增
|
|||
|
|
# ├── docker-compose.yml ← 旧文件(可保留备份)
|
|||
|
|
# ├── data/
|
|||
|
|
# │ └── .config.yaml
|
|||
|
|
# ├── models/
|
|||
|
|
# └── mysql/
|
|||
|
|
# └── data/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 2:下载 docker-compose_all.yml
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /Users/bigemon/WorkSpace/xiaozhi-server
|
|||
|
|
|
|||
|
|
# 下载全模块编排文件
|
|||
|
|
wget -q https://raw.githubusercontent.com/xinnan-tech/xiaozhi-esp32-server/refs/heads/main/main/xiaozhi-server/docker-compose_all.yml \
|
|||
|
|
-O docker-compose_all.yml
|
|||
|
|
|
|||
|
|
# 验证下载
|
|||
|
|
head -20 docker-compose_all.yml
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 3:修复 MySQL 镜像版本(避免 latest 不稳定问题)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 将 mysql:latest 改为 mysql:8.0
|
|||
|
|
sed -i '' 's|image: mysql:latest|image: mysql:8.0|' docker-compose_all.yml
|
|||
|
|
|
|||
|
|
# 确认修改
|
|||
|
|
grep "image:" docker-compose_all.yml
|
|||
|
|
# 预期:mysql:8.0 ✅
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 4:修复 FunASR 模型文件路径
|
|||
|
|
|
|||
|
|
> **问题:** `models/SenseVoiceSmall/model.pt` 是**目录**,但 docker-compose volume 挂载期望它是**文件**。
|
|||
|
|
> **实际模型路径:** `models/SenseVoiceSmall/model.pt/SenseVoiceSmall.pt`
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /Users/bigemon/WorkSpace/xiaozhi-server
|
|||
|
|
|
|||
|
|
# 查看实际模型文件位置
|
|||
|
|
ls -la models/SenseVoiceSmall/model.pt/
|
|||
|
|
# 预期:drwxr-xr-x SenseVoiceSmall.pt(或其他 .pt 文件)
|
|||
|
|
|
|||
|
|
# 移动到正确路径
|
|||
|
|
PT_FILE=$(ls models/SenseVoiceSmall/model.pt/*.pt 2>/dev/null | head -1)
|
|||
|
|
if [ -n "$PT_FILE" ]; then
|
|||
|
|
mv "$PT_FILE" models/SenseVoiceSmall/model.pt.file
|
|||
|
|
ln -sf model.pt.file models/SenseVoiceSmall/model.pt
|
|||
|
|
echo "✅ 模型文件已移动:$(basename $PT_FILE) → model.pt.file"
|
|||
|
|
else
|
|||
|
|
echo "⚠️ 未找到 .pt 模型文件,请手动检查 models/SenseVoiceSmall/model.pt/"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# 验证
|
|||
|
|
ls -lh models/SenseVoiceSmall/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 5:准备初始 .config.yaml
|
|||
|
|
|
|||
|
|
> **关键原则:** `manager-api.secret` 先留空,等智控台启动后从页面获取再填入。
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /Users/bigemon/WorkSpace/xiaozhi-server/data
|
|||
|
|
|
|||
|
|
cat > .config.yaml << 'EOF'
|
|||
|
|
server:
|
|||
|
|
ip: 0.0.0.0
|
|||
|
|
port: 8000
|
|||
|
|
http_port: 8003
|
|||
|
|
# vision_explain: http://192.168.1.194:8003/mcp/vision/explain # 全模块后由智控台管理
|
|||
|
|
|
|||
|
|
manager-api:
|
|||
|
|
url: http://xiaozhi-esp32-server-web:8002/xiaozhi
|
|||
|
|
secret: "" # ← 先留空,智控台启动后从这里获取并填入
|
|||
|
|
|
|||
|
|
# MCP 接入点(保持不变)
|
|||
|
|
mcp_endpoint: ws://mcp-endpoint-server:8004/mcp_endpoint/mcp/?token=usxQ%2BQ7GkvPsndgGVZDZBCE1%2Bcp6w1dJKkmoj7EJCqM%3D
|
|||
|
|
EOF
|
|||
|
|
|
|||
|
|
echo "✅ .config.yaml 初始版本已创建"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 6:停止现有单模块容器
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 停止并移除旧容器(约 1-3 秒服务中断)
|
|||
|
|
docker stop xiaozhi-esp32-server && docker rm xiaozhi-esp32-server
|
|||
|
|
|
|||
|
|
echo "✅ 旧容器已清理"
|
|||
|
|
echo "⚠️ ESP32 设备将在新容器启动后自动重连(端口不变)"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 7:启动全模块 Docker Compose
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /Users/bigemon/WorkSpace/xiaozhi-server
|
|||
|
|
|
|||
|
|
# 启动全部 4 个容器
|
|||
|
|
docker compose -f docker-compose_all.yml up -d
|
|||
|
|
|
|||
|
|
# 确认 4 个容器都在运行
|
|||
|
|
docker ps --format "{{.Names}}\t{{.Status}}" | grep xiaozhi
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**预期输出:**
|
|||
|
|
```
|
|||
|
|
xiaozhi-esp32-server Up
|
|||
|
|
xiaozhi-esp32-server-web Up
|
|||
|
|
xiaozhi-esp32-server-db Up (healthy)
|
|||
|
|
xiaozhi-esp32-server-redis Up (healthy)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 8:等待 MySQL + Redis 健康检查
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 等待 MySQL 健康检查(最多 60 秒)
|
|||
|
|
echo "等待 MySQL 就绪..."
|
|||
|
|
for i in $(seq 1 20); do
|
|||
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' xiaozhi-esp32-server-db 2>/dev/null)
|
|||
|
|
[ "$status" = "healthy" ] && echo "✅ MySQL 已就绪 (${i}0s)" && break
|
|||
|
|
echo " 尝试 ${i}/20,状态: ${status:-starting}..."
|
|||
|
|
sleep 3
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
# 等待 Redis
|
|||
|
|
echo "等待 Redis 就绪..."
|
|||
|
|
for i in $(seq 1 10); do
|
|||
|
|
status=$(docker inspect --format='{{.State.Health.Status}}' xiaozhi-esp32-server-redis 2>/dev/null)
|
|||
|
|
[ "$status" = "healthy" ] && echo "✅ Redis 已就绪 (${i}0s)" && break
|
|||
|
|
echo " 尝试 ${i}/10,状态: ${status:-starting}..."
|
|||
|
|
sleep 2
|
|||
|
|
done
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 9:验证智控台启动成功
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker logs xiaozhi-esp32-server-web 2>&1 | tail -10
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**成功标志:**
|
|||
|
|
```
|
|||
|
|
Started AdminApplication in 16.057 seconds (process running for 17.941)
|
|||
|
|
http://localhost:8002/xiaozhi/doc.html
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 10:注册智控台管理员账号
|
|||
|
|
|
|||
|
|
用浏览器打开:**http://127.0.0.1:8002**
|
|||
|
|
|
|||
|
|
1. 点击**注册**,创建第一个账号
|
|||
|
|
2. **第一个注册的账号自动成为超级管理员**
|
|||
|
|
3. 超级管理员权限:模型管理、用户管理、参数配置
|
|||
|
|
|
|||
|
|
### Step 11:配置 server.secret
|
|||
|
|
|
|||
|
|
1. 登录智控台 → **参数管理**
|
|||
|
|
2. 找到 `server.secret`,复制参数值
|
|||
|
|
3. 填入 `data/.config.yaml`:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 将 SECRET_VALUE 替换为从智控台复制的值
|
|||
|
|
sed -i '' "s|secret: \"\"|secret: \"YOUR_SECRET_VALUE\"|" \
|
|||
|
|
/Users/bigemon/WorkSpace/xiaozhi-server/data/.config.yaml
|
|||
|
|
|
|||
|
|
# 确认
|
|||
|
|
grep "secret:" /Users/bigemon/WorkSpace/xiaozhi-server/data/.config.yaml
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 12:配置 MiniMax LLM
|
|||
|
|
|
|||
|
|
1. 登录智控台 → **模型配置** → **大语言模型**
|
|||
|
|
2. 新增或修改 MiniMax 配置:
|
|||
|
|
- 类型:`OpenAI 兼容`
|
|||
|
|
- 模型名:`MiniMax-M2.5` 或 `MiniMax-M2.7`
|
|||
|
|
- API 地址:`https://api.minimaxi.com/v1`
|
|||
|
|
- API Key:MiniMax Token Plan Key
|
|||
|
|
3. 保存
|
|||
|
|
|
|||
|
|
### Step 13:配置 MiniMax TTS(如需)
|
|||
|
|
|
|||
|
|
1. 智控台 → **模型配置** → **语音合成**
|
|||
|
|
2. 选择 `MinimaxTTS` 或继续使用 `EdgeTTS`(免费无需 Key)
|
|||
|
|
|
|||
|
|
### Step 14:配置 server.websocket 和 server.ota
|
|||
|
|
|
|||
|
|
> 全模块部署后,ESP32 固件需要从智控台读取这两个地址。
|
|||
|
|
|
|||
|
|
1. 登录智控台 → **参数管理**
|
|||
|
|
2. 找到 `server.websocket`,填入:
|
|||
|
|
```
|
|||
|
|
ws://192.168.1.194:8000/xiaozhi/v1/
|
|||
|
|
```
|
|||
|
|
3. 找到 `server.ota`,填入:
|
|||
|
|
```
|
|||
|
|
http://192.168.1.194:8002/xiaozhi/ota/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> ⚠️ OTA 端口是 **8002**(智控台),不是 8003!
|
|||
|
|
|
|||
|
|
### Step 15:重启 server 容器
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
docker restart xiaozhi-esp32-server
|
|||
|
|
docker logs -f xiaozhi-esp32-server 2>&1 | head -20
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**成功标志:**
|
|||
|
|
```
|
|||
|
|
Websocket地址是 ws://192.168.1.194:8000/xiaozhi/v1/
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### Step 16:验证 MiniMax LLM
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 测试玩偶对话(用修复后的 CLI)
|
|||
|
|
cd /Users/bigemon/WorkSpace/child-psycho-companion
|
|||
|
|
source .venv/bin/activate
|
|||
|
|
python xiaozhi_cli_client.py "你好"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
观察日志中是否还有 `401` 错误。如成功,玩偶应正常回复。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. 验证清单
|
|||
|
|
|
|||
|
|
| 验证项 | 命令/方法 | 成功标准 |
|
|||
|
|
|--------|---------|---------|
|
|||
|
|
| MySQL 健康 | `docker inspect xiaozhi-esp32-server-db -f '{{.State.Health.Status}}'` | `healthy` |
|
|||
|
|
| Redis 健康 | `docker inspect xiaozhi-esp32-server-redis -f '{{.State.Health.Status}}'` | `healthy` |
|
|||
|
|
| 智控台 | 浏览器打开 `http://127.0.0.1:8002` | 能注册/登录 |
|
|||
|
|
| MiniMax LLM | `python xiaozhi_cli_client.py "测试"` | 无 401 错误,玩偶回复 |
|
|||
|
|
| WS 服务 | `curl -s --max-time 3 http://127.0.0.1:8003/xiaozhi/v1/` | 连接建立 |
|
|||
|
|
| MCP Endpoint | `docker logs xiaozhi-esp32-server 2>&1 \| grep MCP` | 连接成功 |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 5. 回滚步骤
|
|||
|
|
|
|||
|
|
### 情况 A:全模块启动后 server 连接 manager-api 失败
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 1. 确认 secret 已正确填写
|
|||
|
|
grep "secret:" /Users/bigemon/WorkSpace/xiaozhi-server/data/.config.yaml
|
|||
|
|
|
|||
|
|
# 2. 仅重启 server
|
|||
|
|
docker restart xiaozhi-esp32-server
|
|||
|
|
docker logs -f xiaozhi-esp32-server
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 情况 B:完全回滚到单模块(服务中断最小化)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /Users/bigemon/WorkSpace/xiaozhi-server
|
|||
|
|
|
|||
|
|
# 停止全模块(保留 volume)
|
|||
|
|
docker compose -f docker-compose_all.yml stop
|
|||
|
|
|
|||
|
|
# 恢复旧单模块容器
|
|||
|
|
docker run -d \
|
|||
|
|
--name xiaozhi-esp32-server \
|
|||
|
|
--restart always \
|
|||
|
|
--security-opt seccomp:unconfined \
|
|||
|
|
-e TZ=Asia/Shanghai \
|
|||
|
|
-p 8000:8000 -p 8003:8003 \
|
|||
|
|
-v /Users/bigemon/WorkSpace/xiaozhi-server/data:/opt/xiaozhi-esp32-server/data \
|
|||
|
|
ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest
|
|||
|
|
|
|||
|
|
# 恢复网络连接
|
|||
|
|
docker network connect mcp-endpoint-server_default xiaozhi-esp32-server 2>/dev/null || true
|
|||
|
|
docker network connect xiaozhi-server_default xiaozhi-esp32-server 2>/dev/null || true
|
|||
|
|
|
|||
|
|
echo "✅ 已回滚到单模块"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 情况 C:完全恢复(保留 MySQL 数据)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
cd /Users/bigemon/WorkSpace/xiaozhi-server
|
|||
|
|
|
|||
|
|
# 停止全模块(保留 MySQL 数据 volume)
|
|||
|
|
docker compose -f docker-compose_all.yml down
|
|||
|
|
|
|||
|
|
# 启动单模块
|
|||
|
|
docker run -d \
|
|||
|
|
--name xiaozhi-esp32-server \
|
|||
|
|
--restart always \
|
|||
|
|
--security-opt seccomp:unconfined \
|
|||
|
|
-e TZ=Asia/Shanghai \
|
|||
|
|
-p 8000:8000 -p 8003:8003 \
|
|||
|
|
-v /Users/bigemon/WorkSpace/xiaozhi-server/data:/opt/xiaozhi-esp32-server/data \
|
|||
|
|
ghcr.nju.edu.cn/xinnan-tech/xiaozhi-esp32-server:server_latest
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 附录:已知问题与解决
|
|||
|
|
|
|||
|
|
| 问题 | 原因 | 解决 |
|
|||
|
|
|------|------|------|
|
|||
|
|
| MySQL 启动慢 | `latest` tag 构建不稳定 | 改用 `mysql:8.0` |
|
|||
|
|
| FunASR 模型 404 | volume 挂载期望文件,实际是目录 | 移动 .pt 文件到正确路径 |
|
|||
|
|
| LLM 401 | manager-api 云端配置覆盖本地 | 全模块后由智控台管理 |
|
|||
|
|
| OTA 地址写错端口 | 单模块用 8003,全模块用 8002 | 见 Step 14 |
|