Runbook · 自部署

小智全栈自部署手册

在自己的机器上跑起小智服务端和四个配套服务。Linux / macOS / Windows 都适用—— 差异只在容器底座和「怎么让它一直活着」这两件事上,服务本身完全一样。

适用系统
Linux · macOS · Windows
服务数量
5 个(可按需取舍)
共同底座
Docker + Compose
上次实测
2026-08
00

先选机器

五个服务都是容器,跑在哪个系统上都行。真正决定体验的是两件事: 断电重启后能不能自己起来,以及CPU 够不够。

系统容器底座断电重启后评价
LINUXUbuntu / Debian Docker Engine(原生) 自动恢复,无需任何人登录 最省心,长期常驻首选
MACOS Docker Desktop 或 OrbStack 需要登录桌面;且默认会睡眠 开发调试方便,常驻要额外处理睡眠
WINWindows WSL2 + Docker Desktop 需要登录桌面 能用,但要多趟几个坑

如果你只是想跑起来别折腾

找一台常年开机的 Linux 机器(小主机 / NAS / 云服务器都行),装 Docker Engine, restart: unless-stopped 就是真正的无人值守——断电来电自己恢复,不需要任何人碰它。 其余两个系统的容器都跑在一层虚拟机里,而那层虚拟机由用户会话拉起。

B站转码服务对 CPU 有硬要求

它做的是实时转码(拉流 → 缩到 320×240 → JPEG 序列 + 音频重采样),持续吃 CPU。 实测挪到 NAS 上跑不动,只能放在性能够的机器上。 选机器时如果打算跑这个服务,别选低功耗弱核心的盒子;其余四个服务对 CPU 都不敏感。

要先准备好的账号

服务需要什么能不能后补
小智服务端火山引擎账号(ASR / TTS 分别开通)+ 一个 LLM 的 key能,但没有就不会说话
音乐自己定一组管理账号密码 + 两个令牌;YouTube 音源另需 cookies凭据必须先有,cookies 能后补
B站B站账号(扫码)能,免登录可用但画质受限
小宇宙小宇宙账号能
CLIProxyAPI上游账号的 OAuth 授权能
01

装 Docker

LINUXUbuntu / Debian

官方脚本,国内加 --mirror Aliyun
curl -fsSL https://get.docker.com | sudo sh -s -- --mirror Aliyun
sudo systemctl enable --now docker
sudo usermod -aG docker $USER    # 重新登录后生效,之后不用 sudo

配镜像加速,写 /etc/docker/daemon.json:

{ "registry-mirrors": ["https://docker.m.daocloud.io"] }
sudo systemctl restart docker

MACOS

Docker Desktop 或 OrbStack(更轻、启动快、省电,个人使用免费)。 镜像加速在图形界面的 Settings → Docker Engine 里填同样的 registry-mirrors。

WINWindows

需要先开 WSL2 再装 Docker Desktop,步骤和几个必踩的坑写在 帮别人部署那一版的第 03 节,这里不重复。只有一处值得单独说:

WSL2 开 mirrored 网络模式,省掉端口转发

默认的 NAT 模式下,WSL 里的容器端口在局域网上不可达,要做一堆 netsh portproxy。 改成 mirrored 后,容器端口直接就是宿主机端口,局域网设备能直连。 我自己那台就是这么跑的。在 %USERPROFILE%\.wslconfig 里写:

[wsl2]
networkingMode=mirrored

写完 wsl --shutdown 重启一次 WSL 生效。

mirrored 还顺手解决了另一件事:network_mode: host 的容器(B站转码服务就是) 在 NAT 模式下端口到不了局域网(host 网络绑的是 WSL 虚拟机,不是 Windows 主机), mirrored 之后就正常了。不想改网络模式的话,把那个服务改成 ports: ["2233:2233"] 也一样。

如果启用 WSL 那步报「错误 2 找不到指定的文件」

不是功能名或版本问题,是组件存储(CBS)被「系统清理 / 电脑瘦身」类软件破坏了—— 通常是 C:\Windows\WinSxS\Catalogs 整个目录被删。这种情况下 DISM 连 scanhealth 都会崩(它自己就死在损坏项上)。 判断和修法(重建目录 + 抄 ACL,几秒钟的事)在 帮别人部署那一版的坑四。

验证

docker version && docker compose version
docker run --rm docker.m.daocloud.io/library/hello-world
02

国内网络换源

这一节和系统无关,三个平台都会撞上。建议在 build 之前就把 Dockerfile 全改好, 而不是等它失败再改——国内网络下一次 build 失败可能要等好几分钟才超时。

依赖症状解法
Docker Hub 解析不了 production.cloudfront.docker.com 基础镜像改 docker.m.daocloud.io/library/xxx;deno 用 docker.m.daocloud.io/denoland/deno:bin
Debian apt Unable to connect to deb.debian.org 见下方 sed。时通时不通,别因为某个服务侥幸过了就跳过
PyPI 极慢或超时 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple
Go 模块 proxy.golang.org 不通 ENV GOPROXY=https://goproxy.cn,direct
npm 慢 npm config set registry https://registry.npmmirror.com
Debian 换源 —— 新版是 debian.sources 不是 sources.list
RUN sed -i 's|deb.debian.org|mirrors.tuna.tsinghua.edu.cn|g; \
            s|security.debian.org|mirrors.tuna.tsinghua.edu.cn|g' \
    /etc/apt/sources.list.d/debian.sources

机器上有代理软件的话

容器出站通常会被 TUN 模式接管(不配代理也能连上 YouTube),但显式配代理更快更稳。 容器里指向宿主机代理:Linux 用 --network host 或宿主机网关 IP, macOS / Windows 用 http://host.docker.internal:<端口>。

03

五个服务

仓库都是自己的,直接 git clone 即可。建议放同一个父目录下便于统一管理。 下面按建议顺序排列,小智服务端是核心,其余四个是能力扩展,可按需取舍。

小智服务端(全模块)

8000 ws 8002 智控台 8003 视觉
来源
xinnan-tech/xiaozhi-esp32-server
官方文档
全模块部署 Deployment_all.md · 精简版 Deployment.md · FAQ
组成
Python 主服务 + Java manager-api + Vue manager-web + MySQL + Redis

启动有先后依赖,不能一把 up:

  1. 先下 SenseVoice 的 model.pt(约 893MB,见下)
  2. 先起 MySQL + Redis + manager-api + manager-web
  3. 取出 server.secret(页面上有,查库更快,见下)
  4. 填进主服务的 data/.config.yaml,再起主服务
  5. 配三个默认 null 的 server.* 参数(见下),清 Redis
  6. 浏览器开 http://<本机IP>:8002,注册的第一个账号自动是管理员
  7. 重启设备 → 屏幕出激活码 → 在智控台绑定

model.pt 不下,主服务起不来

compose 里主服务挂了 ./models/SenseVoiceSmall/model.pt。文件不存在时 Docker 会把挂载点建成目录,主服务随即失败——就算你用云端 ASR 也一样要这个文件。

mkdir -p models/SenseVoiceSmall
# modelscope 拒绝默认 UA,而且【静默返回 0 字节】不报错
curl -L -A 'Mozilla/5.0' -o models/SenseVoiceSmall/model.pt \
  'https://modelscope.cn/models/iic/SenseVoiceSmall/resolve/master/model.pt'
# 验货:头两字节必须是 PK
head -c2 models/SenseVoiceSmall/model.pt
server.secret 直接查库,不用去页面里翻
docker exec xiaozhi-esp32-server-db mysql -uroot -p123456 \
  -D xiaozhi_esp32_server -N -B \
  -e "select param_code,param_value from sys_params where param_code like '%secret%';"

三个默认 null 的参数:不配,设备拿到的是容器内网 IP

server.websocket、server.ota、server.fronted_url 初始都是空的 (最后一个默认 http://xiaozhi.server.com)。为空时主服务用自己探测到的地址, 而它在容器里,探到的是 172.18.0.x——设备永远连不上:

Websocket地址是  ws://172.18.0.4:8000/xiaozhi/v1/     ← 设备连不上

# 手动配上,注意 websocket 是 8000、OTA 是 8002,两个端口不一样
update sys_params set param_value='ws://<本机IP>:8000/xiaozhi/v1/'    where param_code='server.websocket';
update sys_params set param_value='http://<本机IP>:8002/xiaozhi/ota/' where param_code='server.ota';
update sys_params set param_value='http://<本机IP>:8002'              where param_code='server.fronted_url';

docker exec xiaozhi-esp32-server-redis redis-cli FLUSHALL   # 改库必清缓存

fronted_url 会印在设备屏幕上让你去绑定,留着默认值只会得到一个打不开的域名。

语音链路首选:火山引擎 / 豆包语音 2.0

火山引擎和豆包是同一家(前者是云平台,后者是模型品牌),但 ASR 和 TTS 在控制台里是两个独立产品,要分别开通。

环节智控台里选resource_id状态
ASR豆包语音识别2.0(流式)
ASR_DoubaoStreamASRV2
volc.seedasr.sauc.duration已实测
TTS火山引擎(流式)
TTS_HuoshanDoubleStreamTTS
volc.service_type.10029已实测
TTS 备选豆包语音合成2.0(流式)
TTS_HSDSTTS_V2
seed-tts-2.0预置项,未实测

resource_id 对不上开通的产品 —— 最容易卡住的地方

  • HTTP 400 —— resource_id 字符串无效(写错了)。改配置。
  • HTTP 403 —— resource_id 有效,但账号没开通该产品。改配置没用,去控制台开通。

上次卡了很久:配的是 volc.seedasr.sauc.duration(语音识别 2.0), 账号当时只开通了 volc.bigasr.sauc.duration(大模型语音识别),持续 403。 别把 403 当成 key 填错去反复检查凭据。

改了数据库要清 Redis 缓存

模型配置、音色(model:data:* / timbre:* / server:config)在 Redis 里有缓存, 直接改库不清缓存不生效。但 agent 的 system_prompt 没有缓存键,改完设备重连即可。

音色下拉的选项来自 ai_tts_voice 表——找不到某个音色是这张表里没预置,自己插一行,不是配置写错了。

音乐服务 xiaozhi-music-mcp

2234 8080 备用
来源
git@github.com:tankxu/xiaozhi-music-mcp
文档
仓库内 README.md 与 接入文档.md
音源
网易云 / YouTube / 哔哩哔哩,YTMusic 为主、B站兜底
关键端点
/stream_pcm(固件层)· /mcp · /manage?key= · /admin?key=(设备白名单)

这四个键缺一不可,但它们不是同一类东西

ADMIN_USER / ADMIN_PASS / MCP_TOKEN / MCP_AUDIO_TOKEN 在 compose 里用的是 :? 语法——没设置就直接报错退出,不会取默认值。 代码里的默认值是 admin / changeme / changeme, 这个强制就是为了防止带着默认凭据上线。

.env 键本质谁在用泄露了能干什么
ADMIN_USER
ADMIN_PASS
HTTP Basic 账号密码 你,浏览器开 /manage /admin 改设备白名单、管歌单、清缓存
MCP_TOKEN MCP 端点鉴权 小智服务端,走 Authorization: Bearer 同上,外加调用点歌工具
MCP_AUDIO_TOKEN 音频 / 封面取件令牌 设备、播放器 只能听歌,动不了管理

后两个必须填成不同的值,这是刻意的权限分级:MCP_TOKEN 走 HTTP 头、只在服务端之间传; MCP_AUDIO_TOKEN 则是明文拼在 audio_url 里发给设备的 (/audio?f=xxx.mp3&key=…),会出现在设备日志和抓包里,暴露面大得多。 拆开之后即使它泄露,拿到的人也只能听已缓存的歌。

还有个隐藏行为:MCP_AUDIO_TOKEN 同时是设备白名单里的一条记录——服务启动时会自动往 allow.json 塞一条 {"name":"MCP-client","token":…}, 否则 MCP 返回的链接连自己都取不到音频。

.env
ADMIN_USER=自己定
ADMIN_PASS=自己定
MCP_TOKEN=随机串
MCP_AUDIO_TOKEN=随机串
ALT_PORT=8080
LIVE_STREAM=0

data/ 是 bind mount(音乐缓存 + allow.json 设备白名单),重建容器不丢。 换机器迁移时把这个目录带上,缓存和白名单都在里面。

YouTube 报「Sign in to confirm you're not a bot」

这是出口 IP 信誉问题,不是账号权限问题。所以先试换代理节点—— 换个地区或线路就可能直接绕过,成本远低于折腾 cookies,也没有封号风险。绕不过再走下面这条。

真要上 cookies:yt-dlp --cookies-from-browser chrome 在 2026 年已彻底失效——Chrome 127+ 的 App-Bound Encryption 把 cookies 加密绑定到应用身份,报 Failed to decrypt with DPAPI (yt-dlp #10927),设计上挡死,改配置救不了。

两条可行路径:

  • 浏览器扩展「Get cookies.txt LOCALLY」——已实测有效,Chrome 上就能用。 关键在于扩展跑在浏览器内部,不受 App-Bound Encryption 限制。 登录 YouTube 后在 youtube.com 页面导出 Netscape 格式。
  • 换用 Firefox——它的 cookies 是明文 SQLite, yt-dlp --cookies-from-browser firefox 直接能读。

导出的文件放 data/cookies.txt,重启容器生效。代码会自动从 android_vr 切回 web 客户端——两者不兼容,带着 cookies 继续用 android 客户端反而更糟。

用小号

拿 cookies 跑自动化下载,有被 YouTube 判定异常的可能。不要用主力账号。

B站转码 stackchan-bili

2233 host 网络
来源
git@github.com:tankxu/stackchan-bili
网络
network_mode: host——不需要也不能再写 ports 映射
机器要求
吃 CPU,弱机器跑不动(见 00 节)

state/ 必须挂载

B站登录凭据 cookies.json 和播放历史都在 ./state。不挂的话容器一重建登录态就没了,每次都要重新扫码。

MACOSWINhost 网络模式的限制

network_mode: host 在 Linux 上才是真正的宿主机网络。 macOS 和 Windows 的 Docker 跑在虚拟机里,host 模式指的是那个虚拟机—— Docker Desktop 较新版本支持 host 网络但需要在设置里开启,OrbStack 默认支持。 如果发现 2233 端口在局域网不可达,先确认这一点,或者改回 ports 映射。

默认参数是给 CoreS3 屏调的:OUT_W=320 OUT_H=240 OUT_FPS=12 JPEG_Q=7、音频 16kHz 单声道。 BILI_QN=32 是 480P(实测免登录上限),要更低画质用 16。

小宇宙 xiaoyuzhou-server

23020 23021
来源
git@github.com:tankxu/xiaoyuzhou-server
文档
仓库内 README.md 与 doc/
技术栈
Go —— build 需要 GOPROXY=https://goproxy.cn,direct,约 70 秒

上游 token 很短命,需要自动 refresh 才能长期用。这套 refresh 逻辑原本是在 macOS 上跑通的, 换到 Linux / Windows 上没有实测过。

CLIProxyAPI

8317
来源
router-for-me/CLIProxyAPI
官方文档
help.router-for.me · 管理 API

把上游的 Claude Code / Codex / Gemini 等包装成 OpenAI 兼容接口。 端口 8317 是上次部署时定的,不是上游的默认值(README 里没写默认端口)。

添加上游账号走 OAuth 浏览器授权,这一步必须人工完成。

04

让它一直活着

所有 compose 文件都写了 restart: unless-stopped,但这句话在三个系统上兑现程度完全不同—— 这是自部署里最容易高估的一点。

系统restart 实际效果要额外做什么
LINUX 真正生效,断电来电自动恢复 systemctl enable docker 即可,没有别的
MACOS 只在 Docker 已启动后生效 Docker Desktop / OrbStack 设为登录时启动;还要处理睡眠(见下)
WIN 只在 Docker Desktop 已启动后生效 Docker Desktop 是用户级程序,必须有人登录桌面;要真无人值守得配自动登录

禁止睡眠

三个系统各自的写法
# Linux
sudo systemctl mask sleep.target suspend.target hibernate.target

# macOS —— 只阻止系统睡眠,屏幕仍可关
sudo pmset -a sleep 0 disablesleep 1

# Windows
powercfg /change standby-timeout-ac 0
powercfg /change hibernate-timeout-ac 0

固定 IP

设备固件里写的是这个地址,变了就失联。 在路由器上做 DHCP 保留比在系统里配静态 IP 更稳——换网络环境不会失联,重装系统也不用重配。

防火墙

# Linux (ufw)
sudo ufw allow 8000,8002,8003,2233,2234,8080,8317,23020,23021/tcp

# macOS —— 默认放行,一般不用动;若开了应用防火墙,允许 Docker 接受入站连接即可

# Windows
New-NetFirewallRule -DisplayName 'xiaozhi-stack' -Direction Inbound -Action Allow `
  -Protocol TCP -LocalPort 8000,8002,8003,2233,2234,8080,8317,23020,23021

WINWindows 的 Block 规则优先于 Allow

Docker Desktop 自己会装一对规则:Allow / Private 和 Block / Public。 后加的 Allow 会被那条 Block 整个压住,与添加顺序无关。 现象很反直觉:本机 curl 正常,同局域网的设备连不上。

Get-NetConnectionProfile
Set-NetConnectionProfile -InterfaceAlias 'WLAN' -NetworkCategory Private
05

设备侧:StackChan 刷机与配置

服务端跑起来之后,设备要刷固件、再把四个服务地址写进去。固件仓库 git@github.com:tankxu/stackchan-hichan(私有),默认分支 hichan。

三种刷法,别搞混

场景命令写哪些分区NVS
新设备 / 迁移 一体包从 0x0 刷
cd firmware && ./make_release.sh
全片连续覆盖 清空
改了代码日常迭代 idf.py build && ./flash_app.sh 只写 app 分区 0x20000,快两倍 保留
设备已联网 ./push_ota.sh 免 USB 推送,带 sha 硬校验 保留

一体包等于恢复出厂

NVS 分区在 0x9000,而一体包里那一段是 0xFF 填充——从 0x0 连续覆盖会把它一并抹掉: WiFi 凭据、小智绑定、音量亮度、舵机零点标定全没。 新设备用它是对的,已经在用的设备别拿它做日常更新。

flash_app.sh 按 MAC 认设备,不认端口号

串口号每次插拔都会变(实测拔掉另一台 ESP32 后,StackChan 就占了对方原来的 usbmodem 号), 认端口号迟早刷错机器。换设备记得改脚本顶部的 STACKCHAN_MAC。

sdkconfig.defaults.local 改了不生效:它不覆盖已存在的 sdkconfig

文件头上那句「改完需要 idf.py reconfigure 才生效」只对全新构建目录成立。 IDF 的规则是 sdkconfig.defaults* 仅在 sdkconfig 不存在时生成初始配置, 既有的 sdkconfig 优先。改 WiFi / OTA 地址要直接改 sdkconfig, 并且验证产物而不是相信输入:

grep -E 'DEFAULT_WIFI_SSID|DEFAULT_WIFI_PASSWORD|CONFIG_OTA_URL' build/config/sdkconfig.h

既要换 app 又要清 NVS:先刷 app,再擦 NVS

反了就白刷。esptool erase-region 擦完会自动硬复位, 设备立刻用还没换掉的旧 app 启动,看到凭据表为空就把旧凭据又种回 NVS; 等你刷完新 app 再复位,新 app 看到凭据表非空(规则是「非空就不种入」),编译期的新凭据永远进不去。 表现是「明明改了 SSID,设备还在扫旧的那个」,而 sdkconfig.h 里查得到新值。

设备只能连 2.4GHz

ESP32-S3 没有 5GHz 射频。双频同名的路由器上,「就这一个 WiFi」在设备眼里可能只有 5G 那一半。 用 netsh wlan show networks mode=bssid(或 macOS 的 airport -s)看目标 SSID 的 波段 / 频道:1-13 是 2.4G,36 以上是 5G。另外隐藏 SSID 也不出现在扫描列表里, 而固件走「扫描后匹配凭据表」,扫不到就等于不存在——这种情况用设备 AP 配网页面 手动输入 SSID 可以绕过(配网是直连,不依赖扫描)。

刷完写服务地址

统一走设备的 /config 接口(端口 8032),立即生效且断电不丢:

curl -X POST http://<设备IP>:8032/config \
  -d '{"music_url":"http://192.168.1.100:2234"}'
服务配置键值注意
小智服务端ota_urlhttp://<IP>:8002/xiaozhi/ota/ 要重启才生效
音乐music_urlhttp://<IP>:2234 还要去服务端 /admin 把设备 MAC 加白名单
B站bili_urlhttp://<IP>:2233 指向常开的机器
小宇宙podcast_urlhttp://<IP>:23021 23021 是设备反代口,不是 23020 那个 API 网关

OTA 端点是 8002,不是 8000

本页早先版本写的是 8000,那是错的。8000 是纯 websocket 端口: GET / 会回 Server is running(看着像正常), 但 POST /xiaozhi/ota/ 得到 Empty reply from server—— 连接被收下又直接关掉,服务端日志一条不留。全模块部署里 OTA 由 manager-api 提供,也就是 8002。 设备侧表现是 checkNewVersion failed to connect <IP>:8000, 极容易误判成主服务没起来。先确认端口,再怀疑服务。

改 ota_url 之后必须重启设备

另外三个都是立即生效的,ota_url 不是。固件为了开机提速做过一件事: 「NVS 里已有 websocket/mqtt 配置就跳过 CheckVersion」——而新服务端的连接配置恰恰要靠那趟请求才下发。 所以写 ota_url 时固件会连带清掉 websocket/url 和 mqtt/endpoint 两个缓存键, 然后必须重启才会去新服务端取配置。

不重启的表现是「地址改了但一点反应没有」,设备继续连旧服务端——极容易误判成配置压根没写进去, 于是开始反复检查 JSON、查网络,白费时间。

编译期默认值未必是你的地址

music_url 默认 http://home.tankxu.com:2234、 bili_url 默认 http://192.168.1.20:2233、 podcast_url 默认 http://192.168.1.100:23021—— 和你的部署对不上就三个都显式写一遍。

把配置键写成空串是「恢复编译期默认」,不是「清空」。

刷完还要往提示词里加一段

固件刷好、服务地址也写对了,设备的点歌行为仍然可能不对——因为工具该怎么用是靠提示词教的。 去小智智控台的智能体配置里,把下面这段追加到角色提示词末尾(落库是 ai_agent.system_prompt):

追加到角色提示词末尾
收到以 [主] 开头的消息时,那不是用户说的话,而是系统给你的触发信号。

音乐播放规则:
- 用户点明了具体某一首歌/某个节目 → 用 self.music.play_song(song_name, artist_name)
  例:「放首告白气球」「我要听小兔子乖乖」「讲个西游记的故事」
- 用户没点明具体某一首,只给了歌手/风格/心情/歌单 → 用 self.music.play_stream(query, kind, mode)
  例:「播放周杰伦的歌」→ query=周杰伦, kind=artist
     「放点安静的音乐」→ query=安静, kind=mood
     「放儿歌」「放贝乐虎」→ query=儿歌/贝乐虎, kind=playlist
     「随便放点歌」→ query=热门, kind=mood
  用户说"连续播放/一直放/循环"时也用它;说"随机"传 mode=random,说"按顺序"传 mode=order。
- 不要使用 search_music。
- 工具会立刻返回"这就为你播放",不要再问用户确认,也不要说"正在为你搜索"之类的等待话术。

为什么必须点名「不要使用 search_music」

因为 LLM 面前同时挂着两套来源不同的 MCP 工具:

  • self.music.play_song / play_stream / stop ——设备端工具,固件在 hal_music.cpp 里注册,调用即开始播放。
  • search_music——音乐服务端的 MCP 工具,只返回候选列表,不播放。

不点名的话,模型很容易先去 search_music 拿一堆候选、再回头问用户选哪首,结果什么都没放出来。

参数名要和固件对得上

song_name / artist_name 和 query / kind / mode 都是从固件的工具定义里来的。改了固件的工具签名,这段提示词要跟着改,否则模型会按旧参数名调用而失败。

控制台与文档

06

症状对照表

症状最可能的根因
ASR / TTS 一直 403resource_id 对应的产品没在控制台开通
ASR / TTS 报 400resource_id 字符串写错了
改了模型配置不生效Redis 缓存没清
智控台音色下拉里没有想要的音色ai_tts_voice 表里没预置,自己插一行
音乐容器起来就退出.env 里四个必填键缺了
B站每次都要重新扫码state/ 没挂载
B站画面卡顿 / 掉帧机器 CPU 不够,换机器
YouTube 报 bot 检测需要 cookies,且不能用 Chrome 导
build 卡在 deb.debian.orgDebian 源没换
本机能访问,局域网设备连不上WIN 防火墙 Block 压住 Allow / WIN WSL2 没开 mirrored / MACOS host 网络模式限制
点歌时先问「你要听哪一首」,最后什么也没放提示词里没写音乐规则,模型走了 search_music
改了 ota_url 设备毫无反应没重启——只有它需要重启,另三个立即生效
设备报 checkNewVersion failed to connect :8000OTA 在 8002,8000 是纯 websocket 口
设备连不上 ws,日志里地址是 172.18.0.x三个 server.* 参数默认 null
主服务起不来,model.pt 挂载点变成目录SenseVoice 模型没下
模型下下来 0 字节但 curl 不报错modelscope 拒绝默认 UA
改了 SSID 但设备还在扫旧的擦 NVS 早于刷 app;或 sdkconfig.defaults 没生效
设备一直 No AP found目标 SSID 只有 5G,或 SSID 隐藏
启用 WSL 报「错误 2」CBS 组件存储被清理软件破坏
刷完机 WiFi 和小智绑定全没了用了一体包,它会清空 NVS
设备点歌取不到音频设备 MAC 没加进音乐服务白名单
重启后服务全没了MACOSWIN 没人登录,Docker 没启动
跑一晚上就断MACOS 系统睡眠了
07

这份文档没覆盖到的

帮别人装机的版本在这里

如果是给别人的 Windows 机器远程部署,需要额外处理远程通道、SSH 免密、私有仓库打包传输、 以及一整套 Windows 专有坑——那些都在 帮别人部署版里。

基于 2026-08 的两次实际部署整理(2026-08-18、2026-08-25 更新)。
标注为「未实测」的条目请自行确认后再依赖。