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