SmartMeeting/speakr/docs/rework-notes/前后端边界梳理修改.md

324 lines
9.1 KiB
Markdown
Raw 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.

# 前后端边界梳理修改记录
更新时间2026-06-23
## 当前目标
本轮重构的目标是把浏览器、Speakr 后端、算法服务之间的处理边界理顺:
- 浏览器只负责音频采集、状态展示、用户操作。
- Speakr 后端统一负责和 FunASR、ASR HTTP、LLM、投屏/外部服务通信。
- 算法服务地址不再通过 `getUserConfig` 暴露给前端。
- 草稿完成后由后端直接进入正式 Recording 处理流,不再由前端下载 blob 后二次上传。
## 已完成修改
### 后端实时 ASR WebSocket 代理
新增 `src/api/asr_ws.py`,提供后端 WebSocket 入口:
```text
/ws/asr/live
```
经 nginx 暴露为:
```text
/tool/speakr/ws/asr/live
```
当前流程:
- 前端只连接 Speakr 后端 WebSocket。
- 后端读取 `FUNASR_WEBSOCKET_IP`,连接 FunASR `/websocket_offline`
- 前端发送 PCM binary chunk 给后端,后端转发给 FunASR。
- FunASR 返回消息后,后端推回前端,尽量保持原 FunASR 返回格式。
- FunASR 初始化参数由后端生成,包括 `mode``chunk_size``hotwords` 等。
相关文件:
- `src/api/asr_ws.py`
- `src/flask_ext.py`
- `src/api/__init__.py`
- `requirements.txt`
新增依赖:
```text
flask-sock
websocket-client
```
### 前端实时 ASR 连接收口
修改 `static/js/wsconnecter.js``static/js/app.js`
旧模式:
```js
wss://${wssBaseUrl.FUNASR_WEBSOCKET_IP}/websocket_offline
```
新模式:
```text
/tool/speakr/ws/asr/live?mode=online
/tool/speakr/ws/asr/live?mode=offline
```
前端不再读取或使用 `FUNASR_WEBSOCKET_IP` 创建 WebSocket。
保留了现有录音采集、16k PCM 分片、实时展示、`segmentList` 渲染逻辑。
### 草稿 finalize 后端化
修改 `src/api/draft_api.py``src/api/recording.py``src/api/__init__.py``static/js/app.js`
已完成:
- 注册 `draft_bp``/api/draft`
- `POST /tool/speakr/api/draft/<draft_id>/finalize` 不再返回音频 blob。
- 后端合并草稿音频后直接创建正式 `Recording`
- 后端绑定 notes、tags、ASR 参数。
- 后端启动现有 `transcribe_audio_task`
- 前端拿到 `{ success: true, recording }` 后进入现有状态轮询。
为复用普通上传链路,`src/api/recording.py` 新增:
```python
create_recording_from_existing_file(...)
```
普通 `/upload` 和草稿 finalize 都走这个函数创建 Recording、绑定标签、启动转写线程。
### 外部算法/服务配置收口
修改 `src/api/main.py``static/js/app.js``templates/account.html`
已完成:
- `getUserConfig` 不再返回 `FUNASR_WEBSOCKET_IP`
- `getUserConfig` 不再返回 `SCREEN_PUSH_IP`
- 原本前端直连 `SCREEN_PUSH_IP` 的请求改为后端代理。
新增后端代理路径:
```text
/tool/speakr/api/external/summarize_text
/tool/speakr/api/external/submit
/tool/speakr/api/external/prompts/list
/tool/speakr/api/external/recommend_prompts
/tool/speakr/api/external/summarize_text_stream
```
新增依赖:
```text
requests
```
### 部署配置调整
修改:
- `Dockerfile`
- `deployment/setup.sh`
- `docs/getting-started/installation.md`
Gunicorn 增加:
```text
--threads 8
```
用于支持后端 WebSocket 长连接。
## 当前运行状态
### 主流程
实时转写主流程当前可用,后端无明显异常,前端 Network 中主要连接 `/tool/speakr/ws/asr/live`
### 已知遗留问题:停止阶段 Invalid frame header
停止录音时前端仍可能打印:
```text
WebSocket connection to 'wss://localhost:8083/tool/speakr/ws/asr/live?mode=online' failed: Invalid frame header
WebSocket connection to 'wss://localhost:8083/tool/speakr/ws/asr/live?mode=offline' failed: Invalid frame header
```
当前处理情况:
- 已移除前端消息回调里基于 `is_final.value == true` 的硬关闭。
- 已增加 `wsFinish()`,结束录音时优先发送 stop 并等待后端 closed 状态或短超时。
- 已调整后端 stop 后等待 FunASR final 消息再退出。
- 已撤掉后端显式 `client_ws.close()`,让 Flask-Sock handler return 后自然关闭。
目前判断:
- 问题更像 WebSocket 关闭阶段的代理/框架兼容性问题,而不是主流程架构问题。
- 现阶段不影响正常使用,先记录,后续结合 nginx access/error log、浏览器 Network close code、后端日志继续定位。
后续排查方向:
- 抓 nginx 对应时刻 `/tool/speakr/ws/asr/live` 的 access log 和 error log。
- 确认 `/tool/speakr/ws/` location 一定优先于普通 `/tool/speakr/` location。
- 确认 nginx 没有在 WebSocket 关闭阶段返回普通 HTTP 错误页。
- 检查当前 Flask-Sock/simple-websocket 与实际运行服务器的关闭帧兼容性。
- 如仍无法消除,可考虑把实时 ASR 代理独立为 ASGI/WebSocket 服务。
### 已知遗留问题:停止阶段 LLM 矫正请求
停止录音时可能伴随:
```text
LLM矫正出错: TypeError: Failed to fetch
```
来源:
```text
/tool/speakr/api/asr/correct
```
可能原因:
- offline final 到达后触发 LLM 矫正,但录音已经进入停止/收尾阶段。
- 请求被页面状态、CSRF refresh、网络或后端瞬时状态影响。
后续建议:
- 结束录音后禁止新发起 LLM 矫正,只等待已发出的请求。
-`getJsonMessage2` 增加停止态判断。
- 后端为 `/api/asr/correct` 增加更明确日志。
## 需要手动同步的非 git 配置
下面两类改动非常重要,但通常不会随 git diff 自动同步给其他成员。
### 1. `.env` 必须同步
后端代理模式下,`FUNASR_WEBSOCKET_IP` 不能再指向 nginx 外部入口,例如:
```env
FUNASR_WEBSOCKET_IP=localhost:8083
```
这个旧值在新架构下会让 Speakr 后端连回 nginx而不是直接连 FunASR。
应改为真实 FunASR WebSocket 服务地址。按当前环境,建议:
```env
FUNASR_WEBSOCKET_IP=ws://10.100.3.22:10095/websocket_offline
```
或:
```env
FUNASR_WEBSOCKET_IP=ws://10.100.3.22:10095
```
代码会在没有 path 时自动补 `/websocket_offline`,但推荐写完整路径。
`SCREEN_PUSH_IP` 仍由后端读取,用于 `/api/external/...` 代理。其他成员需要确认自己的 `.env` 中该值指向对应环境的真实服务地址。
注意:修改 `.env` 后必须重启 Speakr 后端。
### 2. nginx 配置必须同步
需要新增 WebSocket 专用 location并放在普通 `/tool/speakr/` location 前面:
```nginx
location /tool/speakr/ws/ {
proxy_pass http://localhost:5000/ws/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Script-Name /tool/speakr;
proxy_redirect off;
proxy_connect_timeout 60s;
proxy_send_timeout 3600s;
proxy_read_timeout 3600s;
proxy_buffering off;
}
```
如果后端不是 `localhost:5000`,需要替换为实际 Speakr 后端地址。
当前旧 FunASR 直连代理:
```nginx
location /websocket { ... }
location /websocket_offline { ... }
```
新前端理论上不再需要。可以先保留,待新链路稳定后清理,避免其他页面或缓存仍依赖旧路径。
修改 nginx 后需要:
```bash
nginx -t
nginx -s reload
```
## 后续收尾清单
### 清理旧 FunASR 前端遗留
建议后续删除或整理:
- `static/js/wsconnecter.js` 中的 `wssBaseUrl``getBaseUrlFromApi()``configwords`
- `static/js/wsconnecter.js` 中的 `getBaseHot()`
- FunASR demo 遗留变量:`isfilemode``file_ext``file_sample_rate` 等。
- `getJsonMessageLegacy()`
### 整理实时 ASR 生命周期
建议抽成明确 API
```text
startRealtimeAsr()
pauseRealtimeAsr()
finishRealtimeAsr()
disposeRealtimeAsr()
```
减少 `app.js` 中多处直接调用 `wsStop()``wsFinish()` 的分散逻辑。
### 草稿链路收尾
- 确认 `/api/draft/<id>/download` 是否仍需要保留用于预览。
- 如果后续不需要草稿预览 blob可删除该接口。
- 为草稿 finalize 增加自动化测试,覆盖 segment 文件清理和 Recording 创建。
### 测试补充
建议补:
- draft create -> segment -> finalize 返回 `202 + recording`
- finalize 后数据库生成正式 Recording。
- finalize 后草稿记录和 segment 文件被清理。
- `/upload` 原上传链路不回归。
- `/ws/asr/live` 未登录、FunASR 不可达、FunASR 中途断开场景。
- 前端停止/暂停/恢复不会向旧 WebSocket 继续发 chunk。
### 配置拆分
`src/config.py``as_dict()` 仍包含内部地址。只要该 dict 不返回前端,目前可接受;后续建议拆成:
- 后端内部配置。
- 前端可见 UI 配置。
避免以后误把内部服务地址再次返回到页面。
## 当前结论
主要边界调整已经完成:前端不再直连 FunASR不再读取算法服务地址草稿 finalize 已后端化;外部服务地址也改为后端代理。
当前剩余工作主要是运行收尾和清理WebSocket 关闭阶段 `Invalid frame header`、停止阶段 LLM 矫正请求时机、旧 demo/旧直连逻辑删除、自动化测试、`.env` 和 nginx 配置同步。