child-psycho-companion/DEPLOYMENT_GUIDE_LOCAL.md

403 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 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 KeyMiniMax 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 |