基线:姓名档案修复、疑问句防误写、搜索与传输测试

这是进入 Pipecat Flows 改造前的回滚点。
提交时已核对本地与线上代码 md5 一致(engine.py 7e97bad8、
bot_server.py 1cf543ae),所以这个提交就是当前线上状态。

内容(此前一直在线上运行但未提交的改动):
- 姓名档案:用户自报"我叫林"同步写 confirmed 姓名,重连后恢复
- 疑问句防误写:"谁吗"之类的疑问句不再被覆盖成姓名
- 新增 test_profile_memory.py(姓名档案专项回归)
- 新增 test_profile_live.py(需要真实 LLM 的现场测试)
- 搜索、身份、记忆测试的相应补充

背景:下一步要按 P1-Pipecat Flows Agent化与记忆改造交接.md
渐进引入 Pipecat Flows,交接文档明确要求先保护基线、不得
reset/checkout/覆盖现有改动。此提交即该基线。
This commit is contained in:
ylhan
2026-09-16 12:48:18 +08:00
parent 719fc13e48
commit 20cfaa6ef7
16 changed files with 408 additions and 11 deletions
+33 -2
View File
@@ -88,6 +88,7 @@ DoA 读取和网络桥接,不跑任何推理。
| `AI_TTS_INSTRUCTIONS` | `用轻快活泼的语气说` | 风格指令 |
| `AI_STT_THREADS` | `4` | **N5095 上必须改成 2**(见下) |
| `AI_MODEL_DIR` | `/home/yonglin/ai-companion/models` | 两边模型位置不同 |
| `AI_DEFAULT_USER_ID` | `u_ylhan`(默认) | 单用户原型的内部档案 ID;不能作为姓名展示 |
| `SEARCH_ENABLED` | `true` | 向 LLM 注册只读 `web_search` 工具 |
| `SEARXNG_BASE_URL` | `http://127.0.0.1:18080` | 私有 SearXNG,仅服务器回环可达 |
| `SEARCH_TIMEOUT_S` | `6`(默认) | 搜索总超时,失败后让模型明确降级 |
@@ -161,13 +162,17 @@ rsync -az --delete --exclude '__pycache__' --exclude '*.pyc' \
生效。不加限制就变成"每轮全量注入",不再是按需检索。
9. **"被引用的 user 必须存在"由存储层自己保证**(`_ensure_user_row`),
不能让每个调用点各自记得——漏一处就报 `FOREIGN KEY constraint failed`。
10. **内部 `user_id` 不是用户姓名。** 网页客户端当前把每个连接绑定到稳定档案
`AI_DEFAULT_USER_ID`,但提示词不得暴露该值。用户明确说“我叫林”时,称呼同步
写入用户表和 confirmed 姓名记忆;同步写是为了避免刚说完就断线导致后台任务丢失。
新连接从用户表恢复 `display_name`。自报姓名不构成身份认证,不改变 trust level。
### 依赖层
10. **qwen-tts 与 vllm-omni 依赖互斥**(transformers 4.57 vs 5.14、
11. **qwen-tts 与 vllm-omni 依赖互斥**(transformers 4.57 vs 5.14、
hf_hub 0.36 vs 1.31)。装同一个 venv 会互相破坏,实测把 vllm-omni 搞崩过。
所以 TTS 跑独立进程 + HTTP。
11. **flash-attn 装不上**:预编译 wheel 最高只支持 torch 2.9,本机是 2.14
12. **flash-attn 装不上**:预编译 wheel 最高只支持 torch 2.9,本机是 2.14
(vLLM 0.28 拉上来的);源码编译要从 GitHub 拉依赖,而这台机器下
GitHub release 会断连。
@@ -197,6 +202,12 @@ P2 的本地唤醒和 20 秒连续对话窗口。
完整的 `bot-transcription` / `bot-output` 事件,不能只发送 token 级
`bot-llm-text`。
**姓名记忆已修复并迁移。** 用户明确自报姓名时,同步更新 `users.display_name`
和 confirmed 姓名记忆;新连接从用户表恢复称呼,内部 `user_id` 不进入用户可见
称呼。姓名疑问句(例如“知道我是谁吗”)不会再被误写为姓名。需要注意:通用的
“记住/我喜欢/我习惯”目前仍全部写成 candidate,默认检索不会读取,而且还没有确认
和晋升流程;所以通用长期记忆尚未闭环。
**联网搜索已实现并部署。** DeepSeek 通过 Function Calling 请求 `web_search`,引擎
聚合完整的流式工具参数,调用回环地址上的 SearXNG,并把标题、摘要、URL 作为
`tool` 消息回灌后生成最终回答。最多连续搜索两轮,搜索失败不会拖垮对话;网页保留
@@ -207,6 +218,22 @@ P2 的本地唤醒和 20 秒连续对话窗口。
启用 Bing、360 搜索、搜狗;其中 360 与搜狗实测有结果,Bing 当前返回空结果。
容器只监听 `127.0.0.1:18080`,不向局域网或公网暴露。
## Agent 与打断的下一阶段
架构已选择 **Pipecat Flows 渐进改造**,不整体迁移 OpenClaw、Agno、LangGraph 或
LiveKit Agents。现有 `ConversationEngine` 已手写最多两轮的搜索工具循环,而
Pipecat Flows 依赖 Pipecat `LLMService`、context aggregator 和 PipelineTask;
两者不能直接替换。实施必须先完成无传输的兼容性 spike,验证 DeepSeek 流式内容、
工具参数聚合、取消传播和 RTVI 文本事件,再用 `AI_AGENT_RUNTIME=legacy|flows`
保留回退路径。详细任务与验收见笔记库
`P1-Pipecat Flows Agent化与记忆改造交接.md`。
当前打断不是完整闭环:`SimpleVAD` 在助手推送音频时检测到高能量只记录日志;
语音话段要等 700ms 尾静音后才会取消旧轮次。收到 `InterruptionFrame` 时任务确实
可以取消,但服务端尚未在用户开口时主动生成该信号,也没有同时清空浏览器已缓冲音频。
下一阶段先用合成帧验证“开始说话 → 取消生成 → 清空输出”的控制链;真实 AEC、回声
条件下的误触发率仍必须等 N5095 音频硬件到位后验收。
## 未做的事
- **没有常驻服务**:TTS 服务和助手都没有 systemd 开机自启(方案 §10 要求 P2 后做)
@@ -214,6 +241,10 @@ P2 的本地唤醒和 20 秒连续对话窗口。
需要 HTTPS;Tailscale Serve 仍需在 tailnet 管理页启用
- **唤醒尚未实现**:当前网页连接后会持续上传麦克风并响应每段有效语音;正式方案
是在 N5095 本地做关键词检测,待机时音频不出网
- **实时打断尚未闭环**:有能量 VAD、异步任务取消和客户端停播能力,但三者还没有
由可靠的 user-started-speaking 事件串起来
- **通用长期记忆尚未闭环**:除姓名外的自动抽取均为 candidate,缺少确认、晋升、
修改、忘记的用户可见流程
- **设备侧代码不存在**:N5095 的外设桥(USB、WebRTC 客户端、串口)尚未开始
- **STT 没换**:仍是 2023 年的 Zipformer int8,中文识别率从未测过
- **记忆库在服务器上**,而人脸特征按方案必须留在设备端——将来做