1461 lines
58 KiB
Markdown
1461 lines
58 KiB
Markdown
# 录音资料库、说话人分离与反馈素材编排开发计划
|
||
|
||
- 状态:待评审
|
||
- 编制日期:2026-07-23
|
||
- 适用仓库:`teaching-feedback-assistant`
|
||
- 目标部署:Ryzen 7 5700U、CPU FunASR、远程 PostgreSQL、Docker Compose
|
||
- 计划性质:产品、交互、数据、API、转录、迁移、测试和发布的统一实施基线
|
||
|
||
## 1. 执行摘要
|
||
|
||
本次改造不应继续把录音能力作为“反馈生成”页面中的一个折叠区域扩展。目标能力已经包含录音和文件上传、长期管理、音频播放、异步转录、说话人分离、角色映射、逐段选择、跨录音选择、AI 总结、总结预览和写入反馈输入框,实际边界是一个独立的“录音资料库”子系统。
|
||
|
||
最终产品职责划分如下:
|
||
|
||
```text
|
||
现场录音 / 上传音频
|
||
|
|
||
v
|
||
录音资料库
|
||
|
|
||
v
|
||
转录 + 说话人分离
|
||
|
|
||
v
|
||
对话校对、角色映射、片段选择
|
||
/ \
|
||
v v
|
||
直接加入反馈输入框 多录音或片段交给 AI
|
||
|
|
||
v
|
||
总结预览
|
||
|
|
||
v
|
||
写入反馈输入框
|
||
```
|
||
|
||
本计划采用以下总体策略:
|
||
|
||
1. 录音从 `feedback_session` 的附属数据升级为用户拥有的独立资产。
|
||
2. 反馈页面只负责编辑反馈、选择素材、预览总结和保存结果。
|
||
3. 录音列表、录音详情和反馈素材选择使用独立页面,不以大弹窗承载主流程。
|
||
4. 说话人分离只产生匿名说话人簇,教师、学生、家长等业务角色由用户确认。
|
||
5. AI 总结使用选中对话的不可变快照,避免转录重跑或角色修改改变历史结果。
|
||
6. 数据库按 expand、backfill、switch、contract 渐进迁移,旧链路在切换完成前始终可回退。
|
||
7. FunASR 说话人能力先通过 5700U CPU 技术预研门槛,再决定 CAM++ 或 ERes2NetV2,不直接在生产中试错。
|
||
8. 音频默认保留到用户主动删除;自动保留期限作为后续可配置能力,不在首版擅自清理历史录音。
|
||
|
||
## 2. 当前状态与约束
|
||
|
||
### 2.1 当前业务链路
|
||
|
||
当前链路为:
|
||
|
||
```text
|
||
反馈草稿
|
||
-> 创建 feedback_session
|
||
-> 创建 lesson_session
|
||
-> 小程序每 8 分钟录制一个音频片段
|
||
-> 上传 audio_segment
|
||
-> Rust worker 调用 FunASR
|
||
-> audio_segment 写入纯文本 transcript
|
||
-> 用户按 lesson_session 整节选择
|
||
-> summary worker 生成总结
|
||
-> 用户确认后替换反馈输入框
|
||
-> 保存反馈并将 feedback_session 标记 finalized
|
||
```
|
||
|
||
现有能力中可以复用的部分:
|
||
|
||
- 微信小程序录音权限、8 分钟自动切片和失败上传本地保留。
|
||
- Rust API 的用户认证、owner 隔离和请求 ID。
|
||
- 音频持久化目录和 Docker 命名卷。
|
||
- PostgreSQL 异步转录队列、租约、重试和失败状态。
|
||
- FunASR Paraformer、FSMN-VAD 和标点模型服务。
|
||
- 总结任务的幂等键、后台 worker、轮询、预览和应用流程。
|
||
- Grafana、Loki 和结构化日志规范。
|
||
|
||
### 2.2 当前结构性限制
|
||
|
||
1. `lesson_sessions.feedback_session_id` 非空,录音不能脱离反馈草稿独立存在。
|
||
2. 录音查询、重试和删除接口只允许访问 `active` 反馈批次。
|
||
3. 保存反馈后批次变为 `finalized`,当前小程序没有独立入口重新管理其录音。
|
||
4. `audio_segments.transcript` 只有纯文本,没有开始时间、结束时间、说话人或置信信息。
|
||
5. 当前总结来源表只关联整节 `lesson_session`,不支持逐段选择。
|
||
6. Rust API 成功转录后保留原始音频,但没有认证播放接口和统一保留策略。
|
||
7. 数据库级联删除和重复序号上传可能留下磁盘孤儿文件。
|
||
8. 当前本地 ASR 响应只有 `text`、`duration_ms` 和 `request_id`。
|
||
9. 腾讯备用实现明确关闭 `speaker_diarization`,不能承担当前目标。
|
||
10. `recording_routes.rs` 已承载过多职责,继续叠加录音库接口会提高回归风险。
|
||
|
||
### 2.3 固定部署约束
|
||
|
||
- API 和 FunASR 使用 `compose.deploy.yml` 部署。
|
||
- FunASR 不暴露宿主机端口,只允许 API 通过 Compose 网络访问。
|
||
- PostgreSQL 使用现有远程数据库,不新增数据库容器。
|
||
- 首发继续使用 CPU 推理,不假设 CUDA、ROCm 或独立 GPU。
|
||
- 默认单 FunASR 进程和单转录 worker,模型对象不在多线程中并发调用。
|
||
- 模型缓存和音频必须继续使用持久化卷。
|
||
- 不得把音频、模型、数据库凭据、微信密钥或 `.env` 提交到 Git。
|
||
- 日志不得包含转录正文、总结正文、学生姓名、原始文件名或真实磁盘路径。
|
||
|
||
### 2.4 已知但尚未获得的生产基线
|
||
|
||
编码前应从部署服务器补齐以下数据;缺少数据不阻止建表和接口开发,但阻止容量结论和自动清理上线:
|
||
|
||
- PostgreSQL 版本、现有表行数、数据库总大小和每日增长量。
|
||
- `teaching-feedback-audio` 卷当前文件数、容量和每日增长量。
|
||
- 录音平均时长、P95 时长、平均文件大小和最大文件大小。
|
||
- 单日录音数量、并发教师数量和转录队列 P95 等待时间。
|
||
- 5700U 上当前模型的 P50/P95 RTF、峰值内存和失败率。
|
||
- 用户期望的录音保留期限和可接受的删除恢复窗口。
|
||
|
||
## 3. 产品目标与非目标
|
||
|
||
### 3.1 首个完整版本目标
|
||
|
||
用户能够:
|
||
|
||
1. 在录音资料库中查看现场录音和上传录音。
|
||
2. 按学生、日期、来源和处理状态筛选录音。
|
||
3. 查看录音时长、文件状态、转录状态、说话人数和失败原因。
|
||
4. 播放本人录音,并从对话时间点跳转播放。
|
||
5. 上传支持格式的音频文件并在后台完成转录。
|
||
6. 查看按时间排序、按说话人分组标识的对话文本。
|
||
7. 将匿名说话人映射为教师、学生、家长或自定义显示名。
|
||
8. 选择整份录音、某位说话人的全部对话或若干对话片段。
|
||
9. 将选择内容直接追加到反馈输入框,受 2000 字限制保护。
|
||
10. 选择一份或多份录音的部分或全部对话生成 AI 总结。
|
||
11. 预览总结,确认后替换反馈输入框。
|
||
12. 重试失败转录、重新转录、修改录音标题和删除录音。
|
||
13. 在退出页面、应用切后台或请求重试后恢复可恢复的任务状态。
|
||
|
||
### 3.2 首版明确不做
|
||
|
||
- 不做实时字幕和实时说话人分离。
|
||
- 不自动断言“Speaker 0 就是教师”。
|
||
- 不做人脸、声纹实名识别或跨用户声纹库。
|
||
- 不在不同录音之间自动确认同一个自然人。
|
||
- 不做多人协作编辑、公开分享或外链下载。
|
||
- 不做音频裁剪、拼接、降噪参数编辑等专业音频编辑器。
|
||
- 不做波形级标注工作台;首版使用时间轴和播放进度定位。
|
||
- 不删除现有 `lesson_sessions`、`audio_segments` 或旧总结来源表。
|
||
- 不在没有代表性样本验证的情况下升级 FunASR 或切换识别模型。
|
||
- 不在首版启用自动到期删除,除非用户另行确认保留规则。
|
||
|
||
### 3.3 后续可选能力
|
||
|
||
- 用户手工修订转录文本并保留修订历史。
|
||
- 针对固定教师的声纹登记和跨录音角色建议。
|
||
- 自动提取课堂主题、作业、错误点和后续建议。
|
||
- 录音标签、收藏、批量归档和导出。
|
||
- 对象存储替代本地卷,以及分层存储和归档。
|
||
- Web 管理端或桌面端长文本校对工作台。
|
||
|
||
## 4. 信息架构与页面计划
|
||
|
||
### 4.1 页面边界
|
||
|
||
计划新增以下页面:
|
||
|
||
| 页面 | 路径建议 | 职责 |
|
||
|---|---|---|
|
||
| 录音资料列表 | `pages/recordings/index` | 管理、筛选、批量选择、录音和上传入口 |
|
||
| 录音详情 | `pages/recordings/detail` | 播放、说话人映射、对话浏览、选择、重试和删除 |
|
||
| 反馈素材选择 | `pages/recordings/select` | 从反馈页进入,选择录音或对话片段并返回反馈草稿 |
|
||
|
||
弹窗只用于:
|
||
|
||
- 删除录音确认。
|
||
- 重新转录确认。
|
||
- 总结覆盖输入框确认。
|
||
- 简短标题修改。
|
||
- 说话人显示名的单字段输入。
|
||
|
||
不得使用弹窗承载完整录音列表、长对话浏览或批量选择。
|
||
|
||
### 4.2 底部导航决策
|
||
|
||
产品目标上,“录音资料”是一级能力。实现前进行一次 5 项底部导航真机评审:
|
||
|
||
```text
|
||
反馈生成 | 录音资料 | 学生档案 | 反馈记录 | 我的
|
||
```
|
||
|
||
通过条件:
|
||
|
||
- 320px 宽度真机上文字和图标不截断。
|
||
- 各入口点击区域满足现有小程序交互尺寸。
|
||
- 当前 tab 状态不会因从选择页返回而错乱。
|
||
|
||
若 5 项导航不通过,首版不强行增加 tab,改为在反馈页和“我的”页面提供录音资料入口;页面和后端边界保持不变,后续再调整导航。
|
||
|
||
### 4.3 录音资料列表页
|
||
|
||
页面结构:
|
||
|
||
```text
|
||
标题与状态统计
|
||
筛选栏:学生 / 日期 / 来源 / 状态
|
||
录音列表
|
||
- 标题和日期
|
||
- 学生或未关联
|
||
- 现场录音或上传文件
|
||
- 时长、说话人数、字数
|
||
- 转录状态和失败操作
|
||
底部主操作:录音或上传
|
||
```
|
||
|
||
交互要求:
|
||
|
||
- 内容列表是主区域,工具栏保持紧凑,不使用大面积装饰卡片。
|
||
- 使用稳定高度的列表行,状态文字变化不能引起明显布局跳动。
|
||
- 支持下拉刷新和游标分页,不一次加载全部历史录音。
|
||
- 处理中项目按更新时间刷新;就绪项目不进入高频轮询。
|
||
- 批量模式下显示已选数量和一个明确的主操作。
|
||
- 失败项目提供“重试”和“删除”,不使用含糊的通用“处理”按钮。
|
||
- 删除入口必须显示是否会影响已生成反馈;历史总结只保留快照,不依赖原录音继续存在。
|
||
|
||
### 4.4 录音详情工作页
|
||
|
||
页面结构:
|
||
|
||
```text
|
||
顶部导航:返回 / 标题 / 更多
|
||
紧凑播放器:播放、暂停、进度、当前时间、总时长
|
||
说话人映射栏:Speaker 0 -> 教师,Speaker 1 -> 学生
|
||
对话工具栏:全选、按说话人筛选、仅看已选
|
||
对话主区域:
|
||
[复选框] [00:12-00:18] [教师] 对话文本
|
||
[复选框] [00:19-00:27] [学生] 对话文本
|
||
固定底栏:已选 N 段 / 加入反馈或生成总结
|
||
```
|
||
|
||
交互要求:
|
||
|
||
- 对话内容占据主要视觉面积,播放器和筛选工具保持安静、紧凑。
|
||
- 点击时间范围后从对应位置播放。
|
||
- 播放进度变化只更新当前行状态,不重新渲染完整对话列表。
|
||
- 选择状态由 `turn_id` 管理,不能依赖数组位置。
|
||
- 支持按说话人全选和取消,结果仍展开为具体 `turn_id` 提交。
|
||
- 角色映射修改不改写历史总结快照。
|
||
- 转录失败时不渲染空工作台,直接显示失败原因、重试和删除。
|
||
- 说话人数量异常时允许保留“未知”角色,不强制映射为教师或学生。
|
||
|
||
### 4.5 反馈素材选择页
|
||
|
||
从反馈页打开,必须携带目标 `feedback_session_id`。该页复用录音列表和对话选择组件,但使用独立路由,以避免 tab 页面承担临时返回状态。
|
||
|
||
支持两种选择方式:
|
||
|
||
1. 整份录音:服务端解析为该录音当前就绪转录版本的全部对话。
|
||
2. 部分对话:提交明确的 `turn_id` 集合。
|
||
|
||
返回反馈页前显示:
|
||
|
||
- 录音数量。
|
||
- 对话片段数量。
|
||
- 字符数估算。
|
||
- 选择范围中是否包含“未知说话人”。
|
||
|
||
选择草稿以 `feedback_session_id` 为键暂存在小程序本地存储,页面返回或小程序短时退出后可恢复;提交总结时服务端重新校验所有权、状态和转录版本。
|
||
|
||
### 4.6 反馈生成页调整
|
||
|
||
现有展开式“语音记录”区域改为紧凑的素材区:
|
||
|
||
- `开始录音`。
|
||
- `从录音资料选择`。
|
||
- `已选 2 个录音 / 18 段 / 1260 字`。
|
||
- `查看选择`。
|
||
- `生成总结`。
|
||
- 总结状态和预览。
|
||
|
||
现有按整节加入和总结能力在兼容期保留;新能力开关启用后,页面优先走录音资料选择链路。确认稳定后再删除旧前端入口,后端旧接口继续保留一个发布周期。
|
||
|
||
### 4.7 页面状态矩阵
|
||
|
||
| 状态 | 列表页 | 详情页 | 可用操作 |
|
||
|---|---|---|---|
|
||
| 上传中 | 进度或等待 | 不进入对话区 | 取消 |
|
||
| 排队中 | 排队状态 | 音频可播放 | 删除 |
|
||
| 转录中 | 处理中 | 音频可播放 | 删除 |
|
||
| 待确认 | 显示说话人数 | 对话可查看和映射 | 映射、选择、完成确认 |
|
||
| 已就绪 | 正常状态 | 全部能力 | 选择、总结、重转录、删除 |
|
||
| 失败 | 错误分类 | 错误详情 | 重试、删除 |
|
||
| 删除中 | 从正常列表隐藏 | 禁止进入 | 后台重试清理 |
|
||
|
||
## 5. 目标领域模型
|
||
|
||
### 5.1 设计原则
|
||
|
||
- 录音资产由用户拥有,反馈会话只引用素材,不拥有素材。
|
||
- 原始音频、转录运行、说话人和对话片段职责分离。
|
||
- 每次重新转录创建新版本,不原地覆盖已用于总结的版本。
|
||
- 总结来源在任务创建时展开并快照,worker 不读取会变化的最新文本。
|
||
- 用户删除录音后,历史总结仍可解释其来源数量和快照,但不能再播放音频。
|
||
- 所有查询都显式包含 `owner_id` 边界,不依赖前端传入的用户标识。
|
||
- 删除必须同时覆盖数据库引用和物理文件,不再依赖数据库级联完成文件清理。
|
||
|
||
### 5.2 关系图
|
||
|
||
```text
|
||
users
|
||
|
|
||
+-- recordings
|
||
|
|
||
+-- recording_audio_parts
|
||
|
|
||
+-- transcription_runs
|
||
|
|
||
+-- recording_speakers
|
||
|
|
||
+-- transcript_turns
|
||
|
||
feedback_sessions
|
||
|
|
||
+-- feedback_summary_runs
|
||
|
|
||
+-- feedback_summary_run_sources
|
||
-> nullable transcript_turn reference
|
||
-> immutable text/speaker/time snapshot
|
||
```
|
||
|
||
### 5.3 `recordings`
|
||
|
||
职责:一份逻辑录音资产,不等同于某个 8 分钟物理片段。
|
||
|
||
建议字段:
|
||
|
||
| 字段 | 约束与用途 |
|
||
|---|---|
|
||
| `id UUID` | 主键 |
|
||
| `owner_id UUID` | 非空,引用 `users ON DELETE RESTRICT`;账号删除前必须完成受控录音清理 |
|
||
| `profile_id UUID` | 可空,引用学生档案;学生删除时建议 `SET NULL`,不连带删除录音 |
|
||
| `title VARCHAR(120)` | 非空,服务端生成默认标题,允许用户修改 |
|
||
| `source VARCHAR(16)` | `live`、`upload`、`legacy` |
|
||
| `recorded_at TIMESTAMPTZ` | 课堂或文件代表的录制时间 |
|
||
| `ingest_status VARCHAR(16)` | `uploading`、`complete`、`failed` |
|
||
| `duration_ms BIGINT` | 非负,所有 part 汇总 |
|
||
| `byte_size BIGINT` | 非负,所有 part 汇总 |
|
||
| `audio_format VARCHAR(8)` | 逻辑主格式,混合 part 时使用规范化输出格式 |
|
||
| `current_transcription_run_id UUID` | 可空,指向当前展示版本,扩展迁移后补 FK |
|
||
| `legacy_lesson_id UUID` | 可空且唯一,仅用于迁移追踪,后续可保留 |
|
||
| `delete_requested_at TIMESTAMPTZ` | 可空,存在时从正常列表隐藏 |
|
||
| `created_at/updated_at` | 非空 |
|
||
|
||
关键约束与索引:
|
||
|
||
- `CHECK (char_length(title) BETWEEN 1 AND 120)`。
|
||
- `CHECK (source IN ('live', 'upload', 'legacy'))`。
|
||
- `CHECK (ingest_status IN ('uploading', 'complete', 'failed'))`。
|
||
- `CHECK (duration_ms >= 0 AND byte_size >= 0)`。
|
||
- 列表索引:`(owner_id, created_at DESC, id DESC) WHERE delete_requested_at IS NULL`。
|
||
- 学生筛选索引:`(owner_id, profile_id, created_at DESC, id DESC) WHERE delete_requested_at IS NULL`。
|
||
- `legacy_lesson_id` 唯一索引仅覆盖非空值。
|
||
- `current_transcription_run_id` 不能只使用单列 FK;应以
|
||
`(id, current_transcription_run_id)` 复合引用
|
||
`transcription_runs(recording_id, id)`,保证当前 run 一定属于当前 recording。
|
||
|
||
### 5.4 `recording_audio_parts`
|
||
|
||
职责:保存现场录音切片或上传文件经过规范化后的物理音频单元。
|
||
|
||
建议字段:
|
||
|
||
| 字段 | 约束与用途 |
|
||
|---|---|
|
||
| `id UUID` | 主键 |
|
||
| `recording_id UUID` | 非空,引用 `recordings ON DELETE RESTRICT` |
|
||
| `sequence INTEGER` | 从 1 开始,录音内唯一 |
|
||
| `duration_ms INTEGER` | 大于 0 |
|
||
| `byte_size BIGINT` | 大于 0 |
|
||
| `audio_format VARCHAR(8)` | `mp3`、`aac`、`wav`、`m4a`、`amr` |
|
||
| `storage_key TEXT` | 非空,只存内部相对键,不向客户端返回 |
|
||
| `sha256 CHAR(64)` | 非空,用于上传幂等和文件核验 |
|
||
| `created_at` | 非空 |
|
||
|
||
关键约束与索引:
|
||
|
||
- `UNIQUE (recording_id, sequence)`。
|
||
- `UNIQUE (storage_key)`。
|
||
- `CHECK (sequence > 0 AND duration_ms > 0 AND byte_size > 0)`。
|
||
- 为 `recording_id` 建普通索引,支持删除和顺序读取。
|
||
|
||
### 5.5 `transcription_runs`
|
||
|
||
职责:一次可审计、可重试、不可原地覆盖的转录版本。
|
||
|
||
建议字段:
|
||
|
||
| 字段 | 约束与用途 |
|
||
|---|---|
|
||
| `id UUID` | 主键 |
|
||
| `recording_id UUID` | 非空,引用 `recordings` |
|
||
| `revision INTEGER` | 从 1 开始,录音内唯一 |
|
||
| `status VARCHAR(16)` | `queued`、`processing`、`ready`、`failed`、`superseded` |
|
||
| `provider VARCHAR(32)` | 例如 `local-funasr` |
|
||
| `asr_model TEXT` | 实际模型标识 |
|
||
| `speaker_model TEXT` | 可空,实际说话人模型标识 |
|
||
| `config_version VARCHAR(32)` | 转录配置版本 |
|
||
| `source_hash CHAR(64)` | 输入 part 顺序、hash 和配置的摘要 |
|
||
| `attempts INTEGER` | 非负 |
|
||
| `processing_started_at` | 租约和恢复 |
|
||
| `completed_at` | 就绪或失败完成时间 |
|
||
| `error_class VARCHAR(64)` | 稳定错误分类 |
|
||
| `error_message TEXT` | 面向用户的脱敏错误信息 |
|
||
| `created_at/updated_at` | 非空 |
|
||
|
||
关键约束与索引:
|
||
|
||
- `UNIQUE (recording_id, revision)`。
|
||
- `UNIQUE (recording_id, id)`,供 recording 当前 run 的复合 FK 使用。
|
||
- `UNIQUE (recording_id, source_hash)`,相同输入和配置重复请求返回既有 run。
|
||
- 队列部分索引:`(created_at, id) WHERE status IN ('queued', 'processing')`。
|
||
- 状态结果约束保证 `ready` 时 `completed_at` 非空,`failed` 时 `error_class` 非空。
|
||
|
||
### 5.6 `recording_speakers`
|
||
|
||
职责:保存某次转录运行产生的匿名说话人簇及用户业务映射。
|
||
|
||
建议字段:
|
||
|
||
| 字段 | 约束与用途 |
|
||
|---|---|
|
||
| `id UUID` | 主键 |
|
||
| `transcription_run_id UUID` | 非空,引用 `transcription_runs` |
|
||
| `provider_label VARCHAR(32)` | 例如 `SPK0`,run 内唯一 |
|
||
| `role VARCHAR(16)` | `teacher`、`student`、`guardian`、`unknown`、`other` |
|
||
| `display_name VARCHAR(60)` | 可空,例如“学生甲” |
|
||
| `created_at/updated_at` | 非空 |
|
||
|
||
约束:
|
||
|
||
- `UNIQUE (transcription_run_id, provider_label)`。
|
||
- `UNIQUE (transcription_run_id, id)`,供 turn 的复合 FK 使用。
|
||
- 映射只改变业务显示,不修改模型原始 `provider_label`。
|
||
- 首次生成时全部为 `unknown`,不得自动写入 `teacher`。
|
||
|
||
### 5.7 `transcript_turns`
|
||
|
||
职责:一条连续说话人对话,是播放定位和选择的最小单位。
|
||
|
||
建议字段:
|
||
|
||
| 字段 | 约束与用途 |
|
||
|---|---|
|
||
| `id UUID` | 主键 |
|
||
| `transcription_run_id UUID` | 非空,引用 `transcription_runs` |
|
||
| `speaker_id UUID` | 非空,引用 `recording_speakers` |
|
||
| `sequence INTEGER` | 从 1 开始,run 内唯一 |
|
||
| `start_ms BIGINT` | 非负 |
|
||
| `end_ms BIGINT` | 大于 `start_ms` |
|
||
| `text TEXT` | 非空且去除首尾空白后有内容 |
|
||
| `created_at` | 非空 |
|
||
|
||
约束与索引:
|
||
|
||
- `UNIQUE (transcription_run_id, sequence)`。
|
||
- `(transcription_run_id, speaker_id)` 复合引用
|
||
`recording_speakers(transcription_run_id, id)`,禁止 turn 引用其他 run 的 speaker。
|
||
- `CHECK (start_ms >= 0 AND end_ms > start_ms)`。
|
||
- `CHECK (char_length(btrim(text)) > 0)`。
|
||
- `(transcription_run_id, start_ms, id)` 支持时间排序。
|
||
- `(speaker_id, start_ms, id)` 支持按说话人筛选。
|
||
|
||
首版不允许直接编辑 `text`。若后续增加校对,新增修订表或明确的 `edited_text` 和审计字段,不在本次建表中预留含糊字段。
|
||
|
||
### 5.8 `feedback_summary_run_sources`
|
||
|
||
职责:冻结 AI 实际接收的来源,保证重跑、删除和转录升级后仍可审计。
|
||
|
||
建议字段:
|
||
|
||
| 字段 | 约束与用途 |
|
||
|---|---|
|
||
| `summary_run_id UUID` | 引用 `feedback_summary_runs` |
|
||
| `source_order INTEGER` | 从 1 开始,run 内唯一 |
|
||
| `recording_id UUID` | 可空,删除录音后 `SET NULL` |
|
||
| `transcript_turn_id UUID` | 可空,删除转录后 `SET NULL` |
|
||
| `recording_title_snapshot TEXT` | 非空 |
|
||
| `speaker_label_snapshot TEXT` | 非空 |
|
||
| `start_ms_snapshot BIGINT` | 非负 |
|
||
| `end_ms_snapshot BIGINT` | 大于开始时间 |
|
||
| `text_snapshot TEXT` | 非空,是 worker 唯一读取的正文 |
|
||
| `created_at` | 非空 |
|
||
|
||
主键建议为 `(summary_run_id, source_order)`。创建总结任务时在同一事务中:
|
||
|
||
1. 校验所有录音属于当前 owner。
|
||
2. 校验所有 turn 属于对应录音的当前就绪 run。
|
||
3. 将整份录音选择展开为 turn。
|
||
4. 按录音时间和 turn 时间排序。
|
||
5. 写入快照。
|
||
6. 计算 source hash。
|
||
7. 创建或复用幂等总结任务。
|
||
|
||
worker 只读取 `text_snapshot`,不得再次 join 最新 `transcript_turns.text`。
|
||
|
||
## 6. 录音和转录状态机
|
||
|
||
### 6.1 录音采集状态
|
||
|
||
```text
|
||
uploading
|
||
| complete upload/finalize
|
||
v
|
||
complete
|
||
|
||
uploading --validation/storage error--> failed
|
||
failed --retry upload--> uploading
|
||
```
|
||
|
||
规则:
|
||
|
||
- `complete` 后 part 集合默认不可变;补传必须在 finalize 前完成。
|
||
- finalize 是幂等操作,重复调用返回同一结果。
|
||
- live 录音停止后只有全部 part 成功上传才允许 finalize。
|
||
- 上传文件校验失败不得创建可见的半成品录音;失败记录只用于恢复时才保留。
|
||
|
||
### 6.2 转录运行状态
|
||
|
||
```text
|
||
queued -> processing -> ready
|
||
| |
|
||
| +-> failed
|
||
+------------> failed
|
||
|
||
ready --create new revision--> new queued run
|
||
old ready --new run promoted--> superseded
|
||
```
|
||
|
||
规则:
|
||
|
||
- worker 使用 `FOR UPDATE SKIP LOCKED` 和租约领取任务。
|
||
- 进程异常后超过租约的 `processing` 可重新领取。
|
||
- 只有完整写入 speakers 和 turns 后才能把 run 标为 `ready` 并切换 `current_transcription_run_id`。
|
||
- speakers、turns、run ready 和 current pointer 在一个数据库事务中提交。
|
||
- 新 run 失败时旧 ready run 仍保持当前展示版本。
|
||
- 重试同一个 failed run 可增加 attempts;用户主动“重新转录”创建新 revision。
|
||
|
||
### 6.3 删除状态
|
||
|
||
删除采用“先隐藏、后物理清理”:
|
||
|
||
1. DELETE 接口校验 owner 并写入 `delete_requested_at`。
|
||
2. 列表和详情立即隐藏该录音。
|
||
3. cleanup worker 删除所有物理文件。
|
||
4. 删除 speakers、turns、runs、parts 和 recording 行。
|
||
5. summary source 的快照保留,外键设空。
|
||
6. 文件删除失败时保留待清理记录并指数退避重试。
|
||
|
||
不得先删数据库行再尝试删除文件,否则会再次制造不可追踪孤儿文件。
|
||
|
||
## 7. 音频存储与播放
|
||
|
||
### 7.1 存储键
|
||
|
||
新文件使用:
|
||
|
||
```text
|
||
/data/audio/{owner_uuid}/{recording_uuid}/{part_uuid}.{format}
|
||
```
|
||
|
||
要求:
|
||
|
||
- 路径不包含学生姓名、上传原始文件名或业务标题。
|
||
- 数据库只保存相对 `storage_key`;运行时在受控根目录下解析。
|
||
- 写入临时文件后执行原子 rename,数据库只引用完成文件。
|
||
- 校验解析后路径仍位于 `AUDIO_STORAGE_DIR`,防止目录穿越。
|
||
- 上传原始文件名只在确有 UI 需求时保存为脱敏元数据,默认不保存。
|
||
|
||
### 7.2 上传与格式规范化
|
||
|
||
现有 15 MB 内存读取方式不适合长文件上传。新接口必须流式写磁盘:
|
||
|
||
- 请求体按块写临时文件,不能一次读入 `Vec<u8>`。
|
||
- 同步计算 SHA-256 和字节数。
|
||
- 使用文件头和解码结果验证格式,不能只信扩展名和 MIME。
|
||
- 通过 ffmpeg/ffprobe 获取真实时长、采样率和声道数。
|
||
- 超限立即停止写入并清理临时文件。
|
||
- 首版支持 `mp3`、`aac`、`wav`、`m4a` 和 `amr`,以实际 ffmpeg 镜像能力为准。
|
||
|
||
初始限制建议:
|
||
|
||
- 单文件最大 200 MiB。
|
||
- 单份录音最大 4 小时。
|
||
- 单用户同时上传 1 个文件。
|
||
- 限制值通过环境变量配置,并在 5700U 压测后确认。
|
||
|
||
如果微信小程序文件选择或上传通道的真机限制低于服务端限制,前端显示实际可选范围;不得用健康检查代替真机上传验证。
|
||
|
||
### 7.3 认证播放
|
||
|
||
新增 `GET /api/v1/recordings/{recording_id}/audio`:
|
||
|
||
- 必须认证并校验 owner。
|
||
- 支持 HTTP Range、`206 Partial Content`、`Content-Range` 和正确 MIME。
|
||
- 不返回磁盘路径和永久公开 URL。
|
||
- 不在日志中记录 Range 内容、标题、文件名或路径。
|
||
- 对已请求删除或文件校验失败的录音返回稳定错误。
|
||
- 播放请求不触发转录或改变录音状态。
|
||
|
||
### 7.4 保留、容量和孤儿治理
|
||
|
||
首版默认保留到用户主动删除。上线前完成:
|
||
|
||
- 音频卷每日容量统计。
|
||
- 每用户录音数、总时长和总字节统计。
|
||
- 数据库 part 与磁盘文件双向巡检。
|
||
- 孤儿文件仅报告,不在首轮自动删除。
|
||
- 确认备份是否同时覆盖 PostgreSQL 和音频卷,以及二者恢复时间点如何对应。
|
||
|
||
自动保留策略后续通过配置和用户确认实现,不能仅靠 cron 删除文件。
|
||
|
||
## 8. FunASR 说话人分离技术计划
|
||
|
||
### 8.1 官方能力与本项目风险
|
||
|
||
FunASR 官方仓库当前说明 Paraformer 可组合 `spk_model`,模型列表包含 CAM++;官方教程还给出了 ERes2NetV2 说话人模型和带 speaker 的 ASR 示例。官方当前接口可返回句子级时间和说话人信息。
|
||
|
||
本项目不能直接据此上线,原因是:
|
||
|
||
- 仓库固定 `funasr==1.3.14`,官方 `main` 文档可能对应更高版本。
|
||
- 当前服务只读取结果中的 `text`,没有验证 `sentence_info` 契约。
|
||
- 当前 8 分钟片段分别转录,匿名 speaker 编号可能跨片段互换。
|
||
- 教室噪声、重叠说话、远场麦克风和多学生会显著影响 diarization。
|
||
- 增加 speaker 模型会改变 CPU、内存、模型缓存和推理耗时。
|
||
|
||
### 8.2 技术预研候选
|
||
|
||
候选 A:当前 Paraformer + FSMN-VAD + CT-PUNC + CAM++。
|
||
|
||
- 优点:官方快速示例直接支持,CAM++ 参数量较小,适合作为 CPU 首选候选。
|
||
- 风险:短句、重叠语音和跨片段聚类质量必须实测。
|
||
|
||
候选 B:当前 Paraformer + FSMN-VAD + CT-PUNC + ERes2NetV2。
|
||
|
||
- 优点:官方教程将其作为改进的短时说话人特征候选。
|
||
- 风险:CPU 成本、FunASR 固定版本兼容性和模型缓存增加必须实测。
|
||
|
||
候选 C:独立 diarization pipeline。
|
||
|
||
- 只有 A、B 未达到质量门槛时评估。
|
||
- 不在首轮同时引入第二套大型框架,避免镜像、许可和运维复杂度失控。
|
||
|
||
### 8.3 预研实施步骤
|
||
|
||
1. 建立不提交真实敏感音频的本地测试清单和标注格式。
|
||
2. 在独立分支为 `asr-service` 增加实验开关,不修改生产默认值。
|
||
3. 先测试固定 `funasr==1.3.14` 是否支持所需 `spk_model` 和输出字段。
|
||
4. 若固定版本不支持,再验证最小兼容升级,并记录依赖 diff 和模型版本。
|
||
5. 对相同音频分别运行无 speaker、CAM++、ERes2NetV2。
|
||
6. 保存脱敏指标,不保存或提交真实转录正文。
|
||
7. 在 5700U 单进程 CPU 环境测量 RTF、峰值内存、缓存大小和稳定性。
|
||
8. 选择通过门槛的候选;均不通过时停止正式开发并重新评估方案。
|
||
|
||
### 8.4 测试语料矩阵
|
||
|
||
至少包含经授权的以下场景:
|
||
|
||
| 场景 | 最低样本 |
|
||
|---|---:|
|
||
| 两人、安静、轮流说话 | 3 |
|
||
| 两人、真实教室噪声 | 3 |
|
||
| 三人及以上 | 2 |
|
||
| 明显重叠说话 | 2 |
|
||
| 跨 8 分钟切片同一说话人 | 2 |
|
||
| 上传的 mp3/m4a/wav | 每种至少 1 |
|
||
| 30 分钟以上长录音 | 2 |
|
||
|
||
需要人工标注说话人区间和参考文本,才能计算说话人错误率和识别退化。
|
||
|
||
### 8.5 预研通过门槛
|
||
|
||
以下门槛为首轮工程门槛,样本不足时不得宣称达标:
|
||
|
||
- 所有支持格式无崩溃、无内存持续增长、无空结构化结果。
|
||
- 两人安静样本的中位 DER 不高于 15%。
|
||
- 两人真实教室样本的中位 DER 不高于 25%。
|
||
- 跨 8 分钟边界的同人 cluster 一致率不低于 90%。
|
||
- 加入 speaker 模型后,中文转录错误率相对当前基线恶化不超过 5 个百分点。
|
||
- 5700U 上 P95 RTF 不高于 0.5,即 1 小时音频在 30 分钟内处理完成。
|
||
- 单 FunASR 进程峰值内存不超过 12 GiB,且连续 20 次任务后不持续增长。
|
||
- 失败必须返回稳定错误分类,不能导致 API 或 worker 重启循环。
|
||
|
||
如质量达标但性能未达标,应保持异步处理并评估独立 speaker worker;不得简单提高同一模型实例并发。
|
||
|
||
### 8.6 跨切片说话人一致性
|
||
|
||
首选方案是把一份逻辑录音的 part 按顺序规范化并作为一次完整 diarization 输入,使 speaker cluster 在录音级生成。不得把每个 8 分钟 part 的 `SPK0` 直接视为同一人。
|
||
|
||
若长音频一次处理超出内存或模型限制,再实现:
|
||
|
||
1. 每个 part 提取 speaker embedding。
|
||
2. 在 recording 级对相邻 part 的 cluster 做相似度匹配。
|
||
3. 设置信心阈值,低信心时生成新的匿名 speaker。
|
||
4. 保存原始 provider label 和合并后的 recording speaker,不覆盖原始输出。
|
||
|
||
该备选方案必须单独评审,不能在首版临时拼接字符串完成。
|
||
|
||
### 8.7 ASR 服务响应契约
|
||
|
||
新响应建议:
|
||
|
||
```json
|
||
{
|
||
"text": "完整转录文本",
|
||
"duration_ms": 480000,
|
||
"request_id": "uuid",
|
||
"model": "paraformer-zh",
|
||
"speaker_model": "cam++",
|
||
"speakers": [
|
||
{ "key": "SPK0" },
|
||
{ "key": "SPK1" }
|
||
],
|
||
"turns": [
|
||
{
|
||
"sequence": 1,
|
||
"start_ms": 1200,
|
||
"end_ms": 6800,
|
||
"speaker_key": "SPK0",
|
||
"text": "今天先检查上节课的内容。"
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
契约要求:
|
||
|
||
- `turns` 按时间排序。
|
||
- 时间统一使用整数毫秒。
|
||
- `speaker_key` 必须能在 `speakers` 中找到。
|
||
- 相邻且同 speaker、间隔很短的句子可按明确规则合并,但必须保持时间有序。
|
||
- 无法分离时返回一个 `SPK0`,而不是伪造多说话人。
|
||
- `text` 是 turns 文本的兼容聚合,不是新的独立事实来源。
|
||
- Rust 必须校验时间范围、顺序、空文本和 speaker 引用,再写数据库。
|
||
|
||
## 9. API 设计
|
||
|
||
### 9.1 录音列表
|
||
|
||
`GET /api/v1/recordings`
|
||
|
||
查询参数:
|
||
|
||
- `cursor`:基于 `(created_at, id)` 的不透明游标。
|
||
- `limit`:默认 20,最大 100。
|
||
- `profile_id`:可选。
|
||
- `source`:`live` 或 `upload`。
|
||
- `status`:API 派生状态。
|
||
- `date_from/date_to`:可选。
|
||
|
||
响应只包含列表需要的字段,不返回 turns 和 storage key。
|
||
|
||
### 9.2 创建和上传
|
||
|
||
`POST /api/v1/recordings`
|
||
|
||
- 创建逻辑录音,参数包含 `source`、可选 `profile_id`、标题和 `recorded_at`。
|
||
- 支持 `Idempotency-Key`,防止页面重试创建重复录音。
|
||
|
||
`POST /api/v1/recordings/{id}/parts`
|
||
|
||
- multipart 流式上传。
|
||
- 参数包含 `sequence`、`duration_ms`、`audio_format` 和 `audio`。
|
||
- `(recording_id, sequence)` 唯一;相同 hash 重试返回原 part,不同 hash 冲突返回 409。
|
||
|
||
`POST /api/v1/recordings/{id}/complete`
|
||
|
||
- 校验 part 连续、文件存在、时长和大小一致。
|
||
- 原子地把 ingest 标为 complete 并创建首个 transcription run。
|
||
- 重复调用幂等。
|
||
|
||
上传单文件在客户端表现为一个 recording;服务端可按解码和 ASR 限制生成内部 part,但不把内部切分暴露为多份用户录音。
|
||
|
||
### 9.3 详情和播放
|
||
|
||
- `GET /api/v1/recordings/{id}`:录音元数据、当前 run、speakers 和分页/分块 turns。
|
||
- `GET /api/v1/recordings/{id}/turns?cursor=&speaker_id=`:长对话分页。
|
||
- `GET /api/v1/recordings/{id}/audio`:认证 Range 播放。
|
||
- `PATCH /api/v1/recordings/{id}`:修改标题、学生关联和录制日期。
|
||
- `DELETE /api/v1/recordings/{id}`:进入删除队列。
|
||
|
||
### 9.4 说话人映射和重转录
|
||
|
||
- `PATCH /api/v1/recordings/{recording_id}/speakers/{speaker_id}`:更新 role 和 display name。
|
||
- `POST /api/v1/recordings/{id}/transcription-runs`:创建新 revision。
|
||
- `POST /api/v1/transcription-runs/{id}/retry`:重试 failed run。
|
||
|
||
角色更新使用乐观并发字段或 `updated_at` 前置条件,避免两个页面互相覆盖。
|
||
|
||
### 9.5 直接加入反馈
|
||
|
||
`POST /api/v1/feedback-sessions/{session_id}/recording-sources/apply`
|
||
|
||
建议请求:
|
||
|
||
```json
|
||
{
|
||
"mode": "append",
|
||
"selections": [
|
||
{ "recording_id": "uuid", "selection": "all" },
|
||
{
|
||
"recording_id": "uuid",
|
||
"selection": "turns",
|
||
"turn_ids": ["uuid", "uuid"]
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
服务端负责:
|
||
|
||
- 校验 session active、owner、run ready 和 turn 归属。
|
||
- 按时间顺序格式化带角色的文本。
|
||
- 检查合并后不超过 2000 字。
|
||
- 在一个事务中更新反馈草稿和记录来源应用状态。
|
||
- 返回更新后的 `FeedbackSession`。
|
||
|
||
### 9.6 创建总结任务
|
||
|
||
扩展现有 summary run 请求,使用同一 `selections` 结构。服务端在创建 run 的事务中展开并写入 `feedback_summary_run_sources`。
|
||
|
||
兼容策略:
|
||
|
||
- 新请求写 `feedback_summary_run_sources`。
|
||
- 旧 `lesson_ids` 请求继续写 `feedback_summary_run_lessons`。
|
||
- summary worker 优先读取新 sources;没有新 sources 时走旧 lesson 查询。
|
||
- 新旧请求都保留现有幂等键和 2000 字结果约束。
|
||
|
||
新总结的来源和模型预算规则:
|
||
|
||
- 单次最多选择 20 份录音。
|
||
- 单次最多展开 2000 个 turn。
|
||
- source snapshot 总字符数初始上限为 200,000,通过环境变量配置。
|
||
- 超限时返回明确的 `summary_source_too_large`,不能静默截断或丢弃尾部 turn。
|
||
- worker 按录音和时间顺序格式化来源,例如
|
||
`[00:12-00:18][教师] 对话文本`,匿名角色使用 `Speaker 0` 等稳定标签。
|
||
- 每份录音先按不拆分 turn 的原则组成有限字符块,再生成录音级摘要,最后结合
|
||
`existing_content` 生成不超过 2000 字的总反馈。
|
||
- `prompt_version` 升级为新的明确版本;旧 run 继续保留原版本。
|
||
- 转录文本按不可信输入处理,使用清晰分隔符包裹,并在系统提示中明确禁止执行转录内容中的指令。
|
||
- 记录 source turn 数、source 字符数、模型请求次数和输出字符数,但不记录正文。
|
||
|
||
### 9.7 错误语义
|
||
|
||
至少定义稳定错误分类:
|
||
|
||
- `recording_not_ready`
|
||
- `recording_upload_incomplete`
|
||
- `recording_audio_missing`
|
||
- `recording_format_unsupported`
|
||
- `recording_too_large`
|
||
- `recording_duration_exceeded`
|
||
- `transcription_unavailable`
|
||
- `transcription_failed`
|
||
- `diarization_failed`
|
||
- `speaker_mapping_conflict`
|
||
- `transcript_version_changed`
|
||
- `summary_source_unavailable`
|
||
- `audio_storage_delete_failed`
|
||
|
||
HTTP 状态和用户提示在 API 文档中逐项映射,前端不能依赖错误字符串包含关系判断业务状态。
|
||
|
||
## 10. 后端模块拆分
|
||
|
||
避免继续扩大单个 `recording_routes.rs`,建议按职责拆分:
|
||
|
||
```text
|
||
server/src/recordings/
|
||
mod.rs
|
||
routes.rs # 列表、详情、元数据、删除
|
||
upload.rs # 流式上传、格式校验、finalize
|
||
playback.rs # owner 校验和 Range 响应
|
||
selection.rs # 选择展开、格式化和快照
|
||
|
||
server/src/transcription_worker.rs
|
||
server/src/recording_cleanup_worker.rs
|
||
server/src/summary_worker.rs
|
||
server/src/speech.rs
|
||
```
|
||
|
||
拆分原则:
|
||
|
||
- 不为每条 SQL 创建无意义 repository trait。
|
||
- 跨路由复用的 owner 校验、选择展开和存储键解析提取为明确函数。
|
||
- 网络调用不放在数据库事务中。
|
||
- 上传文件写入和数据库提交之间保留可恢复状态。
|
||
- worker 状态转换使用条件 UPDATE,防止重复 worker 覆盖完成结果。
|
||
|
||
### 10.1 预计文件变更矩阵
|
||
|
||
| 文件或目录 | 计划变更 |
|
||
|---|---|
|
||
| `server/migrations/0006_recording_library.sql` | 新录音、run、speaker、turn 和 summary source 表 |
|
||
| `server/src/recordings/` | 列表、上传、播放、选择和删除路由 |
|
||
| `server/src/routes.rs` | 合并新 router,健康能力字段和 OpenAPI 注册 |
|
||
| `server/src/main.rs` | cleanup worker、配置和共享状态接线 |
|
||
| `server/src/config.rs` | 功能开关、上传限制、speaker 模型和 cleanup 配置 |
|
||
| `server/src/transcription_worker.rs` | 新 run 领取、结构化结果事务写入和版本切换 |
|
||
| `server/src/recording_cleanup_worker.rs` | 物理文件清理、重试和孤儿巡检 |
|
||
| `server/src/summary_worker.rs` | 新 source snapshot 读取和分层总结,保留旧来源兼容 |
|
||
| `server/src/speech.rs` | 解析 speakers/turns,兼容纯文本响应 |
|
||
| `server/src/bin/migrate-recording-library.rs` | legacy backfill 和一致性报告 |
|
||
| `server/API.md`、`server/API_GUIDE.md` | 新接口、错误语义和示例 |
|
||
| `asr-service/app.py` | speaker 模型、结构化响应、长音频处理和校验 |
|
||
| `asr-service/requirements.txt` | 只在阶段 0 证明必要时锁定兼容升级 |
|
||
| `asr-service/README.md` | 模型、环境变量、响应和性能说明 |
|
||
| `asr-service/Dockerfile` | speaker 模型所需依赖和 ffmpeg 能力核验 |
|
||
| `pages/recordings/` | 列表、详情和选择页面 |
|
||
| `components/recording-*` | 经确认的列表行、播放器和 turn 复用组件 |
|
||
| `pages/feedback/index.*` | 精简语音区域并接入录音素材选择 |
|
||
| `utils/types.ts`、`utils/api.ts` | 新类型、API 和上传认证恢复 |
|
||
| `app.json`、`custom-tab-bar/` | 页面注册;tab 仅在真机决策通过后修改 |
|
||
| `tests/` | 选择、状态和前端恢复测试 |
|
||
| `DEBUG_GUIDE.md`、`DEPLOY_5700U.md` | 调试、部署、模型缓存和验收更新 |
|
||
| `docs/observability-runbook.md` | 新事件、查询和故障定位 |
|
||
|
||
## 11. 小程序实现计划
|
||
|
||
### 11.1 类型和 API
|
||
|
||
在 `utils/types.ts` 增加:
|
||
|
||
- `RecordingSummary`
|
||
- `RecordingDetail`
|
||
- `RecordingSpeaker`
|
||
- `TranscriptTurn`
|
||
- `TranscriptionRun`
|
||
- `RecordingSelection`
|
||
- `RecordingListPage`
|
||
|
||
在 `utils/api.ts` 增加对应请求和 snake_case 映射。上传复用认证刷新逻辑,必须覆盖 `wx.uploadFile` token 失效重试和幂等键。
|
||
|
||
### 11.2 可复用组件
|
||
|
||
只为真实复用点增加组件:
|
||
|
||
- `components/recording-list-item`:列表页和选择页复用。
|
||
- `components/transcript-turn`:详情页和选择预览复用。
|
||
- `components/recording-player`:详情页复用,封装播放和时间跳转。
|
||
|
||
筛选栏和固定底栏先保留为页面局部结构,避免过早抽象。
|
||
|
||
### 11.3 性能要求
|
||
|
||
- turns 使用分页或分块加载,不一次 `setData` 数百条完整对象。
|
||
- 选择状态使用 ID 集合并只更新受影响行。
|
||
- 播放时间更新节流,不以 100ms 频率调用整页 `setData`。
|
||
- 页面隐藏时停止播放进度更新和高频轮询。
|
||
- 只轮询 queued/processing run;ready/failed 停止轮询。
|
||
- 列表空态、失败态和加载骨架使用稳定尺寸,避免布局跳动。
|
||
|
||
### 11.4 可访问性和视觉约束
|
||
|
||
- 对话内容是详情页主要区域,工具栏和状态栏保持紧凑。
|
||
- 一个区域内只保留一个蓝色主操作。
|
||
- 使用白、石墨和浅灰中性色,不新增装饰渐变和重阴影。
|
||
- 控件圆角遵循当前小程序设计,不用夸张胶囊按钮。
|
||
- 播放、删除、重试等按钮提供明确 `aria-label`。
|
||
- 说话人不能只靠颜色区分,同时显示角色文字。
|
||
- 长标题、长角色名和 2000 字边界在最小真机宽度验证。
|
||
|
||
## 12. 数据迁移与兼容发布
|
||
|
||
### 12.1 Expand
|
||
|
||
新增迁移建议从 `0006_recording_library.sql` 开始:
|
||
|
||
1. 创建 `recordings`、`recording_audio_parts`、`transcription_runs`、`recording_speakers`、`transcript_turns`。
|
||
2. 创建 `feedback_summary_run_sources`。
|
||
3. 创建所有 CHECK、UNIQUE、FK 和必要索引。
|
||
4. `recordings.current_transcription_run_id` 在 run 表创建后再添加 FK。
|
||
5. 不修改现有表的非空约束,不删除旧表或旧索引。
|
||
|
||
迁移必须在真实规模副本上测量锁时间。大表索引若不能在事务迁移中安全创建,应拆分为运维步骤并使用 PostgreSQL `CREATE INDEX CONCURRENTLY`。
|
||
|
||
### 12.2 Backfill
|
||
|
||
新增可重复运行的 `migrate-recording-library` 二进制或受控迁移任务:
|
||
|
||
1. 每个 `lesson_session` 创建一个 `source='legacy'` recording。
|
||
2. `legacy_lesson_id` 唯一保证重复执行不重复创建。
|
||
3. 每个 `audio_segment` 创建一个 recording part;先将现有绝对 `audio_path` 规范化并验证位于
|
||
`AUDIO_STORAGE_DIR` 内,再转换为相对 storage key。目录外路径只报告,不自动导入。
|
||
4. ready segment 的纯文本转为一个 `unknown` speaker 下的 turn。
|
||
5. turn 的时间范围按 segment 顺序和 duration 累计估算,并标记为 legacy 导入,不伪装成模型级精确时间。
|
||
6. processing/failed segment 保留原状态映射,是否重转录由用户或后续任务决定。
|
||
7. 每批固定数量提交,输出游标、成功数、跳过数和失败数。
|
||
8. 回填不移动物理文件,避免大批量 I/O 和回滚困难。
|
||
|
||
一致性检查:
|
||
|
||
- legacy recording 数等于 lesson 数。
|
||
- part 数等于 audio segment 数。
|
||
- 每个 ready legacy segment 至少有一个非空 turn。
|
||
- duration 和 byte size 汇总无负值。
|
||
- 所有 storage key 可解析且文件存在,缺失文件单独报告。
|
||
|
||
### 12.3 双读和切换
|
||
|
||
1. API 上线新表和新接口,功能开关默认关闭。
|
||
2. 完成 backfill 并运行一致性报告。
|
||
3. 录音资料列表先只对测试账号开放。
|
||
4. 新现场录音写新表;兼容期如仍需旧页面显示,显式双写映射关系,不复制音频文件。
|
||
5. summary worker 支持新 sources 和旧 lessons 两条读取路径。
|
||
6. 小程序启用新页面和选择流程。
|
||
7. 观察一个完整发布周期后,停止旧前端创建 lesson 链路。
|
||
8. 旧 API 和表至少再保留一个发布周期。
|
||
|
||
### 12.4 Contract
|
||
|
||
只有满足以下条件才进入 contract:
|
||
|
||
- 没有旧版本小程序仍在产生新 lesson 数据。
|
||
- 新录音、重试、删除、总结和回滚均通过生产验证。
|
||
- legacy 一致性报告无未解释差异。
|
||
- 已完成数据库和音频卷可恢复备份。
|
||
|
||
contract 阶段另建计划,不在本次直接安排删除 `lesson_sessions` 或 `audio_segments`。
|
||
|
||
### 12.5 回滚
|
||
|
||
- 关闭 `RECORDING_LIBRARY_ENABLED`,反馈页恢复旧语音入口。
|
||
- 关闭 `ASR_DIARIZATION_ENABLED`,新转录回退纯文本模式。
|
||
- 关闭 `RECORDING_UPLOAD_ENABLED`,保留已上传数据但隐藏新上传入口。
|
||
- API 旧路由、旧 worker 查询和旧表保持可用。
|
||
- 新表保留,不在应用回滚时执行 down migration。
|
||
- 新录音数据不得自动反向写成不完整 lesson;需要时提供显式兼容导出任务。
|
||
- 回滚过程中不删除任何音频文件。
|
||
|
||
## 13. 功能开关和配置
|
||
|
||
建议增加:
|
||
|
||
```env
|
||
RECORDING_LIBRARY_ENABLED=false
|
||
RECORDING_UPLOAD_ENABLED=false
|
||
ASR_DIARIZATION_ENABLED=false
|
||
ASR_SPEAKER_MODEL=cam++
|
||
MAX_RECORDING_UPLOAD_BYTES=209715200
|
||
MAX_RECORDING_DURATION_SECONDS=14400
|
||
RECORDING_CLEANUP_ENABLED=false
|
||
```
|
||
|
||
要求:
|
||
|
||
- 开关在健康接口中只返回启用状态,不返回敏感配置。
|
||
- 小程序根据 health capability 决定展示入口。
|
||
- speaker 模型变化必须更新 `config_version`,不能静默改变同一 run 的结果。
|
||
- cleanup 首次上线保持关闭,只运行 dry-run 巡检。
|
||
|
||
## 14. 可观测性计划
|
||
|
||
### 14.1 新事件
|
||
|
||
- `recording_created`
|
||
- `recording_part_uploaded`
|
||
- `recording_completed`
|
||
- `recording_upload_failed`
|
||
- `recording_playback_failed`
|
||
- `transcription_run_queued`
|
||
- `transcription_run_started`
|
||
- `transcription_run_finished`
|
||
- `transcription_run_failed`
|
||
- `speaker_mapping_updated`
|
||
- `recording_delete_requested`
|
||
- `recording_cleanup_finished`
|
||
- `recording_cleanup_failed`
|
||
- `recording_selection_applied`
|
||
- `summary_sources_snapshotted`
|
||
|
||
### 14.2 允许记录的字段
|
||
|
||
- `recording_id`
|
||
- `transcription_run_id`
|
||
- `feedback_session_id`
|
||
- `summary_run_id`
|
||
- `owner` 不记录真实 ID 之外的身份信息;需要统计时使用不可逆聚合方式。
|
||
- `status`、`error_class`、`attempt`。
|
||
- `audio_size_bytes`、`audio_duration_ms`、`turn_count`、`speaker_count`。
|
||
- `queue_wait_ms`、`inference_latency_ms`、`rtf`、`cleanup_latency_ms`。
|
||
- `provider`、`asr_model`、`speaker_model`、`config_version`。
|
||
|
||
禁止记录:
|
||
|
||
- 对话正文、总结正文和文件内容。
|
||
- 学生姓名、录音标题、说话人显示名。
|
||
- 原始文件名、绝对路径和 storage key。
|
||
- 上传请求体和第三方原始错误响应。
|
||
|
||
### 14.3 生产观察指标
|
||
|
||
- queued/processing run 数量和最老任务等待时间。
|
||
- 转录成功率、diarization 失败率和按 error_class 分布。
|
||
- P50/P95 RTF、内存峰值和音频时长分布。
|
||
- 每日上传字节、音频卷增长和剩余空间。
|
||
- cleanup 待处理数量、最老任务年龄和失败次数。
|
||
- 总结来源 turn 数、字符数和总结失败率。
|
||
- API 列表、详情、播放首字节和上传完成延迟。
|
||
|
||
## 15. 安全与隐私
|
||
|
||
录音和对话属于敏感教学数据,必须满足:
|
||
|
||
1. 每个录音、speaker、turn、播放和总结来源查询都通过 recording owner 反查授权。
|
||
2. 不接受前端传入 `owner_id` 作为授权依据。
|
||
3. 上传文件执行大小、时长、格式和解码验证。
|
||
4. ffmpeg 处理设置超时、资源上限和受控临时目录。
|
||
5. 临时文件在成功、失败、超时和进程恢复路径都能清理。
|
||
6. 播放接口不生成永久免认证 URL。
|
||
7. 删除操作提供清晰确认并记录稳定审计事件,不记录正文。
|
||
8. 数据库备份和音频卷备份使用相同的访问权限和恢复演练要求。
|
||
9. 产品界面明确录音用途和删除后果,遵循微信麦克风权限说明。
|
||
10. 不以说话人 embedding 建立跨录音身份库,除非后续完成单独隐私评审。
|
||
|
||
## 16. 分阶段实施与提交计划
|
||
|
||
每个阶段独立提交、可测试、可回滚。不得把数据库、ASR、全部页面和切换一次性合并为一个提交。
|
||
|
||
### 阶段 0:基线和技术预研
|
||
|
||
交付:
|
||
|
||
- 代表性音频测试清单和本地标注格式。
|
||
- 固定 FunASR 版本的 CAM++/ERes2NetV2 兼容性结论。
|
||
- 5700U CPU 质量、RTF、内存和缓存报告。
|
||
- 最终 speaker 模型和输出契约决定。
|
||
|
||
建议提交:
|
||
|
||
- `test(asr): add diarization evaluation fixtures`
|
||
- `docs(asr): record speaker diarization benchmark`
|
||
|
||
退出条件:第 8.5 节门槛通过,或形成明确否决结论。
|
||
|
||
### 阶段 1:数据和存储基础
|
||
|
||
交付:
|
||
|
||
- 新表、约束和索引。
|
||
- 流式 part 上传和 finalize。
|
||
- 新存储键、hash、时长和大小校验。
|
||
- 认证 Range 播放。
|
||
- cleanup worker 和 dry-run 孤儿报告。
|
||
|
||
建议提交:
|
||
|
||
- `feat(recordings): add recording library schema`
|
||
- `feat(recordings): stream audio parts to storage`
|
||
- `feat(recordings): add authenticated audio playback`
|
||
- `feat(recordings): add deferred audio cleanup`
|
||
|
||
退出条件:上传、播放、删除、失败恢复和 owner 隔离集成测试通过。
|
||
|
||
### 阶段 2:结构化转录和说话人分离
|
||
|
||
交付:
|
||
|
||
- ASR 新响应契约。
|
||
- speaker 模型配置和健康信息。
|
||
- transcription run 版本、队列和原子写入 speakers/turns。
|
||
- 重试和重转录。
|
||
- 模型错误分类和指标。
|
||
|
||
建议提交:
|
||
|
||
- `feat(asr): return timestamped speaker turns`
|
||
- `feat(voice): persist versioned transcript turns`
|
||
- `fix(voice): recover expired diarization jobs`
|
||
|
||
退出条件:技术预研样本和端到端 DB 写入均通过。
|
||
|
||
### 阶段 3:录音资料页面
|
||
|
||
交付:
|
||
|
||
- 列表 API 和游标分页。
|
||
- 详情、turn 分页和 speaker 映射 API。
|
||
- 录音列表页、详情页和播放器。
|
||
- 处理状态、失败重试、删除和角色映射。
|
||
|
||
建议提交:
|
||
|
||
- `feat(recordings): add recording library APIs`
|
||
- `feat(miniprogram): add recording library page`
|
||
- `feat(miniprogram): add transcript review workspace`
|
||
|
||
退出条件:真机可浏览、播放、映射、重试和删除录音。
|
||
|
||
### 阶段 4:素材选择和总结
|
||
|
||
交付:
|
||
|
||
- 反馈素材选择页。
|
||
- 选择本地恢复。
|
||
- 直接加入反馈接口。
|
||
- summary source 快照。
|
||
- summary worker 新旧来源兼容。
|
||
- 反馈页精简和总结预览接入。
|
||
|
||
建议提交:
|
||
|
||
- `feat(feedback): select recording transcript turns`
|
||
- `feat(summary): snapshot selected transcript sources`
|
||
- `feat(feedback): compose feedback from recording library`
|
||
|
||
退出条件:未选中的 turn 不进入输入框或 AI 请求,历史总结不受重转录影响。
|
||
|
||
### 阶段 5:历史回填和灰度切换
|
||
|
||
交付:
|
||
|
||
- 可重复运行的 legacy backfill。
|
||
- 一致性报告。
|
||
- 测试账号功能开关。
|
||
- 生产灰度、监控和回滚演练。
|
||
- 小程序导航最终决策。
|
||
|
||
建议提交:
|
||
|
||
- `feat(recordings): backfill legacy voice lessons`
|
||
- `chore(recordings): enable recording library rollout`
|
||
- `docs(recordings): add operations runbook`
|
||
|
||
退出条件:一个完整发布周期无高严重度数据或隐私问题。
|
||
|
||
## 17. 测试计划
|
||
|
||
### 17.1 Rust 单元测试
|
||
|
||
- 存储键只解析到允许目录。
|
||
- Range 请求边界、无效范围和完整下载。
|
||
- 选择展开排序、去重和所有权校验。
|
||
- 整份录音选择正确展开全部当前 turns。
|
||
- 2000 字限制和角色格式化。
|
||
- source hash 对顺序、文本和版本变化敏感。
|
||
- 状态机拒绝非法转换。
|
||
- 同一幂等键返回同一录音或总结任务。
|
||
|
||
### 17.2 PostgreSQL 集成测试
|
||
|
||
- 所有 CHECK、UNIQUE 和 FK 生效。
|
||
- 不同 owner 无法查询、播放、修改、选择或删除彼此录音。
|
||
- worker 并发领取不重复处理。
|
||
- ready run 原子切换 current pointer。
|
||
- 新 run 失败不覆盖旧 ready run。
|
||
- summary source 在录音删除后保留快照。
|
||
- cleanup 失败不会丢失待处理记录。
|
||
- backfill 重跑不产生重复 recording、part 或 turn。
|
||
- 游标分页无重复和遗漏。
|
||
|
||
### 17.3 Python/ASR 测试
|
||
|
||
- 无 speaker 模式保持当前响应兼容。
|
||
- speaker 模式返回合法 speakers 和 turns。
|
||
- 空语音、单人、多人、重叠语音和损坏音频。
|
||
- 所有支持格式和长音频。
|
||
- 临时文件在每条失败路径清理。
|
||
- 连续 20 次推理无内存持续增长。
|
||
- request ID 从 API 传播到 ASR 日志。
|
||
|
||
### 17.4 小程序测试
|
||
|
||
- 列表筛选、分页、刷新和空态。
|
||
- 上传成功、网络中断、认证刷新和重试。
|
||
- 播放、暂停、拖动、跳到 turn 时间。
|
||
- speaker 映射和按 speaker 全选。
|
||
- 跨页面返回后选择恢复。
|
||
- 后台转录完成后的状态刷新。
|
||
- 失败重试和删除确认。
|
||
- 直接加入超过 2000 字时不修改草稿。
|
||
- 总结 ready 后预览和替换输入框。
|
||
- 320px 宽度、常见安卓机和 iPhone 真机布局。
|
||
|
||
### 17.5 端到端验收场景
|
||
|
||
1. 现场录制 20 分钟两人对话,跨越至少两个 part。
|
||
2. 后台转录并保持同一说话人跨 part 一致。
|
||
3. 用户映射教师和学生角色。
|
||
4. 只选择学生的若干 turn 直接加入反馈。
|
||
5. 再选择两份录音生成总结。
|
||
6. 验证 AI 输入不包含未选 turn。
|
||
7. 预览后替换输入框并保存反馈。
|
||
8. 在录音库仍可查看录音;删除录音后历史反馈和总结仍存在。
|
||
9. 重启 API 和 FunASR 后任务恢复,模型不重复下载,音频仍存在。
|
||
|
||
### 17.6 常规校验命令
|
||
|
||
开发提交前至少执行:
|
||
|
||
```bash
|
||
npm test
|
||
cd server && cargo fmt --check
|
||
cd server && cargo test
|
||
cd server && cargo clippy --all-targets --all-features -- -D warnings
|
||
python3 -m py_compile asr-service/app.py asr-service/logging_config.py
|
||
docker compose -f compose.deploy.yml config
|
||
```
|
||
|
||
涉及 FunASR 依赖变更时还要在镜像内执行 `pip check` 和真实音频推理,不能只做 Python 语法检查。
|
||
|
||
## 18. 部署与发布计划
|
||
|
||
### 18.1 发布前
|
||
|
||
1. 备份远程 PostgreSQL,并记录可恢复时间点。
|
||
2. 备份或快照 `teaching-feedback-audio` 卷。
|
||
3. 记录当前镜像 tag、Git SHA、模型缓存大小和健康状态。
|
||
4. 在生产规模副本验证迁移锁时间和 backfill 批次。
|
||
5. 确认新 speaker 模型缓存所需磁盘和首次下载时间。
|
||
6. 功能开关全部保持关闭。
|
||
|
||
### 18.2 发布顺序
|
||
|
||
1. 拉取已经测试并推送的提交。
|
||
2. 构建 API 和 FunASR 新镜像,不停止现有容器。
|
||
3. 运行 expand migration。
|
||
4. 启动新 API 和 FunASR,保持新功能关闭。
|
||
5. 验证健康检查、旧录音链路和旧总结链路。
|
||
6. 运行 legacy backfill dry-run,再执行正式 backfill。
|
||
7. 对测试账号启用录音资料和 diarization。
|
||
8. 完成真实录音、上传、播放、选择、总结和删除验收。
|
||
9. 扩大灰度范围。
|
||
10. 最后发布使用新入口的小程序版本。
|
||
|
||
### 18.3 回滚触发条件
|
||
|
||
出现以下任一情况立即关闭对应功能:
|
||
|
||
- 数据跨 owner 暴露或未授权播放。
|
||
- 新转录覆盖或丢失既有 ready 转录。
|
||
- 删除流程造成不可恢复的非目标音频删除。
|
||
- worker 重复处理导致队列持续增长。
|
||
- FunASR 容器内存持续增长或反复重启。
|
||
- speaker 输出结构错误导致批量任务失败。
|
||
- 新总结包含未选中的对话。
|
||
- 数据库迁移或查询导致生产明显锁等待和超时。
|
||
|
||
## 19. 验收标准
|
||
|
||
### 19.1 产品验收
|
||
|
||
- 用户可以独立进入录音资料,不依赖未保存反馈草稿。
|
||
- 现场录音和上传文件出现在统一列表。
|
||
- 用户可以播放、筛选、重试、重转录和删除本人录音。
|
||
- 详情页按时间和匿名说话人展示对话。
|
||
- 用户可以映射角色并按 turn 选择。
|
||
- 用户可以选择整份录音或多份录音生成总结。
|
||
- 总结可预览,确认后才写入反馈输入框。
|
||
- 录音保存、反馈保存和录音删除互不错误级联。
|
||
|
||
### 19.2 数据完整性验收
|
||
|
||
- recording、part、run、speaker、turn 数量关系满足约束。
|
||
- 所有 ready recording 都有 current ready run 和至少一个非空 turn。
|
||
- 所有 summary run 的新来源均有完整快照。
|
||
- legacy backfill 可重复执行且结果一致。
|
||
- 删除后无数据库悬挂 part,失败删除仍有可重试记录。
|
||
- 孤儿巡检结果为零或每一项都有已记录处置。
|
||
|
||
### 19.3 安全验收
|
||
|
||
- 两个测试用户之间录音列表、详情、播放和选择完全隔离。
|
||
- 伪造 recording、speaker、turn ID 均返回 404 或等价资源不可见响应。
|
||
- 日志检索不到标题、学生姓名、转录文本、原始文件名和路径。
|
||
- Range 播放必须携带有效认证。
|
||
- 上传恶意扩展名、损坏文件和超限文件被拒绝且临时文件清理。
|
||
|
||
### 19.4 性能验收
|
||
|
||
- 录音列表 20 条 P95 API 延迟目标低于 300ms,不含公网网络时间。
|
||
- 详情元数据 P95 低于 300ms;turn 分页 P95 低于 500ms。
|
||
- 音频 Range 首字节在局域网 P95 低于 500ms。
|
||
- 数据库查询计划使用 owner + 时间游标索引,无全量 offset 扫描。
|
||
- diarization 达到第 8.5 节质量和资源门槛。
|
||
- summary worker 在 100 份录音或大量 turns 输入时受明确字符/token 预算限制。
|
||
|
||
### 19.5 运维验收
|
||
|
||
- API、FunASR、migration 和 cleanup 状态可从结构化日志定位。
|
||
- 容器重启后 queued/processing 任务可恢复。
|
||
- 模型缓存和音频卷重启后保持。
|
||
- PostgreSQL 与音频卷完成一次关联恢复演练。
|
||
- 回滚开关和旧链路在生产实际演练成功。
|
||
|
||
## 20. 风险清单
|
||
|
||
| 风险 | 严重度 | 应对 |
|
||
|---|---|---|
|
||
| 说话人分离质量不足 | 高 | 技术预研门槛、匿名标签、用户映射、允许 unknown |
|
||
| 跨 8 分钟 speaker 编号互换 | 高 | 录音级 diarization,禁止直接拼接 part 标签 |
|
||
| 数据库记录与文件不一致 | 高 | 受控删除队列、hash、巡检和恢复状态 |
|
||
| 跨用户音频泄露 | 阻断 | 每条接口 owner 反查、Range 集成测试 |
|
||
| 总结包含未选内容 | 高 | 事务内展开 turn 并快照,worker 只读快照 |
|
||
| 大文件占满内存或磁盘 | 高 | 流式上传、限额、临时目录和磁盘告警 |
|
||
| FunASR 升级引入回归 | 高 | 固定版本优先、对照基线、独立镜像 tag 和回滚 |
|
||
| 新旧表迁移丢失历史 | 高 | expand/backfill/一致性报告,不提前 contract |
|
||
| 长对话导致小程序卡顿 | 中 | turn 分页、局部 setData、节流播放进度 |
|
||
| 五项 tab 拥挤 | 中 | 真机决策门槛,不通过则普通页面入口 |
|
||
| 自动角色识别误导用户 | 中 | 不自动断言角色,必须用户确认 |
|
||
| 存储长期增长 | 中 | 容量监控、配额、后续保留策略 |
|
||
|
||
## 21. 开工前需要确认的产品决定
|
||
|
||
以下决定有推荐默认值,不应在实现中隐式猜测:
|
||
|
||
| 决定 | 推荐默认值 |
|
||
|---|---|
|
||
| 录音保留期限 | 保留到用户主动删除 |
|
||
| 学生档案删除时录音处理 | 录音保留,`profile_id` 置空 |
|
||
| 说话人角色 | 教师、学生、家长、其他、未知,允许自定义显示名 |
|
||
| 是否自动认定教师 | 否 |
|
||
| 首版是否允许修改转录文本 | 否,只允许角色映射 |
|
||
| 上传限制 | 200 MiB、4 小时,完成真机和服务端验证后确认 |
|
||
| 录音删除后历史总结 | 保留来源快照,不再提供播放 |
|
||
| 底部导航 | 真机通过后增加“录音资料”,否则保留普通页面入口 |
|
||
| diarization 模型 | 预研后在 CAM++ 和 ERes2NetV2 中选择 |
|
||
|
||
## 22. Definition of Done
|
||
|
||
只有同时满足以下条件,本功能才算完成:
|
||
|
||
1. 本文档中首版目标均有对应实现或明确批准的范围变更。
|
||
2. 数据库迁移、backfill、API、worker、小程序和部署文档全部合并。
|
||
3. 自动测试、代表性音频评测和真机端到端测试通过。
|
||
4. owner 隔离、日志脱敏和删除恢复通过安全验收。
|
||
5. 5700U CPU 上 diarization 达到质量和资源门槛。
|
||
6. 生产完成灰度、监控、回滚和一次容器重启恢复验证。
|
||
7. API 文档、调试指南、部署指南和观测 runbook 已同步更新。
|
||
8. 所有提交使用仓库现有 Conventional Commit 风格并已推送。
|
||
9. 服务器可通过 `git pull --ff-only` 获取确定的 Git SHA 重新部署。
|
||
10. 未完成项、残余风险和生产开关状态在发布报告中明确列出。
|
||
|
||
## 23. 参考资料
|
||
|
||
项目内依据:
|
||
|
||
- `README.md`
|
||
- `DEBUG_GUIDE.md`
|
||
- `DEPLOY_5700U.md`
|
||
- `server/API.md`
|
||
- `server/migrations/0002_voice_feedback_sessions.sql`
|
||
- `server/migrations/0003_async_transcription_jobs.sql`
|
||
- `server/migrations/0005_feedback_summary_runs.sql`
|
||
- `server/src/recording_routes.rs`
|
||
- `server/src/transcription_worker.rs`
|
||
- `server/src/summary_worker.rs`
|
||
- `server/src/speech.rs`
|
||
- `asr-service/app.py`
|
||
- `pages/feedback/index.ts`
|
||
- `pages/feedback/index.wxml`
|
||
|
||
官方技术依据,均应在实际实现时锁定到使用版本对应的文档或提交:
|
||
|
||
- [FunASR 官方仓库与 Paraformer speaker model 示例](https://github.com/modelscope/FunASR)
|
||
- [FunASR 官方教程中的 Speaker Verification / Diarization](https://github.com/modelscope/FunASR/blob/main/docs/tutorial/README.md)
|
||
- [FunASR 官方 Paraformer 实现说明](https://github.com/modelscope/FunASR/blob/main/funasr/models/paraformer/model.py)
|
||
|
||
官方 `main` 分支能力不等于本项目固定 `funasr==1.3.14` 已验证能力;阶段 0 的固定版本兼容测试是正式开发的阻断门槛。
|