# AI 桌面陪伴助手 · 实施规划 v2.4 > 📁 **代码已与本文档分离**(2026-09-15)。本目录(Obsidian 笔记库)只保留 > 方案、P0 报告和硬件记录;所有代码在 **`~/workspace/ai-companion/`** > (独立 git 仓库),包括:助手本体、TTS 服务、P0 基准脚本、ESP32 固件、 > `README.md`(代码说明)、`部署说明.md`(运维)——这两份也在代码仓库。 > > 更新:2026-09-15。状态:**助手本体已在服务器上实现并跑通**(端到端测试 16/16); > **Pipecat 传输层实验已做,传输层通、帧路由未通**(见 §10 与 > `P0-Pipecat传输层实验报告.md`)——**A/B 架构方案待定**,这是当前卡点; > 设备侧(N5095 外设桥、ESP32)尚未开始。 > 已确认:Ubuntu 24.04;N5095 / 16G 作为**客户端**;助手本体部署在 cadadmin-server(i9-14900K / 3090);麦克风、音箱、摄像头均未购;屏幕已于 09-14 到货并点亮(2.8 寸 ILI9341 触摸屏,非原定的 1.28 寸圆屏)。 > 核心目标:听得清、回应及时、可打断、表情明确、转头自然。 > 最新外形要求:屏幕不用太大;摄像头水平可用旋转总行程至少180°,按正前方左右各90°理解,不是镜头视场角180°。 > 原方案备份:AI桌面陪伴助手方案.v1备份.md。本版指标为拟定目标,不是已测性能。 > > **本版重大变更:架构从"单机"改为"服务器即助手、本地为客户端"。** > > 起因要说得准确:**N5095 并非跑不动本地语音**。实测证明它能跑 > Zipformer STT(RTF 0.22)和 Piper TTS(RTF 0.36),是一条可用的低配本地链路。 > 真正的问题是——**它达不到当前的目标音质与延迟**:音质令人满意的 > Qwen3-TTS 在 N5095 上 RTF 高达 1.86(慢于实时),而服务器上只有 0.09。 > 所以是"本地语音可用但不够好",不是"本地语音不可行"。 > 详见 `P0-语音基准报告.md`。 > > 变更后 N5095 只做 USB 外设接入与网络桥接,**不运行 STT/TTS/LLM 等重型推理** > (轻量推理仍在本地,见 §1)。 > 详细部署现状见代码仓库里的 `部署说明.md`,实测数据见本目录的五份 P0 报告。 ## 1. 范围与待讨论事项 第一版做好中文语音、屏幕表情、按用户隔离的记忆;同一轮开发加入多人脸跟踪、轮流交互和显式身份切换。录入式人脸识别作为可关闭的试验功能,实测达标后用于身份建议;重叠说话分离、可靠声纹认人不作为首版要求。视觉问答和米家后续加入。 | 项目 | 状态/建议 | 影响 | |---|---|---| | **主机分工** | **服务器(cadadmin-server)跑助手本体;N5095 降为客户端/外设桥** | **v2.4 重大变更,见 §2** | | 服务器 | i9-14900K / 32线程 / 188G / RTX 3090 24G,局域网延迟 1–2ms | 与 N5095 同局域网,1ms 级 | | N5095 角色 | USB 麦克风阵列、USB 摄像头、DoA 读取、WebRTC 客户端、串口给 ESP32 | **不运行 STT/TTS/LLM 等重型推理**;本地仍跑 VAD、唤醒词、人脸检测等轻量推理(这些延迟敏感或依赖本地硬件,放服务器反而更慢) | | 使用人数 | 本人使用为主,同时考虑多人扩展 | 首版基础多人,身份与会话从一开始分离 | | 新增预算 | 服务器为复用已有机器,不算追加采购 | 音频优先,其次屏幕,再机械 | | 操作系统、现有外设 | Ubuntu 24.04 已确认;麦克风、音箱、摄像头都没有;屏幕已到货 | 需采购音频与摄像头 | | 使用距离 | 待确认;暂以桌面约0.5~2米作为测试设计假设 | 不能当作已确认需求或性能保证 | | 屏幕 | **已到货:2.8 寸 ILI9341 触摸屏**(与原定 1.28 寸圆屏不符,待确认最终形态) | 见 `ESP32-S3与2.8寸SPI触摸屏接线记录.md` | | 摄像头旋转 | 水平可用总行程至少180°,正前方左右各90° | 优先屏幕与摄像头共用一轴头部,麦克风固定底座 | | **LLM** | **DeepSeek `deepseek-flash`(云端,关思考)**;本机 Qwen3.8-27B 已停用 | 首 token 0.8–1.2s;月费来自 LLM 与按需视觉 | | **TTS** | **Qwen3-TTS 1.7B(服务器 3090)**,Serena 音色 + 轻快活泼指令 | RTF 0.55–0.94;首音延迟的主要来源 | | **STT** | Zipformer 中英双语 int8(CPU)——**仍是 2023 年模型,中文识别率未测** | v2.4 未替换,遗留项 | | 米家设备 | 到扩展阶段收集型号、地区、网关/HA情况 | 集成与采购选择 | | 结构 | 屏幕/摄像头到货后称重,确认加工条件 | 电机、支架、电源 | ## 2. 总体架构 ```text ┌─ cadadmin-server(172.16.1.101)—— 助手本体 ────────────────┐ │ 会话引擎(身份 → 记忆 → LLM → TTS) │ │ ├─ LLM:DeepSeek deepseek-flash(云端,关思考) │ │ ├─ TTS:Qwen3-TTS 1.7B(RTX 3090,独立 HTTP 服务 :18040) │ │ ├─ 传输:Pipecat SmallWebRTC :7860(实验,帧路由未通) │ │ ├─ STT:Zipformer 中英宽带 int8(CPU 4 线程) │ │ ├─ 记忆:SQLite(按 owner_user_id 强制隔离) │ │ └─ 身份:手动切换 + 访客兜底 │ └────────────────────────────────────────────────────────────┘ ▲ WebRTC(实验中) ▲ 手机/浏览器客户端 │ 局域网 1–2ms │ ┌─ N5095 / 16G —— 客户端 / 外设桥 ───────────────────────────┐ │ USB 麦克风阵列(固定底座)──处理后音频、DoA──┐ │ │ USB 摄像头(随头部转动)──本地图像───────────┤ │ │ ├─ DoA 读取(本地,**不过网**——转头回路延迟敏感) │ │ ├─ 本地人脸检测 / 目标锁定 → 角度控制(不依赖LLM响应) │ │ ├─ 插话检测、音频编解码、WebRTC 客户端 │ │ └─ USB 串口 115200 → ESP32 │ └────────────────────────────────────────────────────────────┘ │ ▼ ESP32-S3:本地表情、运动轨迹、限位、心跳超时处理 ├─ SPI → 2.8寸 ILI9341 触摸屏(已到货) └─ 控制信号 → 一轴执行器(独立合适供电支路) ``` - **分工原则:延迟敏感且硬件绑定的留在本地**(音频进出、唤醒、VAD、 插话检测、DoA、表情/动作),**算力密集的放服务器**(STT、TTS、LLM 调用、记忆)。 - Pipecat作为唯一会话管线框架。服务器侧用 WebRTC 传输(SmallWebRTC), 它**自带** AEC、抖动缓冲、丢包恢复的实现,省去了自行编写。 [传输选型](https://docs.pipecat.ai/client/concepts/choosing-a-transport) - ⚠️ **但"WebRTC 自带 AEC"不等于 AEC 问题已解决。** 框架有实现,不代表 链路配置对了。至少要满足: 1. N5095 的 WebRTC 客户端把**麦克风采集**和**扬声器播放参考信号**都正确 送入音频处理模块——播放参考缺失或时间戳不对齐,AEC 就不会工作; 2. XVF3800 自身的 AEC 路由要实测(它自己也会做回声消除,两处 AEC 串联可能互相干扰,需要确认该用哪一个、另一个是否要关); 3. 打断检测依赖 AEC 的输出质量,AEC 不对,打断就会误触发或漏触发。 **P2 的 AEC 项保持"待验证",在真实麦克风+音箱接上并实测前不算通过。** - 不叠加Vision Agents:它是Stream的独立框架,不是Pipecat官方扩展。人脸检测另选轻量本地模型。[项目官方仓库](https://github.com/GetStream/vision-agents) - STT/TTS采用可替换适配器(本地/远端两种后端已实现)。LLM 为 DeepSeek `deepseek-flash`;这是可更新别名,实施时记录实际版本并做流式/工具调用验证。 **实测必须显式关闭思考模式**,否则多轮与工具回灌一律 400。 [官方公告](https://deepseek.com/news/deepseek-v4-1-flash/) · 详见 `P0-DeepSeek验证报告.md` - **视觉跟随在客户端做**(N5095 本地),先试640×480、5~10fps, 按CPU、温度和延迟调整,未承诺该帧率已可达到。视觉问答按请求抓拍和缩放上传, 不持续发送视频。 - CPU标称TDP不能当整机常开功耗;实际插座功率、风扇噪声、温度在原型阶段测量。 - **降级策略**:服务器或网络失效时,本地按键、静音和屏幕继续工作; 建议保留一个极小的本地 TTS 兜底(piper-x_low 仅 20MB、RTF 0.17), 用于播报"网络断了"——这是设备失联时唯一能出声的东西。 ### 视频采集与框架边界 - Ubuntu由单一CameraService通过OpenCV/V4L2打开摄像头,维护带单调时间戳的最新帧;检测、身份候选和视觉问答共享采集源,不分别抢占设备。 - 本地检测以低分辨率/限帧消费最新画面,积压时丢旧帧;按需问答读取足够清晰的新帧并缩放编码。必要时从较高分辨率采集再降采样,实际USB带宽和解码负载需测试。 - Pipecat通过自定义适配器接入图像帧/取图工具。模型支持图像不等于当前Pipecat适配器已支持该模型的图像格式;P0补测纯文本流式、图片输入、工具调用、取消/超时四条路径。图像不通过时先修适配,不因此更换整套框架。[Pipecat DeepSeek服务](https://docs.pipecat.ai/api-reference/server/services/llm/deepseek) - 图像请求带request_id、拍摄时间和会话轮次,过期帧拒绝使用,图片不无限留在对话历史中。摄像头拔出后禁用视觉,语音保持可用。 - 音频回调只做轻量读写;识别、合成和视觉推理在受控工作线程/进程执行,不能阻塞会话事件循环。各队列有限长,TTS限制预生成量。 ## 3. 实施阶段与验收 阶段通过后再扩大范围,不预先给出未经验证的固定工期。 **进度列说明**:✅ 完成 · 🔶 部分完成 · ⬜ 未开始 | 阶段 | 实施内容 | 拟定完成标准 | 进度 | |---|---|---|---| | P0 环境与服务 | 盘点Ubuntu版本/USB;固定依赖;本地STT/TTS基准及DeepSeek请求 | 样例音频/文本先测试;设备到货后10次录放测试;记录RTF、首音延迟、CPU和内存 | 🔶 **环境盘点、STT/TTS/LLM 基准、DeepSeek 四条路径均已实测**;缺 10 次录放(无音频硬件)。**STT 中文识别率仍是未闭合项** | | P1 基础语音 | 按键启动;流式回复;停止按钮;超时提示 | 20轮固定中文用例至少18轮完成;短问答结束到首段有声回复,目标P50≤2秒、P95≤4秒 | 🔶 **链路已跑通**(文本/音频 → STT → 记忆 → LLM → TTS → 音频,16/16 通过);缺实时音频进出、按键、20 轮用例。首音实测约 1.5s,加 VAD 后 **2.0–2.2s,卡在目标线** | | P2 外放与表情 | AEC、打断、唤醒;听/想/说/离线/静音表情 | 指定距离音量下20次打断至少18次成功;检测到有效用户语音至停播目标≤300ms;30分钟外放无自我触发;运行2小时稳定 | ⬜ 未开始。**AEC 待验证**——WebRTC 有现成实现,但需要麦克风采集与播放参考正确送入、且 XVF3800 自身的 AEC 路由要实测(见 §2) | | P3 多用户记忆 | 用户档案、显式切换、偏好/摘要/修改/遗忘 | 两个用户同一问题得到各自偏好;未知用户无私人记忆;重启保留、更正/删除有效 | 🔶 数据层已实现并测试(身份隔离、越权删除、级联清理、候选不参与检索共 28 项);缺"修改/遗忘"的用户入口 | | P4 一轴转头 | 静态DoA/人脸验证,再接电机;锁定和限位 | 左/中/右各10次唤醒至少27次转向正确区域;轻微移动不频繁摆动;断联/重启不撞限位;运动时重复P2测试 | ⬜ 未开始,待摄像头与电机 | | P4a 基础多人/身份试验 | 2~3张脸跟踪;轮流发言;可选录入式人脸识别 | 20次轮流唤醒至少18次选中正确目标;同方向/交叉/遮挡时允许拒判;未知人、身份切换不得串记忆 | 🔶 `IdentityProvider` 接口与手动切换已实现(人脸候选**不自动切换**已测试);缺人脸检测 | | P5 扩展 | 按需视觉问答;HA接一盏灯 | 检查10次视觉请求;开关灯、离线、超时均反馈真实结果 | 🔶 工具调用链路已验证(DeepSeek 与本机 vLLM 均通过);缺米家与视觉 | 验收记录距离、背景声音、播放音量、网络、模型版本、样本数。目标用于定位瓶颈,不保证所有环境都达到。耳麦通过不能代替远场外放验收。 **v2.4 新增的验收项**: - **首音延迟必须按部署机(服务器)测**,不能沿用开发机的数字。 同一份代码在 N5095 上首音 1980ms,在服务器上 179ms(vits 时代)。 - **TTS 与 STT 的线程配置分别测**。实测两者线程扩展方向相反: STT 超过 2 线程急剧退化(N5095 无 AVX2,4 线程慢 3.9 倍), TTS 是线程越多越快。不能沿用一套配置。 - **换语音模型时必须重测首音延迟**。Qwen3-TTS 比 vits-zh-ll 音质好一个档次, 但首音从 179ms 涨到 1545ms(8.6 倍)。音质与延迟在这个硬件上是此消彼长的。 P2的30分钟/2小时测试仅是初步门槛,不能证明长期稳定。阶段通过后做一次正常音量下至少8小时运行:记录误唤醒次数、内存增长、音频欠载、重连及温度。P4a把“转对人”和“认对身份”分开统计,额外报告误认、拒判、切换次数,不用总成功率掩盖串身份。测试人数不足时,多用户记忆可用模拟身份测试,人脸/发言者匹配则明确标记未实测。 ## 4. 分批采购清单 > **v2.4 更新(2026-09-15)**:ESP32-S3 开发板与屏幕**已于 09-14 到货并点亮**, > 但屏幕是 **2.8 寸 ILI9341 触摸屏(240×320)**,与原定的 1.28 寸 GC9A01 圆屏不符。 > 接线与测试程序见 `ESP32-S3与2.8寸SPI触摸屏接线记录.md`。 > **最终屏幕形态待确认**——这会影响外壳与转台结构设计,越早定越省返工。 > 下列清单已按现状调整。 本表未报价:用户预算尚未确定,具体SKU和商家现价需核价。**当前实际还缺的只有音频(麦克风阵列+音箱)和摄像头。** ### 第一批:已基本到位 | 物品 | 数量 | 状态 | |---|---:|---| | ESP32-S3开发板 | 1 | ✅ 已有 N16R8(带 PSRAM) | | 屏幕 | 1 | ✅ 已有 2.8 寸 ILI9341 触摸屏(**非原定 1.28 寸圆屏**) | | USB数据线 | 按缺口 | 按缺口补 | | 杜邦线/面包板/绝缘固定件 | 1套 | 按缺口补;只用于逻辑低电流,不走电机大电流 | | 瞬时按键 | 2 | 待购;会话停止、软件静音,不宣称物理断麦 | 若最终决定用 1.28 寸圆屏形态,仍需单独采购 GC9A01 模块;若接受 2.8 寸方屏,则本批已完成。 两种形态的表情素材、外壳和结构设计都不同,**先定形态再动结构**。 ### 同期优先核价:音频套件 | 物品 | 数量 | 优先候选/要求 | 下单条件 | |---|---:|---|---| | USB麦克风阵列 | 1 | Seeed reSpeaker XVF3800 USB 4-Mic Array成品板 | 核对具体SKU、USB固件、目标系统、DoA例程、录放音通道、输出与附件;报价合适再买 | | 有源小音箱 | 1套 | 需新购;3.5mm模拟输入,体积小,合适独立供电 | 与阵列输出匹配;初期不选只能走USB音频/蓝牙的音箱 | | 音频线 | 按缺口 | 匹配两端的3.5mm音频线 | 查套装是否自带 | 阵列官方资料列出USB音频、DoA主机控制、可接耳机/有源音箱的AUX输出。采购成品板,不能仅凭XMOS芯片名替代。[Seeed官方文档](https://wiki.seeedstudio.com/respeaker_xvf3800_introduction/) 拟定路径(v2.4 修正:TTS 已不在 N5095): ``` 服务器 Qwen3-TTS → WebRTC → N5095 → 阵列 USB 播放端点 → AUX → 有源音箱 ``` AEC 需要实际播放参考;同一板卡也要验证固件/通道配置,不能假定接上即成功。[XMOS官方指南](https://www.xmos.com/documentation/XM-014888-PC/pdf/xvf3800_user_guide_v3.2.1.pdf) **注意**:播放链路跨了服务器和客户端两侧,AEC 所需的"播放参考信号"必须在 N5095 本地可得(即从阵列 USB 播放端点或本地播放缓冲取),**不能依赖服务器侧的回放时钟**—— 两侧时钟不同步,参考信号就对不齐。[详见 §2 的 AEC 说明] 不为了阵列额外购买XIAO ESP32S3,主机直接接USB即可。最终内置喇叭/功放留到结构阶段,按接口、电压、阻抗和功率匹配。 ### 第二批:摄像头 - 需新购普通USB UVC单目摄像头1个,支持1080p及低分辨率模式。为早测多人/身份,可与第一批一起买;无需增加第二台摄像头。 - 优先小巧轻便、可固定、柔软线缆、有视场角资料的型号;近距离物品问答需核对最近对焦距离。 - 摄像头随头部水平旋转,可用总行程至少180°(左右各90°)。这是机械旋转范围,不要求购买180°鱼眼镜头。单摄像头只覆盖当前朝向的视野,转动可依次查看不同方向,不能同时看全180°范围。 - 首版多人按当前视野内、轮流发言验证。镜头选型兼顾多人覆盖与脸部像素,过广视角会让远处脸变小。 - 不需要双目/深度相机和ESP32-CAM。摄像头不影响P0~P3先行。 ### 第三批:机械结构确定后购买 | 物品 | 为什么暂缓 | 解锁条件 | |---|---|---| | 舵机/步进 | 已要求可用行程≥180°,重量/重心/噪声未知,暂不锁MG996R/DS3218 | 称重后选型;额定/机械行程要有软限位余量,实测可用行程≥180° | | 电机电源/降压 | 电压及启动/堵转电流依赖电机 | 数据表确认,峰值留余量 | | 转台/轴承/支架/外壳 | 安装孔、重心、线缆弯曲需配合 | 原型尺寸确定 | | 步进驱动、回零开关、编码器 | 取决于执行器及是否需要实测位置 | 电机方案确定 | | USB Hub | 主机可能已有足够接口 | P0盘点确认不足 | | 米家网关/新设备 | 兼容性依型号地区而异 | 现有设备验证后再定 | 当前不采购:新主机、GPU、双目相机、HC-05蓝牙、第二块视觉MCU、电池系统。 ## 5. 音频与陪伴行为 ### 语音方案(v2.4 已实测定稿) STT是语音转文字,TTS是文字转语音。**v2.4 起重型语音推理(STT/TTS/LLM)全在服务器**, N5095 只做音频进出与本地轻量推理。 准确的表述是:**N5095 能完成低配本地语音,但达不到当前的目标音质与延迟**。 实测它能跑 Zipformer STT(RTF 0.22)和 Piper TTS(RTF 0.36);而音质达标的 Qwen3-TTS 在它上面 RTF 1.86(慢于实时),在服务器上只有 0.09。见 `P0-语音基准报告.md`。 | 模块 | 采用方案 | 实测 | 备选与限制 | |---|---|---|---| | VAD | 本地轻量VAD | — | 单独处理唤醒和说完判断 | | 唤醒 | sherpa-onnx中文关键词检测作为候选 | — | 单独模型/词表,用户确定称呼后调阈值;普通STT不会自动等于唤醒引擎 | | STT | **Zipformer 中英双语 int8**(服务器 CPU 4线程) | RTF 0.03 | ⚠️ **最大功能风险**:仍是 2023 年模型,**中文真人识别率至今无可信数据**。音频硬件到手后优先录制本人语音算 CER,并与 SenseVoice / faster-whisper large-v3 对比(已下载待测) | | TTS | **Qwen3-TTS 1.7B-CustomVoice**(服务器 3090,独立 HTTP 服务) | RTF 0.55–0.94 | 音色 Serena + 指令"用轻快活泼的语气说"(用户试听选定)。备选:vits-zh-ll(RTF 0.09 但音色不满意)、CosyVoice 2/3(未测)。**首音延迟需按真实链路重测**,见 §10 | | LLM | **DeepSeek `deepseek-flash`**(云端,关思考) | 首 token 0.8–1.2s | 本机 Qwen3.8-27B 的 vLLM 已停用——GPU 让给 TTS。可改 `AI_LLM_PRIMARY=local` 切回,但切回后 GPU 不够跑 Qwen3-TTS | [sherpa-onnx官方功能](https://k2-fsa.github.io/sherpa/intro.html)、[SenseVoice模型说明](https://k2-fsa.github.io/sherpa/onnx/sense-voice/pretrained.html)、[中文VITS模型](https://k2-fsa.github.io/sherpa/onnx/tts/pretrained_models/vits.html)。以上说明模型/运行时支持,不构成性能保证。 **音质与延迟的取舍(v2.4 的核心权衡)**:同一台服务器上 | 配置 | 首音延迟 | 音质 | |---|---|---| | 本地 Qwen3.8-27B + vits-zh-ll | **179 ms** | 用户不满意 | | DeepSeek + Qwen3-TTS | **1545 ms** | 用户试听选定 | 用约 1.4 秒的首音延迟换音质。这个取舍是可逆的(改配置即可), 但**在 24GB 显存下两者不可兼得**。后续若装成 flash-attn 或实现真流式, 延迟可望收回一部分——详见 `P0-Qwen3TTS报告.md`。 P0测试10~20条不同长度中文,覆盖数字、日期、中英混读。记录STT识别错误、语音结束后等待时间、TTS首段耗时和RTF(生成/处理耗时除以音频时长,低于1表示平均快于实时)。音频与视觉并发时重测,限制推理线程和视觉帧率,避免CPU争抢。 **v2.4 补充:STT 与 TTS 的线程配置必须分别调**——实测两者线程扩展方向相反。 TTS按语义短句合成并播放,不把整段一次合成称为原生流式;分句仍要测首音等待、接句停顿及取消响应。本地效果不达标时先调整模型/线程/分句,再讨论是否开启云端后备,不自动发送音频到第三方。 - 本地唤醒引擎保持工作;闲置时暂停完整识别、降低视觉频率。保留短时内存音频预卷(初始0.5~1秒)避免唤醒后吞掉指令开头,静音时清除缓冲。[关键词检测官方文档](https://k2-fsa.github.io/sherpa/onnx/kws/index.html) - 活跃会话内允许免唤醒接续,初始闲置超时20秒后回到待机,参数可调。多人时其他人的普通谈话不应自动抢占;以唤醒/显式切换为主要入口。 - 记录阵列实际采样率、通道和采样格式,STT/TTS边界显式重采样;避免把原始麦克风/回放参考通道当成处理后语音。Ubuntu固定设备标识和录放音路由,不依赖重启后会变化的默认声卡序号。 - DeepSeek输出区分最终回答、思考内容、工具调用与工具结果,只将面向用户的回答交给TTS;数字/日期/单位和Markdown做轻量朗读归一化。 - P1按键启动,P2再加唤醒。语音活动检测、说完判断、唤醒是不同环节。 - 打断时取消旧回复生成、TTS和待播缓冲,丢弃旧轮次迟到结果,不能暂停后继续放旧回复。 - 分别测安静、设备外放、背景音乐、风扇噪声;P4加入运动噪声,装壳后重测。 - 默认简短自然回复;等待时及时更新表情,长回复按语义分段播放。 - 明确待机、在听、思考、说话、静音、离线状态。专注/夜间默认不主动开口,主动问候后续再加。 - 网络失败给简短提示,本地按键、静音和屏幕继续工作,不反复播旧回复。 - 麦克风、音箱、电机尽量机械隔离;软件静音和真实物理断麦需明确区分。 ### 客户端/服务器的音频边界(v2.4 定稿) **决定日期:2026-09-15。这条边界决定 WebRTC 客户端的接口形态、 首音延迟预算和隐私边界,先定下来再写代码。** | 问题 | 决定 | 理由 | |---|---|---| | **传什么** | **闲置关闭,会话中持续上传**(含 0.5–1s 预卷) | 闲置时服务器**完全听不到**房间声音(隐私);会话中上传完整音频流,服务端才能做可靠的"说完判定"(方案 §5 要求)。折中方案见下 | | **唤醒** | **本地**(N5095) | 延迟低、断网也能响应;唤醒词模型轻量,N5095 跑得动。且它是"**注意力**的门",不是"音频传输"的门 | | **打断判定** | **客户端** | 三条理由:① WebRTC 的 AEC 本来就在客户端跑(需要播放参考),客户端的麦克风信号已是去过回声的;② 客户端**知道自己正在播什么**——"用户在助手说话时开口"本地就能判,不需要额外协议;③ 比服务端判定少一个网络来回,更容易满足 ≤300ms | | **断线/抖动** | WebRTC 内建 ICE restart 重连;抖动缓冲初始 **100–200ms**,实测调整 | 缓冲换稳定性,但**直接加首音延迟**,不能凭感觉设,必须实测 | #### 状态机 ``` 待机(不上传任何音频) │ │ 本地唤醒词命中 ▼ 会话中(持续上传音频 + 控制信号) │ │ 闲置超时 20s(可调) ▼ 待机(停止上传) ``` **关键点:唤醒控制的是"是否上传",不是"是否处理"。** 闲置时麦克风数据只进本地唤醒引擎,不出网。 #### 接口约定 **N5095 → 服务器** - 音频流:16kHz 单声道(WebRTC 内部用 Opus 编码) - `wake` 信号:本地唤醒词命中,会话开始 - `barge_in` 信号:用户在助手播放期间开口,服务端据此取消当前生成 - `identity_hint`:本地人脸候选(**仅建议,不自动切身份**,见 §6) **服务器 → N5095** - 音频流:TTS 合成结果(Qwen3-TTS 输出 24kHz,需重采样到播放端) - 状态事件:`listening` / `thinking` / `speaking` / `idle` —— 客户端据此渲染表情并判断"是否正在说话" #### 三条必须遵守的约束 1. **播放参考信号必须在 N5095 本地可得**。AEC 需要它,而服务器与客户端的 回放时钟不同步,不能依赖服务器侧的时间戳。从阵列 USB 播放端点或 本地播放缓冲取。 2. **预卷缓冲 0.5–1s**。唤醒/打断判定完成到开始上传之间有延迟, 不留预卷会吞掉用户指令的开头。静音时清除缓冲。 3. **会话中持续上传 ≠ 持续存储。** 服务端处理完即丢弃, 默认不落盘(方案 §8:"默认不存原始音视频")。 #### 这条边界的已知代价 - 会话中上传的是**连续音频**,包括用户的停顿、思考声和周围环境音。 服务器处理这些数据,但不存储。 - 客户端 VAD/唤醒的**误判会切掉话头**——这是"闲置不上传"的固有代价, 靠预卷缓冲缓解,不能消除。 - 抖动缓冲直接计入首音延迟。**做 WebRTC 客户端时必须一并测**, 否则首音延迟数字不完整(见 §10)。 ## 6. 朝向与机械 DoA是声源来向,不代表身份或距离。阵列固定底座,摄像头随头部转动。 状态:待机 → 有效唤醒 → 可靠DoA粗转 → 匹配人脸 → 锁定 → 丢失短暂保持 → 超时待机。多人先按轮流发言处理;同方向多人或重叠说话时保持目标/请求轮流,不凭DoA猜身份。 ### 多人能力与避免返工 - 基础多人:同画面维护多个临时track_id,记录位置和最后出现时间;用带时间戳的DoA与人脸位置关联当前发言目标,设置切换滞后。走位/遮挡后track_id可能变化,不能充当永久身份。 - 读取DoA时核对固件、波束选择和单位。XVF3800可返回多个波束角度,它们不是“几个人的身份/独立语音”;不要直接把第一个角度当发言者。优先用与当前处理后音频对应的选中波束,查不到可靠对应时允许拒判。[官方主机控制代码](https://github.com/respeaker/reSpeaker_XVF3800_USB_4MIC_ARRAY/blob/master/python_control/xvf_host.py) - 首版可靠身份入口:手动/对话确认切换到已建档的user_id;无法确认时使用unknown/guest。看到熟人不等于确认他正在说话。 - 显式报名字用于个性化档案选择,不是身份认证,不能据此解锁敏感信息或权限。信任等级与user_id分开,未来增加认证时不改普通偏好存储。 - 可选人脸试验:OpenCV YuNet检测+SFace特征作为候选,需用户主动录入、相似度阈值和未知人拒判。低频识别,平时跟踪;不用于门锁/支付或敏感身份认证。[OpenCV官方教程](https://docs.opencv.org/5.0/tutorials/dnn/dnn_face/dnn_face.html) - 注册特征本地保存,可查看和删除;识别不确定时不读取/写入私人记忆。群体会话默认仅共享明确公开的上下文,不主动播报私密记录。 - 预留IdentityProvider接口:输入时间戳、track_id、候选身份、置信度与来源;输出确认的user_id或unknown。后续声纹模块可接入此接口,不改记忆表结构。 - 声纹认人、声音与人脸可靠绑定、重叠语音分离属于更高难度阶段,不承诺通过增加一块阵列即可解决。第一版不做全场多人同时自由插话。 - 调试候选:3~5°死区、丢失保持1~2秒;速度/加速度实机调节。 - 坐标约定:从设备视角向右为正。标定阵列零点、摄像头中心、电机零点;统一DoA单位和方向。 - 人脸像素位置经相机内参/标定换算成相对角度,再加头部角度估计,不能把像素直接当绝对角度。 - 普通位置舵机先做平滑目标角度,不默认再加内环PID;无反馈时只有估计位置。 - 摄像头与麦克风有位置偏移,近距离DoA和画面方位不完全重合;先粗转,再以视觉纠偏。运动过程中按时间戳使用对应角度估计,避免拿旧图配新角度。无反馈舵机的首版身份匹配优先在稳定停转后进行。 - 步进需独立驱动器、明确STEP/DIR等接口,补回零/失步/软限位。 - 只做左右一轴,用户要求摄像头可用总行程至少180°;初始工作范围设为[-90°, +90°],屏幕与摄像头共用转动头部,麦克风固定底座。不要求连续360°旋转。 - 执行器实际机械行程须大于工作范围,以留软/硬限位余量;采购候选可考察标称270°的位置舵机,但型号、真实行程及噪声仍待核对。不能把普通标称180°舵机两端硬顶当作已满足要求,也不选无角度定位反馈的连续旋转舵机来直接代替位置舵机。 - 转轴/轴承承担结构负载;USB摄像头线、屏幕/MCU线缆在左右端点均要有弯曲余量,避免拉扯插头。有限180°往返先用柔软线缆和固定应力释放,不把滑环列为必需采购件。 - P4增加行程验收:实测摄像头左右端点至少相差180°,端点不碰撞、不拉线;低速往返100次检查线缆、USB掉线、抖动和供电。上电及重连仍须受限速和限位约束。 - MCU独立限位和断联停止;失联先停止更新轨迹,保持/卸力按结构实测,避免盲目断电造成坠落。 - 电机独立合适供电支路并正确共地,选合适线径/端子/去耦,不由ESP32板载电源或面包板供大电流。 ## 7. 串口协议与恢复 先用UTF-8单行JSON、换行分帧,字符串换行由JSON转义。115200起步。Linux可能ttyUSB/ttyACM,Windows为COM,使用可配置稳定设备标识。 ```json {"v":1,"seq":101,"type":"expression","state":"listening"} {"v":1,"seq":102,"type":"motion","yaw_deg":20,"max_speed_deg_s":25} {"v":1,"seq":102,"type":"ack","status":"accepted"} ``` - 表情和运动分开;转向目标最新值优先,不排队执行过时动作。 - 定义版本、字段范围、最大帧长(初始1KB)、坏帧丢弃和超时恢复。 - seq关联请求与确认;重试不能重复动作。accepted仅表示收到,无传感器不能报告实测已到位。 - 心跳500ms、失联2秒触发本地停止作为初始候选参数,实测后定稿。 - MCU重启发hello/能力信息,重连恢复当前状态,不回放历史运动。 - 字体和表情素材本地存储,串口不传全屏视频;WiFi/MQTT旁路暂不做。 ## 8. 记忆、费用与数据 P3用SQLite,Mem0和向量库不是前置条件。从建表起就包含users、sessions、memories;记忆带owner_user_id、scope(private/shared)、来源会话/轮次、时间和确认状态。查询和写入均强制身份过滤,未知身份只用访客临时上下文。视觉track_id、会话session_id、永久user_id分开。 | 类型 | 保存策略 | |---|---| | 明确偏好 | 结构化键值,来源、确认状态、更新时间,新值替换旧值 | | 临时事项 | 带时间与过期规则;记住事项不等于实现提醒调度 | | 对话摘要 | 限长、按需检索、可清除,不默认永久保存逐字稿 | - 支持记住/修改/忘掉/查看;模型推测不当作确认事实。 - 默认显式切换身份;人脸只提供候选,经当前发言者关联及确认后才启用对应记忆。切换身份清理上一人的私有检索上下文;异步任务绑定创建时的user_id/session_id,避免迟到结果写入新用户。 - 删除同时覆盖会话上下文、摘要和未来索引,防止删库后仍引用。 - 回复后异步写记忆,检索有超时;记忆失败不阻塞对话。 - 真实使用中检索不足再评估Mem0 + Chroma,支持不代表无需配置测试。[Mem0官方文档](https://docs.mem0.ai/components/vectordbs/dbs/chroma) - 本地STT/TTS记录耗时与资源,不产生语音API调用费;统计LLM/按需视觉及未来云端后备费用,按实际日用量估月费并讨论上限。 - 默认不存原始音视频;视觉按需上传。密钥保存在本机配置,不写日志;提供静音和相机关闭入口。 ## 9. 米家接入 P5通过Home Assistant统一调用。按实际设备比较官方Xiaomi Home和Xiaomi Miot Auto,核实地区、网关、本地/云端路径,不提前采购网关。[小米官方集成](https://github.com/XiaoMi/ha_xiaomi_home) 先接一盏现有灯:意图 → 工具/实体白名单 → 参数校验 → HA执行 → 状态确认 → 回复。请求受理和设备已改变分开描述,超时明确未确认。高风险设备不默认加入。 工具执行与语音回复生命周期分开:用户打断播报不意味着已发出的设备操作被撤销。每次动作保留执行ID和状态;超时先查状态,不盲目重试;优先使用“设为开/关”而非“切换”以降低重复执行风险。摄像头中的文字和检索记忆作为数据,不能自行扩大工具权限。 ## 10. 运行维护与当前下一步 ### 已完成(截至 2026-09-15) - Ubuntu 24.04 已确认,USB 接口已盘点;依赖版本固定在代码仓库的 `requirements.txt` - 助手本体在服务器上实现并跑通,端到端测试 16/16(**用文件音频,非实时链路**) - STT/TTS/LLM 三方基准实测;DeepSeek 四条路径验证 - 记忆与身份的数据层实现并测试(88 项断言) - 代码剥离为独立仓库 `~/workspace/ai-companion/` ### 下一步顺序(按风险从高到低) 评审建议的思路——**先把风险最大的实时语音链路闭环**——认可并采纳。 但执行顺序要修正一处:**服务端目前没有任何客户端能连的入口**, 所以第一块不是客户端,是服务端的 WebRTC 入口。 > 核实过:代码里 `pipecat` 一次都没被 import 过(只在 `requirements.txt` 里), > 引擎是独立 asyncio,由测试脚本用文件音频驱动。**没有进程在监听 WebRTC 连接。** | # | 步骤 | 状态 | 能验证什么 / 不能验证什么 | |---|---|---|---| | 0 | **音频边界定稿** | ✅ 2026-09-15 完成(见 §5) | 决定了客户端接口形态 | | 1 | **服务端 WebRTC 入口 + 客户端骨架** | 🔶 **传输层跑通,帧路由未通**(见 `P0-Pipecat传输层实验报告.md`) | 已验证:信令、ICE、data channel、双向媒体轨。**未验证:帧进不到自定义处理器,引擎从未运行** | | 1b | **查清帧路由根因 → 定 A/B 方案** | **待办,当前卡点** | 根因决定 A 方案(Pipecat 只做传输)是否可行,还是必须转 B(按框架 service 模型重构) | | 2 | **N5095 客户端骨架** | 🔶 雏形已有(`client/test_client.py`) | 非 trickle ICE、data channel 心跳、文件音频实时发送、回复录音都已实现。**不做 AEC/播客参考/打断**——那些要真实声卡 | | 3 | **接入真实麦克风与音箱** | 阻塞:硬件未购 | 唯一能验证 AEC、打断、唤醒的一步,也是 P2 的全部内容 | | 4 | **STT 中文识别正确率** | 阻塞:需录音 | 见下"最大功能风险" | | 5 | ESP32 表情串口 | 硬件已到货,可独立推进 | 串口协议、表情渲染 | | 6 | 摄像头、多人跟踪、电机 | 阻塞:硬件未购 | — | ### 第 1 步的实验结论(2026-09-15) **传输层可用,但"把整条会话逻辑塞进一个 FrameProcessor"不是开箱即用的。** 自定义处理器被创建、`setup()` 被调用,但 **`process_frame` 调用次数为 0**—— 连自己注入的 StartFrame 都没进去。现象是"连接全对、音轨协商成功、 但助手永远不说话,且日志里一条错误都没有"。 **这对 A/B 决策的影响**:实验前判断"Pipecat 的价值收敛到传输层", 现在要修正——**传输层与自定义逻辑之间的接缝比预想的深**。 B 方案恰恰绕开这个接缝(用框架原生 service,不由一个处理器吞掉全部)。 **但根因未定位**,两种可能必须区分: - 若是接线错误(管线用法与 Pipecat 1.10 的实际约定不符)→ 接缝浅,A 仍可行 - 若是模型不兼容(该版本不接受这种用法)→ 接缝深,应转 B 实验同时挖出 **8 个 Pipecat 集成坑**(客户端需发 ping、offer 无媒体则不收集 ICE 候选、`run()` 要 WorkerParams、后台异常被静默吞掉等), 每个都表现为"什么都没发生",详见实验报告。 **第 2 步的边界要说清楚**:用合成音频能验证传输层,**验不了 AEC 和远场识别**—— 那些必须等真实麦克风和音箱。但传输层能提前跑通,硬件一到就能直接接, 不用同时调两件事。 **理由**:语音链路是全局瓶颈。它若走不通,后面的表情、转头做得再好也没用; 而它若走通,其余部分都是可分解的独立工作。 ### 最大功能风险:STT 中文识别率 Zipformer 的速度没问题(服务器 RTF 0.03),但**中文真人识别率至今没有可信数据**—— 自带的 4 段测试音频没有参考文本,而 TTS→STT 往返法在这个模型上不成立 (合成音频会触发大量重复字,真人录音却正常)。 音频硬件到手后应优先做:**录一组本人语音**,覆盖普通话、数字、日期、中英混读、 以及真实桌面距离(0.5–2m)与背景噪声,计算 CER,并**同时与 SenseVoice、 faster-whisper large-v3 对比**(两者已下载待测)。这是 P0 唯一未闭合的功能项。 ### 首音延迟必须按真实链路重测 当前 1545ms 是**非流式、无 VAD、无网络传输、无播放缓冲**的链路数据。 真实对话还要加上:分句等待、WebRTC 传输抖动、N5095 侧播放缓冲。 **必须测 P50/P95,不能只看 RTF。** 加 VAD 后已处在 2 秒目标边缘, 这些额外开销很可能把它推过线。 ### 运维待办 - **没有常驻服务**:TTS 服务和助手本体都没有 systemd 开机自启 (方案要求 P2 后配置)。服务器重启后需手动拉起 - 日志保存耗时、错误、费用、连接事件并轮转,不默认保存完整私人对话 - P2 后配置异常重启、音频/串口重连,测试拔插、断网、主机重启恢复 - 记录板卡 SKU、固件、通道、引脚及接线图(ESP32 部分已有记录) - 模型文件授权与运行时授权分别记录(Qwen3-TTS 为 Apache-2.0; Matcha 用的 Baker 数据集**标注仅限非商用**,若将来产品化需换音色) ### 仍未确认 - **屏幕最终形态**:2.8 寸方屏 vs 原定 1.28 寸圆屏,影响外壳与转台设计 - **使用距离**:暂按 0.5–2m 设计,未实测确认 - 音频套件与摄像头的具体 SKU 与核价