Pipecat 传输层实验:传输层通,帧路由未通

新增实验代码
- services/bot_server.py:Pipecat SmallWebRTC 服务端,
  把 ConversationEngine 包成一个 FrameProcessor(检验 A 方案的核心假设)
- client/test_client.py:自写 aiortc 客户端——Pipecat 的客户端 SDK
  覆盖浏览器/移动端/C++/ESP32,没有适配"Linux x86 盒子接 USB 阵列"这种形态

实验结果
- ✅ 传输层跑通:信令(47–280ms 往返)、ICE 连通、data channel、
  双向媒体轨(客户端音轨送达服务端,服务端音轨客户端收到)
- ❌ 帧路由未通:自定义处理器的 process_frame 调用次数为 0,
  连自己注入的 StartFrame 都没进去,引擎从未运行
- 现象:连接全对、音轨协商成功、助手永远不说话,日志里一条错误都没有

对架构决策的影响
A 方案(Pipecat 只做传输、逻辑归自有引擎)的核心假设不是开箱即用的。
B 方案按框架原生 service 重构,绕开了这个接缝。
根因未定位:接线错误(接缝浅,A 可行)vs 模型不兼容(接缝深,应转 B)。

顺带挖出 8 个 Pipecat 集成坑,每个都表现为"什么都没发生",已记入 README:
客户端需发 data channel ping、offer 无媒体则不收集 ICE 候选、
run() 要 WorkerParams、TaskManager 需在事件循环内建、
后台任务异常被静默吞掉等。

其他
- bot_server.py 保留 [诊断] 前缀日志,供下一轮排查,定位后应清理
- 客户端默认地址改用 tailscale 域名(局域网已于当日重新编址,
  从 192.168.0.x 变为 172.16.1.x)
- 新增依赖 aiortc(含 av 17.x,注意 18.x 与 aiortc 1.15 不兼容)
This commit is contained in:
ylhan
2026-09-15 17:54:34 +08:00
parent 35e107c290
commit 74a30b11d7
4 changed files with 819 additions and 2 deletions
+39 -1
View File
@@ -31,7 +31,11 @@ app/ 助手本体(部署到服务器)
└── tests/ 4 个测试,共 88 项断言,全部无需硬件
services/
└── tts_server.py Qwen3-TTS 服务(跑在 ~/tts-qwen,独立 venv)
├── tts_server.py Qwen3-TTS 服务(跑在 ~/tts-qwen,独立 venv)
└── bot_server.py Pipecat SmallWebRTC 服务端(实验,见下)
client/
└── test_client.py 自写 aiortc 客户端(实验用,也是 N5095 客户端的雏形)
p0/ P0 阶段的验证与基准脚本(保留用于复现)
├── verify_deepseek.py DeepSeek 四条路径
@@ -76,6 +80,40 @@ cd ~/ai-companion
**引擎与传输解耦。** 不依赖 Pipecat、不依赖任何传输方式,只依赖四个接口。
好处:可以用文件音频端到端测试;接 WebRTC 时只需写薄适配层。
## 当前卡点:Pipecat 传了但没接上
方案文档写"Pipecat 作为唯一会话管线框架",但**代码里它只在 requirements.txt 里**。
2026-09-15 做了传输层实验(`services/bot_server.py` + `client/test_client.py`):
- ✅ **传输层跑通**:信令、ICE、data channel、双向媒体轨
- ❌ **帧路由未通**:自定义处理器的 `process_frame` 调用次数为 0,
连自己注入的 StartFrame 都没进去;引擎从未运行
现象是"连接全对、音轨协商成功、但助手永远不说话,且日志里一条错误都没有"。
**这直接影响 A/B 架构方案**:A 是"Pipecat 只做传输、逻辑归自有引擎",
B 是"按 Pipecat 的 service 模型重构"。帧送不进自定义处理器,
说明 A 的核心假设不是开箱即用的。
根因未定位,两种可能必须区分:接线错误(接缝浅,A 可行)
vs 模型不兼容(接缝深,应转 B)。
完整记录见代码仓库外的 `P0-Pipecat传输层实验报告.md`(在 Obsidian 笔记库里)。
### 实验挖出的 8 个坑
每个都表现为"什么都没发生",极难排查:
1. Pipecat 客户端 SDK 不覆盖"Linux x86 盒子接 USB 阵列"这种形态 → 客户端得自己写
2. 客户端必须建 data channel 并定期发 `"ping"`,否则服务端一帧音频都不推
3. offer 里没有媒体时 aiortc **完全不收集 ICE 候选**(连连通性测试都要加静音轨)
4. ICE 收集完成前发 offer → SDP 里 0 个候选地址
5. `PipelineTask.run()` 要 `WorkerParams(task_manager=...)`,不是 `PipelineParams`
6. `TaskManager()` 必须在事件循环内创建
7. **后台任务里的异常被静默吞掉**,日志里查不到
8. 信令回调里必须 `create_task`,不能 `await task.run()`
## 踩过的坑
全部写在对应文件的注释里,这里只列索引,详见 `部署说明.md` §已知的坑: