Runbook · 2026-08 实测

小智全栈装机手册

把小智服务端和四个配套服务,远程部署到一台国内网络环境下的 Windows 机器。 这份文档写给执行部署的 agent——每一步都给到可直接执行的命令、验证方式,以及上一次实际踩到的坑。

目标环境
Windows 10/11 + 国内网络
服务数量
5 个
远程方式
Tailscale + OpenSSH
上次实测
2026-08-18
00

开工前:给 agent 的执行须知

你要操作的是别人的电脑。机主已经授权,但这不代表可以随便动——下面三条是硬规矩,上一次部署时每一条都付出过代价。

规矩一 · 不要把委托人的账号发过去

音乐服务的 API key、小智服务端的 LLM/TTS 凭据、B站和小宇宙的登录态,一律让机主自己注册、自己扫码。 你的角色是把服务跑起来并留好填凭据的入口,不是把自己这边的账号复制一份过去。

规矩二 · 打包前必须读 .gitignore,不要凭记忆列排除项

三个私有服务要从委托人的机器打包传过去。上次我凭记忆写 --exclude, 漏掉了 app/allow.json(设备 MAC 白名单),把它一起打进包传到了对方机器。 正确做法:tar 前先 cat .gitignore,按文件里写的来;打完包先列一遍包内容确认没有 state/cookies/凭据,再传。

规矩三 · 私有仓库 clone 不了,只能打包传

下面五个服务里有三个是私有仓库(git@github.com:tankxu/…),对方机器没有权限。 唯一的路径是在委托人的机器上 tarscp → 对方机器解压。 公开的那两个(小智服务端、CLIProxyAPI)可以直接在对方机器上拉。

开工前要跟机主要到的东西

要什么用途拿不到的后果
Tailscale 上的 IP建立 SSH 通道只能靠 ToDesk 手动点,效率差一个数量级
Windows 用户名配置计划任务的 -UserIdDocker 拉镜像那一步会卡死
局域网 IP(建议固定)设备要连的地址DHCP 一换设备就失联
是否装了代理软件排查网络问题的前提会把代理造成的现象错怪到别的地方
大模型 / TTS 的 key小智服务端服务能起但不出声

机器硬指标

上次那台是 Win10 专业版 22H2 / 16 核 / 32G / C 盘剩 125G,跑满五个服务无压力。 家庭版装不了 Hyper-V,但 WSL2 可以,所以家庭版也能跑——只是 Docker Desktop 只能走 WSL2 后端。 磁盘至少留 40G。

01

建立远程通道

先把 SSH 打通,后面所有操作都在 SSH 里做。ToDesk 只在两个场景用:装 Tailscale 之前的引导,和后面需要人在桌面上点确认的时刻(Docker Desktop 首次启动、B站扫码登录)。

验证

从你这边连过去,应当不问密码直接进,并且 IsAdminTrue

ssh w@100.x.x.x "powershell -c \"([Security.Principal.WindowsPrincipal] \
  [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole('Administrators')\""

这条通道重启后依然可用

sshdtailscaled 都是系统服务,开机自启,不需要有人登录 Windows 桌面。 但下一节的 Docker Desktop 是用户级程序——机器重启后必须有人登录桌面,容器才会起来。这个区别后面会反复咬人。

02

三个 Windows 专有坑

这一节放在装东西之前,因为它们是整个部署里最耗时间的部分。上次五个服务里,每一个都在其中某一条上卡过。 先读完再动手,能省掉大半天。

坑一:SSH 会话里 Docker 拿不到 registry 凭据

任何碰 registry 的命令(docker pulldocker compose build)在 SSH 会话里都会报:

error getting credentials - err: exit status 1,
out: `A specified logon session does not exist.`

根因是 Docker Desktop 的凭据助手依赖 Windows 的交互式登录会话, 而 SSH 是非交互式会话,拿不到那个 token。这是设计限制,不是配置问题—— DOCKER_CONFIG--config 指向干净目录、删掉 credsStore 字段,这三种办法我都试了,全部无效

唯一可行的解法:注册一个 LogonType Interactive 的计划任务,把命令推回用户会话执行。

通用模板 —— 凡是要拉镜像 / build 的命令都套这个
$dir  = 'C:\xiaozhi-music-mcp'
$log  = "$dir\build.log"
$act  = New-ScheduledTaskAction -Execute 'cmd.exe' `
        -Argument "/c cd /d $dir && docker compose up -d --build > $log 2>&1"
$prin = New-ScheduledTaskPrincipal -UserId 'DESKTOP-XXXX\w' -LogonType Interactive

Register-ScheduledTask -TaskName 'dc-build' -Action $act -Principal $prin -Force
Start-ScheduledTask -TaskName 'dc-build'

计划任务是异步的,你拿不到退出码

Start-ScheduledTask 立刻返回,命令在后台跑。判断进度要靠轮询副作用docker images 里镜像有没有出现、build.log 的尾部、docker ps 的状态。

写轮询脚本时注意不要用宽泛的 grep 判成功——上次我用 grep ota 结果匹配到了日志里的 Total, 用 grep TTS 匹配到的其实是一条 TTS 失败日志,两次都误报了成功。 匹配要够具体(POST /xiaozhi/ota/ 这种),并且收集一个时间窗口的完整日志再判断,不要一命中就 break。

坑二:Windows 防火墙的 Block 规则优先于 Allow

Docker Desktop 安装时会自己写入一对规则:Allow / PrivateBlock / Public。 你后加的「Profile=Any 放行 8000」会被那条 Block 整个压住——因为 Windows 防火墙里 Block 永远赢 Allow,与添加顺序无关。

现象极其反直觉

你从 Tailscale 连得上,同一个局域网里的设备反而连不上。 因为 Tailscale 虚拟网卡被识别成 Private(走 Allow),而 WLAN 被识别成 Public(撞上 Block)。 排查时很容易怀疑到端口、容器、路由上去,实际上是网络配置文件的问题。

解法:把 WLAN 改成专用网络
Get-NetConnectionProfile          # 先看当前是 Public 还是 Private
Set-NetConnectionProfile -InterfaceAlias 'WLAN' -NetworkCategory Private

这比删掉 Block 规则更安全:笔记本带出门连公共 WiFi 时,那条 Public 的 Block 会继续保护。 另外仍然要为服务端口加放行规则:

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

坑三:PowerShell 5.1 的编码

操作默认行为后果
Get-Content -Raw 按 ANSI 读 UTF-8 中文注释读成乱码,并且吃掉换行——compose 文件 88 行变 85 行,YAML 直接塌掉
> 重定向 写 UTF-16LE JSON 文件首字节是 0xFF,任何解析器都读不了
数组里拼引号 [char]34 被当独立元素 生成的字符串结构错乱
一律改用 .NET 方法读写
$enc = New-Object System.Text.UTF8Encoding $false   # $false = 不带 BOM
$txt = [System.IO.File]::ReadAllText($path, $enc)
[System.IO.File]::WriteAllText($path, $txt, $enc)

# 拼引号用反引号转义,不要用 [char]34
$s = "key=`"value`""
03

装 WSL2 与 Docker Desktop

验证

ssh w@100.x.x.x "docker version && docker run --rm docker.m.daocloud.io/library/hello-world"

hello-world 那条报 logon session 错误,说明必须走计划任务(坑一),这是预期内的,不是装错了。

04

国内网络镜像总表

每一个服务都在这张表里的某一行卡过。建议在 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。时通时不通——上次 B站服务侥幸过了,音乐服务就卡死,所以不要因为一个服务通了就跳过这步
PyPI 极慢或超时 pip install -i https://pypi.tuna.tsinghua.edu.cn/simple
Go 模块 proxy.golang.org 不通 Dockerfile 里加 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

如果机器上装了代理软件

上次那台装了 0dcloud(Meta Tunnel,mihomo 内核)。TUN 模式下走 fake-ip, 所有域名解析到 198.18.x.x,HTTP 代理口在 127.0.0.1:17891。 容器出站也被它接管(不配代理也能连上 YouTube),但显式配 http://host.docker.internal:17891 更快更稳。

它的路由表是干净的(192.168.1.0/24 正常走 WLAN)——排查局域网连不通时不要错怪代理, 先按坑二查防火墙。

05

五个服务

目录统一放 C 盘根下,凭据各自存在同目录的 KEYS.txt 里,方便机主自己查改。 下面按建议的部署顺序排列——小智服务端是核心,其余四个是它的能力扩展,可按需取舍。

先建好目录结构

C:\xiaozhi-server       # 小智服务端(全模块)
C:\xiaozhi-music-mcp    # 音乐
C:\stackchan-bili       # B站转码
C:\xiaoyuzhou-server    # 小宇宙播客
C:\cli-proxy-api        # CLIProxyAPI

私有仓库的传输方式

在委托人的机器上执行
# 1. 先看清楚要排除什么 —— 不要凭记忆写
cat .gitignore

# 2. 打包(--exclude 按 .gitignore 来,state/data/cookies 一律不带)
tar --exclude='.git' --exclude='state' --exclude='data' \
    --exclude='__pycache__' --exclude='.env' \
    -czf /tmp/svc.tgz -C /path/to/repo .

# 3. 传之前先列一遍包内容,确认没有凭据混进去
tar -tzf /tmp/svc.tgz | grep -iE 'cookie|allow|token|secret|\.env|state/'

# 4. 传过去解压
scp /tmp/svc.tgz w@100.x.x.x:/C:/tmp/
ssh w@100.x.x.x "tar -xzf C:/tmp/svc.tgz -C C:/xiaozhi-music-mcp"

小智服务端(全模块)

8000 ws 8002 智控台 8003 视觉
来源
xinnan-tech/xiaozhi-esp32-server(公开,v0.9.6 全模块)
目录
C:\xiaozhi-server
官方文档
全模块部署 Deployment_all.md · 常见问题 FAQ.md · xiaozhi.biz
组成
Python 主服务 + Java manager-api + Vue manager-web + MySQL + Redis
传输
公开仓库,对方机器直接拉
要机主自备
火山引擎账号(ASR / TTS 分别开通)+ 一个 LLM 的 key

启动顺序有依赖,不能一把 up:

  1. 先起 MySQL + Redis + manager-api + manager-web
  2. 浏览器开 http://<局域网IP>:8002注册的第一个账号自动是管理员
  3. 在智控台「参数管理」里取出 server.secret
  4. 把 secret 填进主服务的 data/.config.yaml,再起主服务

改了数据库要清 Redis 缓存

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

语音链路首选:火山引擎 / 豆包语音 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 系统预置项,未实测

两者都填 appid + access_token,TTS 的 ws_url 保持预置值 wss://openspeech.bytedance.com/api/v3/tts/bidirection 即可。 LLM 与语音链路互相独立,上次配的是 DeepSeek。

最容易卡住的地方:resource_id 和实际开通的产品对不上

resource_id 决定你调的是哪个产品,它必须和控制台里真正开通了的那个对应。 两种报错方向完全相反,先分清再动手:

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

上次整整卡在这里:配的是 volc.seedasr.sauc.duration(语音识别 2.0), 而账号当时只开通了 volc.bigasr.sauc.duration(大模型语音识别),持续 403。 开通对应产品后立刻就通了。不要把 403 当成 key 填错,去反复检查凭据——那是白费时间。

音色

智控台音色下拉的选项来自数据库 ai_tts_voice 表,火山这一路预置了几十个 (TTS_HuoshanDoubleStreamTTS_0001 爽快思思、_0002 温暖阿虎……), 底层是 speaker 字符串如 zh_female_shuangkuaisisi_moon_bigtts

下拉里找不到某个音色,是这张表里没有预置,不是配置写错了——自己往 ai_tts_voice 插一行即可(讯飞的 x6_pro 就属于这种情况)。 插完记得清 Redis 缓存。

备选链路

讯飞(ASR_XunfeiStream + TTS_XunFeiStreamTTS)也实测跑通过,可以作为火山出问题时的退路。 另外系统预置了 ASR_FunASR 等本地方案,不依赖云服务但吃 CPU, 机器性能够、又不想为语音付费时可以考虑。

音乐服务 xiaozhi-music-mcp

2234 8080 备用
来源
git@github.com:tankxu/xiaozhi-music-mcp私有,需打包传)
目录
C:\xiaozhi-music-mcp
音源
网易云 / YouTube / 哔哩哔哩,YTMusic 为主、B站兜底
关键端点
/stream_pcm(固件层)、/mcp(MCP 端点)、/manage?key=(管理页)、/admin?key=(设备白名单)
要机主自备
一组管理账号密码 + 两个令牌(见下)+ 可选的 YouTube cookies

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

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 返回的链接连自己都取不到音频。

C:\xiaozhi-music-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 DPAPIyt-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 判定异常的可能。不要用主力账号。

在目标机器上导出,不要用你自己机器导的。cookies 与请求出口 IP 不一致时更容易触发风控。

B站转码 stackchan-bili

2233 host 网络
来源
git@github.com:tankxu/stackchan-bili私有
目录
C:\stackchan-bili
网络
network_mode: host —— 不写也不能写 ports 映射
要机主自备
B站账号(扫码登录)

state/ 必须挂载

B站登录凭据 cookies.json 和播放历史都在 ./state不挂的话容器一重建登录态就没了,每次都要重新扫码。这个目录也不要从委托人那边带过来。

登录要在对方的桌面上完成:用 ToDesk 弹出扫码页,让机主用自己的B站 App 扫。 服务会轮询扫码状态(最长 3 分钟),成功后 state/cookies.json 落盘。

默认参数是给 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私有
目录
C:\xiaoyuzhou-server
技术栈
Go(build 需要 GOPROXY=https://goproxy.cn,direct
要机主自备
自己的小宇宙账号——不要用委托人的

Go 编译约 70 秒。上游 token 很短命,Mac 侧原本有自动 refresh 的机制; 如果对方机器上要长期用,需要确认 refresh 逻辑在 Windows 上也跑得起来——这一条上次没有验证

CLIProxyAPI

8317
来源
router-for-me/CLIProxyAPI(公开)
官方文档
help.router-for.me · 管理 API
目录
C:\cli-proxy-api
要机主自备
上游账号的 OAuth 授权

最后一步只能由人来做

添加上游账号走的是 OAuth 浏览器授权,agent 代劳不了。 把服务跑起来、端口通了之后,剩下的登录动作留给机主,并把操作步骤写清楚交给他。

上游来源与官方文档汇总

服务仓库官方文档
小智服务端 xinnan-tech/xiaozhi-esp32-server
公开
Deployment_all.md(全模块,本手册对应)
Deployment.md(只跑 server 的精简版)
FAQ.md · xiaozhi.biz
CLIProxyAPI router-for-me/CLIProxyAPI
公开
help.router-for.me
管理 API
音乐服务 git@github.com:tankxu/xiaozhi-music-mcp
私有,打包传
仓库内 README.md接入文档.md
B站转码 git@github.com:tankxu/stackchan-bili
私有,打包传
仓库内 README.md
小宇宙 git@github.com:tankxu/xiaoyuzhou-server
私有,打包传
仓库内 README.mddoc/

上游文档和这份手册的分工

上游文档讲的是标准环境下怎么装;这份手册讲的是国内网络 + Windows + 远程操作下额外要处理什么。 两者是叠加关系——先按上游文档理解服务本身的结构,再回来看这里的坑。 上游文档不会告诉你 Docker 凭据在 SSH 会话里拿不到,也不会告诉你防火墙 Block 优先于 Allow。

06

收尾:让它能被找到、且一直活着

交付清单

07

设备侧: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

刷完写服务地址

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

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

ota_url 之后必须重启设备

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

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

固件的编译期默认值指向的是你自己的服务,不是机主的

music_url 默认 http://home.tankxu.com:2234bili_url 默认 http://192.168.1.20:2233podcast_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_namequery / kind / mode 都是从固件的工具定义里来的。改了固件的工具签名,这段提示词要跟着改,否则模型会按旧参数名调用而失败。

控制台与文档

08

症状对照表

症状最可能的根因去哪一节
A specified logon session does not existSSH 非交互会话拿不到 Docker 凭据坑一
Tailscale 能连,局域网设备连不上防火墙 Block/Public 压住了 Allow坑二
compose 文件被改后 YAML 报错Get-Content -Raw 吃掉了换行坑三
JSON 文件读不了,首字节 0xFF> 重定向写成了 UTF-16LE坑三
Docker 引擎一直转圈起不来装了 16MB 的内核包而不是 250MB 完整包03-3
sshd 服务不存在但装的时候说成功了Add-WindowsCapability 拉不到组件01-2
密钥认证失败、继续问密码公钥放了 ~/.ssh/ 而不是 administrators_authorized_keys01-3
icacls 报找不到组中文版 Windows,要用 SID 不能用组名01-3
build 卡在 deb.debian.orgDebian 源没换04
音乐容器起来就退出.env 里四个必填键缺了服务卡 2
B站每次都要重新扫码state/ 没挂载服务卡 3
YouTube 报 bot 检测需要 cookies,且不能用 Chrome 导服务卡 2
改了模型配置不生效Redis 缓存没清服务卡 1
点歌时先问「你要听哪一首」,最后什么也没放提示词里没写音乐规则,模型走了 search_music07
改了 ota_url 设备毫无反应没重启——只有它需要重启,另三个立即生效07
刷完机 WiFi 和小智绑定全没了用了一体包,它会清空 NVS07
设备点歌取不到音频设备 MAC 没加进音乐服务白名单07
重启后所有服务都没了没人登录桌面,Docker Desktop 没启动06-4
09

这份文档没覆盖到的

诚实标注,避免下一个 agent 把没验证的当成已验证的:

上次部署的耗时分布,供估算

远程通道(Tailscale + OpenSSH)约 1 小时,WSL/Docker 底座约 2 小时(大半耗在那个 16MB 的错误安装包上), 五个服务约 4 小时(大半耗在 Docker 凭据和各种换源上)。 照着这份文档走,底座和换源的时间应该能压掉大半。

基于 2026-08-18 一次真实远程部署整理。目标机 Windows 10 专业版 22H2 / 16 核 / 32G。
文档里所有「上次」指的都是那一次,标注为「没有验证」的条目请自行确认后再依赖。