Files
ylhan bdd6bfb63d AI桌面陪伴助手:方案更新至 v2.4,代码剥离到独立仓库
代码剥离
- 助手本体、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:四条路径验证

两份旧报告加了状态说明(结论仍有效但选型已变),
并修正了随代码移走而失效的引用与复现命令。
2026-09-15 11:08:56 +08:00

170 lines
6.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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)。
复现:
```bash
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) |
## 二、思考模式:可关,且必须关
关闭方式(**已实测有效**):
```json
"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 延迟,这对"回应及时"是关键。
## 九、工程陷阱记录
1. **本机 vLLM 思考字段名是 `reasoning`**,不是 `reasoning_content`(DeepSeek 的写法)。
2. **只接受一条 system 消息**,多条直接 400。
3. **开思考会吃掉 `max_tokens` 预算**,导致回答被截断。
语音场景下 `max_tokens` 本来就要设小,这个问题会被放大。
4. N5095 上系统 `python3` 无 PIL,测试脚本要用 venv 里的解释器。