本文目标:照着做,大约 20~30 分钟(其中 13GB 模型下载占大头),你的 NAS 上就能跑起一个"本地 AI 赛博女友"——对着麦克风说话,她用你克隆的音色回答,嘴型跟着动。
全程本地运行,不需要 API Key,不花一分钱,断网也能用。
看之前先把这几个换成你自己的:
-<NAS_IP>—— 你的 NAS 内网地址(路由器后台看,或者ping一下别的设备反查)
-<项目目录>—— 你放这个项目的地方,示例用/volume1/docker/cybergirl
- 端口21790 / 21791 / 21792—— 三个都在.env里可以改,撞了端口就改这里,不用动代码
0. 先看跑起来是什么样

网页全屏是数字人,左边橙色语音球。你说话 → 她听见 → 大模型想 → 用你的音色说回来 → 嘴型同步。底部实时出字幕。
手机上一样能用:

它由三个容器组成(都在你的 NAS 上,不联网):
1. 开始之前,确认三件事
⚠️ 如果你的 NAS 上还跑着 ComfyUI / Ollama:16G 卡上这几个一起开会显存不够,ComfyUI 一跑就 OOM。
装好之后有现成的让位脚本:bash scripts/14-显存让位.sh(停掉吃显存的),用完bash scripts/15-显存复位.sh(开回来)。
2. 把文件传到 NAS
下载部署包解压,里面应该长这样:
赛博女友-部署包/
├── docker-compose.yml
├── .env.example
├── app/ # 网页前端 + 语音链路服务
├── docker/ # 两个镜像的 Dockerfile
├── persona/ # 人设(默认「小雅」)
├── scripts/ # 各种脚本,00~18
├── assets/ # 数字人形象底版视频
├── voice/ref/ # 参考音色
└── docs/ # 补充文档上传方式三选一:
网页文件管理器(最省事):把整个目录打包成 zip,上传到 NAS,解压。
scp(NAS 开了 SSH 时):
scp -r ./赛博女友-部署包 <NAS用户名>@<NAS_IP>:<项目目录>git:推到 GitHub/Gitee,NAS 上
git clone。
⚠️ 上传完一定要检查有没有多套一层
这是最容易踩的坑,不报错,就是"文件不存在":
cd <项目目录>
ls -a你要看到 scripts/、app/、docker-compose.yml 都在这一层。
如果看到的是 赛博女友-部署包/scripts/...,说明多了一层,把里面的东西提上来:
cd <项目目录>
mv 赛博女友-部署包/* 赛博女友-部署包/.env.example 赛博女友-部署包/.gitignore . 2>/dev/null
rm -rf 赛博女友-部署包
ls -a # 再确认一次3. 四条命令,跑起来
3.1 生成配置
cd <项目目录>
cp .env.example .env默认配置就能用,一般不用改。真要改就改这几个:
vi .env端口选号建议:别用 80/443/3000/5000/8000/8080/8888/9000,也别用 32768 以上(那是内核临时端口段,容易偶发冲突)。
3.2 预检(强烈建议跑)
bash scripts/00-预检.sh它会告诉你:Docker 能不能用、容器里能不能看到显卡、三个端口空不空、磁盘够不够、模型还缺哪些。
输出长这样:
项目目录:/volume1/docker/cybergirl
── 1/5 Docker ──
✓ docker 正常
✓ docker compose 正常
── 2/5 GPU ──
✓ docker 已注册 nvidia runtime
✓ 容器里能看到显卡
── 3/5 端口 ──
✓ 端口 21790(网页)空闲
✓ 端口 21791(数字人)空闲
✓ 端口 21792(大模型)空闲
── 4/5 磁盘 ──
可用空间:XX GB(模型 + 镜像 + 缓存大约要 40 GB)看到"容器里看不到显卡"就先别往下走,去确认
nvidia-container-toolkit装没装。
如果你 NAS 上已经有 ComfyUI 容器能跑nvidia-smi,说明机制是好的,只是这台还没注册。
3.3 下载模型(约 13GB)
bash scripts/01-下载模型.sh全走 hf-mirror 国内镜像,断了重跑会续传,不用怕。
下完会打印体积清单:
2.9G models/asr
4.7G models/llm
4.3G models/tts
86M livetalking/models/s3fd.pth
205M livetalking/models/wav2lip.pth
372K assets/idle.mp4嫌一次下太多太慢?
bash scripts/01-下载模型.sh lip只下嘴型那部分,其余按需再下。
3.4 构建镜像 + 启动
bash scripts/02-构建镜像.sh脚本会自动挑一个能拉到的 CUDA 基础镜像(pytorch 官方 → nvidia 官方 → 你 NAS 上已有的 ComfyUI 镜像),首次 10~25 分钟。之后改代码不用重建(代码是挂载进去的)。
三个候选都拉不下来?在 1Panel → 容器 → 镜像加速里配个国内加速地址,然后手动
docker pull任意一个含 torch 2.5+/cu124 的镜像,把.env的BASE_IMAGE指过去。
bash scripts/04-启动.sh看到这三个就成了:
── 等待服务就绪 ──
✓ 大模型 就绪
✓ 数字人引擎 就绪
✓ 语音服务 就绪4. 打开网页
http://<NAS_IP>:21790/进去点「开始」,浏览器会要麦克风权限,点允许。

然后就可以跟她说话了:

⚠️ 如果点了"开始"完全没反应
这是 90% 的人都会卡住的地方。 原因不是代码:Chrome 规定"非 localhost 的 http 页面"不给麦克风权限。
你在 Mac/手机上用 http://192.168.x.x:21790 打开,属于"不安全上下文",浏览器直接不给权限。表现就是"点了开始一点动静没有,日志里一条识别记录都没有"。
解法(选一个):
a) 自己签个 https 证书(推荐,一次搞定,手机也能用)
bash scripts/09-生成自签证书.sh
bash scripts/05-停止.sh && bash scripts/04-启动.sh然后用 https://<NAS_IP>:21790/ 打开,浏览器提示证书不受信任时点「继续访问」。
b) 让 Chrome 放行这个 http 源(仅桌面)
Chrome 快捷方式目标后面加:
--unsafely-treat-insecure-origin-as-secure=http://<NAS_IP>:21790另外记得:进设置把 NOISE GATE 拉到最左边(0)。这个滑块拉高会把正常说话的音量直接掐掉,症状跟上面一模一样。
想用手机在公网上玩
套一条 Cloudflare Tunnel:
cloudflared tunnel --url http://localhost:21790拿到 https 域名后手机直接开,麦克风权限也正常。
5. 换成你自己的
5.1 数字人形象
最省事:直接用网页右上角的「形象」,上传一张正面照或一段 5~10 秒的小视频,生成完点「使用」就切过去了,不用重启容器。

素材要求(很重要,选错了效果会很怪):
视频(推荐):5~10 秒、嘴唇闭合、固定机位、单人、光线均匀、半身或全身入镜、有轻微呼吸/眨眼
照片:正面、单人、脸占画面 1/3 左右、背景干净
⚠️ 手托腮、手压下巴的姿势不要用 —— 口型模型只替换嘴部区域,手越过分界线会出鬼影。
⚠️ 脸太小也不行 —— 人脸在画面里占比不够,生成出来的人脸图会小于 256×256,Wav2Lip256 会直接报错Kernel size can't be greater than actual input size。
用命令行的话(从 assets/idle.mp4 生成):
bash scripts/03-准备形象.sh --from-idle
# 然后把 .env 里的 AVATAR_ID 改成生成出来的名字
bash scripts/05-停止.sh && bash scripts/04-启动.sh5.2 声音(音色)
部署包里已经带了一份可直接用的参考音色(voice/ref/ref.wav + voice/ref/ref_text.txt)。想换成自己的:
录 5~15 秒:干净、无背景音、单人、语气自然(别念稿)
存成
voice/ref/ref.wav把录音里说的每个字写进
voice/ref/ref_text.txt(语气词「嗯」「啊」也要写)统一格式成 16k / 单声道:
docker run --rm -v "$PWD/voice/ref:/r" linuxserver/ffmpeg \
-i /r/ref.wav -ac 1 -ar 16000 -c:a pcm_s16le -y /r/ref_16k.wav
mv voice/ref/ref_16k.wav voice/ref/ref.wav也可以用预置音色现场生成:
bash scripts/12-生成参考音色.sh Serena # 温柔年轻女声情绪会一起被克隆。参考音频里带委屈和撒娇,克隆出来就全是这个基调。想要中性一点,就录一段平铺直叙的。
5.3 人设
设置抽屉里的 INSTRUCTIONS 就是人设,改完点「保存」即时生效,不用重启。
人设三条硬规则(照抄就行):
每次两三句 —— 语音场景说太长听着累,合成时间也线性增加
不用 Markdown / 列表 / 表情符号 —— 这些会被逐字念出来(
*会念成"星号")口语化 +
/no_think—— 关掉思考模式,回复更直接

6. 自检(跑完看一眼心里有底)
bash scripts/06-分层自检.sh四关:
第 ③ 关会产出一段 data/logs/tts_sample.wav,下载下来听:像你的声音就成了。
7. 出问题看这里
日志位置:data/logs/voice.log(语音链路)、docker logs -f cybergirl-livetalking(数字人)。
8. 常用命令
docker compose ps # 状态
docker compose logs -f voice # 语音链路日志
docker logs -f cybergirl-livetalking # 数字人日志(看 fps)
docker logs -f cybergirl-llm # 大模型日志(看 token 速度)
bash scripts/05-停止.sh # 停止
docker compose up -d # 再启动
bash scripts/04-启动.sh # 启动(带就绪等待,输出打开地址)
docker compose down # 卸载(模型还在目录里,删不删随你)升级/改代码:代码是挂载进容器的,改完 docker compose restart voice 就行,不用重建镜像。
9. 目录结构(知道东西在哪,排查方便)
<项目目录>/
├── docker-compose.yml 三个容器的编排
├── .env 你的配置(端口、模型、路径都在这)
├── app/ 语音链路服务 + 网页前端(挂载,改完即生效)
├── persona/ 人设文件
├── scripts/ 各种脚本(00 预检 / 01 下模型 / 02 构建 / 04 启动 / 06 自检 …)
├── assets/ 数字人形象底版视频 idle.mp4
├── voice/ref/ 参考音色 ref.wav + ref_text.txt
├── models/ 模型(脚本下载,不进镜像,删容器不丢)
├── livetalking/ 数字人引擎的权重和形象
│ ├── models/ wav2lip.pth、s3fd.pth
│ └── data/avatars/ 形象目录(full_imgs/ + face_imgs/ + coords.pkl)
├── data/ 日志、缓存
└── certs/ 自签证书(跑 09 脚本后生成)10. 进阶:全身出镜 + 跳舞
实时层用的是 Wav2Lip256,只替换嘴部区域,所以实时状态她不会做大幅动作。
想要「远离镜头、全身入镜」:用 ComfyUI 生成一段全身 idle 底版,重新做形象即可(做法见包内 docs/全身与舞蹈复刻-设计.md)。
想要「跳舞」:ComfyUI + Wan Animate 2 做动作迁移,或者 Wan Dancer 自己编舞,生成 mp4 放进 library/dances/,网页「表演」里点播。
bash scripts/11-下载ComfyUI模型.sh # 补 ComfyUI 侧权重(约 25.6GB)
bash scripts/10-复刻舞蹈.sh --video 你的舞.mp4 --ref 她的全身图 --name 我的舞生成期间 GPU 占满,实时聊天会卡 —— 先
bash scripts/05-停止.sh,生成完再bash scripts/04-启动.sh。
收尾
跑成功的话,你现在有了一个:
不联网、不要 Key、不花钱的本地 AI 女友
你说一句她回一句,首响 1~2.5 秒
声音是克隆的,形象是能换的
NAS 重启自动拉起(三个容器都设了
restart: unless-stopped)
遇到问题,把 bash scripts/06-分层自检.sh 的输出贴出来,问题基本都能定位。
说明:整套由开源组件组成(llama.cpp / Qwen3 / faster-whisper / Qwen3-TTS / LiveTalking / Wav2Lip),不涉及任何破解或付费资源。默认形象和参考音色都是脚本生成的。换自己的形象和声音时,也请用自己的素材。
致谢:本文部署思路参考了零度博客的《AI 赛博女友!本地部署教程》,并结合 NAS 与 Docker Compose 环境进行了改造和实测,特此感谢原作者的分享。



