代码剥离 - 助手本体、TTS 服务、P0 基准脚本、ESP32 固件移到 ~/workspace/ai-companion (独立 git 仓库),本目录只留方案、P0 报告与硬件记录 - 因此删除 esp32_smile_test.ino / esp32_touch_eyes.ino - 同时移除已失效的 .gitignore(原本只为挡住模型体积,代码已搬走) 方案 v2.4 重大变更:架构从"单机"改为"服务器即助手、本地为客户端" - 起因:实测 N5095 无 AVX2,跑不动可用的语音模型 - N5095 降为客户端/外设桥,只做 USB 接入、DoA、WebRTC 客户端 - 实施阶段表加进度列(P0/P1/P3/P4a 部分完成),新增三条验收要求 - 采购清单按实际到货更新,标出屏幕形态待确认(2.8寸 vs 原定1.28寸圆屏) 技术栈(实测更新) - LLM:DeepSeek deepseek-flash(云端,必须关思考);本机 27B 已停用 - TTS:Qwen3-TTS 1.7B(服务器 3090);vits-zh-ll 因音色不满意被替换 - STT:Zipformer int8 未变,中文识别率仍是未闭合项 新增两份 P0 报告 - P0-Qwen3TTS报告.md:TTS 换型过程与实测 - P0-DeepSeek验证报告.md:四条路径验证 两份旧报告加了状态说明(结论仍有效但选型已变), 并修正了随代码移走而失效的引用与复现命令。
6.0 KiB
P0 验证报告 · 本地 Qwen3.8-27B
⚠️ 状态:该服务已于 2026-09-15 停用。 原因:用户要求换更强的语音模型(vits-zh-ll 音色不满意), 而 3090 只有 24GB——27B 的 vLLM 占 21.5GB,装不下 Qwen3-TTS。 决定让 LLM 走云端 DeepSeek,GPU 让给语音。见
P0-Qwen3TTS报告.md。本报告的结论仍然有效(用于将来重启该服务,或评估其它本地 LLM): 关思考可让首 token 从 1390ms 降到 367ms;思考 token 会吃掉 max_tokens 预算; 只接受一条 system 消息;思考字段名与 DeepSeek 不同。
日期:2026-09-14。端点:http://192.168.0.20:18020/v1(vLLM,局域网 1.4ms)。
脚本:~/workspace/ai-companion/p0/verify_local_llm.py。结果:5/6 通过(未通过项为视觉,见 §5)。
复现:
set -a; . ~/.config/ai_assistant/env; set +a
cd ~/workspace/ai-companion && python3 p0/verify_local_llm.py
一、服务实况
方案文档假设 LLM 用 DeepSeek 云 API。实测发现局域网内已有可用服务:
| 项 | 值 |
|---|---|
| 服务 | vLLM(不是 llama.cpp,服务器上两套都有) |
| 模型 | Qwen3.8-27B-W4A16-AutoRound-fast(4bit 权重,16bit 激活) |
max_model_len |
65536 |
| 思考解析 | --reasoning-parser qwen3 |
| 到 N5095 延迟 | 1.4ms |
| 视觉 | 不支持(见 §5) |
二、思考模式:可关,且必须关
关闭方式(已实测有效):
"chat_template_kwargs": {"enable_thinking": false}
实测三种模式(同一个数学题,max_tokens=512):
| 模式 | 思考字数 | 完成 tokens | 首 token(回答) |
|---|---|---|---|
| 默认(不传) | 168 | 214 | 527ms |
| enable_thinking=false | 0 | 512 | 81ms |
| enable_thinking=true | 289 | 239 | 620ms |
开思考的代价不只是慢。看上面第二组数字:开思考时完成 tokens 只有 239,
而关思考是 512——因为思考 token 占用 max_tokens 预算。
在真实语音 prompt 上这个问题更严重(max_tokens=200):
| 模式 | 首 token(回答)P50 | 完成 tokens 中位 | 实际回答 |
|---|---|---|---|
| 关思考 | 367ms | 10 | 「物流显示昨天已签收。」 |
| 开思考 | 1390ms | 187 | 「我不」 ← 被截断 |
开思考时首 token 慢 3.8 倍,而且 200 的预算被思考吃光,回答只吐出两个字。
三、对比 DeepSeek 云 API
| 指标 | 本机 Qwen3.8-27B | DeepSeek deepseek-flash |
|---|---|---|
| 首 token(关思考) | 367ms | 786ms |
| 网络延迟 | 1.4ms | 含在 786ms 内 |
| 思考模式 | 可关(chat_template_kwargs) |
可关(thinking),但字段名不同 |
| 视觉 | ❌ 本实例不支持 | ✅ 可用 |
| 费用 | 0(本地) | 按 token 计费 |
| 数据 | 不出局域网 | 上传第三方 |
本机首 token 比云端快一倍多,因为省掉了网络往返与排队。
四、两个会改设计的集成约束
1. 思考内容的字段名不一致
| 后端 | 字段名 |
|---|---|
| 本机 vLLM | reasoning |
| DeepSeek | reasoning_content |
适配层必须两个都认。只按 DeepSeek 的字段名写,会静默丢弃思考内容—— 不报错,但"把思考念出来"或"思考占用显示"这类问题会莫名其妙出现。
2. 只允许一条 system 消息
发送两条 system 消息直接 400:
System message must be at the beginning.
这推翻了原记忆设计。原方案打算"检索到的记忆作为一条独立 system message 注入", 在本机 vLLM 上会直接失败。
修正后的做法:保持唯一一条 system 消息,每轮重建:
system = 人设 + 规则 + 本轮检索到的记忆
这其实更干净,也和"注入的记忆每轮替换而非追加"的要求一致。
五、未通过项:视觉
本实例返回:
At most 0 image(s) may be provided in one prompt.
模型目录 Qwen3.8-27B-W4A16-AutoRound-fast/ 只有 safetensors 与 config,
没有视觉塔,是纯文本部署。
服务器上另有视觉能力:~/models/mmproj-F16.gguf(928MB)配合
llama.cpp 的 128k_vision.sh,以及 llama.cpp 的 --mmproj 参数。
但那是另一套服务,需要单独起。
影响:方案 §P5 的视觉问答在本端点不可用,需要么另起一个带 mmproj 的 llama.cpp 实例,要么走 DeepSeek。留到 P5 再定。
六、其余验证项
| 项 | 结果 |
|---|---|
| 工具调用(关思考) | ✅ set_light({"room":"客厅","on":true}) |
| 纯文本流式 | ✅ |
| 端点与模型清单 | ✅ |
未测:取消/超时、长上下文退化、多客户端并发。
七、对 P1 延迟预算的影响
更新后的预算(用本机 LLM):
| 环节 | 耗时 | 来源 |
|---|---|---|
| VAD 判定说完 | 200–500ms | 估算,待实测 |
| STT 冲刷 | ≈0ms | 实测 |
| LLM 首 token | 367ms | 实测 |
| TTS 首音(vits-zh-ll @服务器) | 57–152ms | 实测 |
| 网络 | 1.4–2ms | 实测 |
| 合计 | 约 0.6–1.0 秒 |
方案 P1 目标 P50≤2 秒。余量超过一倍。
八、上下文建议
实测语音 prompt 约 406 tokens(人设 + 记忆 + 3 轮对话)。 建议按 4k tokens 设上限,超出时截断最旧的轮次:
- system(人设 + 本轮记忆):400–800 tokens
- 最近对话轮次:500–1000 tokens
- 当前输入:~50 tokens
vLLM 服务端的 max_model_len=65536 是全局配置,不需要为助手改动
(那是共享服务)——短上下文靠客户端控制实际 prompt 长度即可。
短上下文不只省资源,还直接降低首 token 延迟,这对"回应及时"是关键。
九、工程陷阱记录
- 本机 vLLM 思考字段名是
reasoning,不是reasoning_content(DeepSeek 的写法)。 - 只接受一条 system 消息,多条直接 400。
- 开思考会吃掉
max_tokens预算,导致回答被截断。 语音场景下max_tokens本来就要设小,这个问题会被放大。 - N5095 上系统
python3无 PIL,测试脚本要用 venv 里的解释器。