# 📋 钢琴课精华视频生成技能 - 会话状态与交接文档 > **生成时间**: 2026-04-02 > **最后更新**: 2026-04-03 > **当前状态**: ✅ 全流程验证通过,环境配置已固化 --- ## 🎯 核心目标 利用 `piano-lesson-highlight-generator` 技能,根据 PPT 和视频自动生成精华视频(<15分钟),包含标题卡、字幕、转场。 --- ## 🔧 环境配置 **详见 `ENVIRONMENT.md`** 关键信息: - **Python 环境**: `D:/ProgramData/anaconda3/envs/py312_cuda/python.exe`(Python 3.12.13) - **PyTorch**: 2.5.1+cu121(**必须 GPU**,CPU 会超时) - **Whisper 模型**: `D:/AI/LM-Models/faster-whisper/large-v3/` - **运行命令**: 直接使用完整路径调用 Python,不要用 `conda activate` ### 环境验证 ```bash "D:/ProgramData/anaconda3/envs/py312_cuda/python.exe" -c "import torch; print(torch.cuda.is_available())" # 必须输出 True ``` --- ## 📊 已解决的结构性问题 ### 1. 知识点匹配错误(核心修复) | 问题 | 根因 | 修复方案 | 状态 | |------|------|----------|------| | 匹配到导读区而非教学区 | 搜索词按顺序匹配,找到第一个就 break;评分只看密度 | 完整关键词+核心子词+相关词映射+数字归一化收集所有 segment;评分综合考量出现次数、文本量、讲解密度、孤立程度 | ✅ | | 匹配到回顾区而非教学区 | 回顾区有更多相关 segment | 靠近作业时间惩罚 + 回顾性语言检测("刚才"、"刚刚"等) | ✅ | | 匹配到"等下再说"而非实际教学 | 预告和实际教学无法区分 | 预告/讲解特征词检测;推迟语言检测;导读过滤(无讲解 + segment ≤ 2 → 跳过) | ✅ | | 通用词误匹配(如"记号"匹配到无关内容) | 搜索词包含 2 字通用词 | 通用后缀过滤(记号、符号、音符等不单独匹配);相关词映射 | ✅ | | 数字不匹配("十六分音符"≠"16分音符") | Whisper 将中文数字识别为阿拉伯数字 | 数字归一化匹配(中文数字↔阿拉伯数字互转) | ✅ | | "掀起你的盖头来"只在导读出现 | 转录中未出现该歌曲名 | 导读过滤:无讲解特征 + segment ≤ 2 → 跳过 | ✅ | ### 2. 字幕同步问题 | 问题 | 根因 | 修复方案 | 状态 | |------|------|----------|------| | 字幕 1分18秒开始乱套 | clip3/clip4 重叠导致 JSON 内容重复,偏移计算用 config duration 而非实际 segments end | 偏移改用 JSON segments 实际最大 end 时间 | ✅ | | 字幕滞后 2-3 秒 | 转录过滤包含跨边界片段,调整后的时间戳超出 clip 实际时长 | 限制时间戳在 clip 实际时长范围内 | ✅ | | clip2(延音线)被跳过 | Whisper 把"延音"识别为"言音",内容验证失败 | 内容验证时先应用术语纠正(言音→延音、演音→延音等) | ✅ | ### 3. 作业片段匹配 | 问题 | 根因 | 修复方案 | 状态 | |------|------|----------|------| | 作业片段匹配不到 | 引导语列表不全 | 用"作业"词密度替代引导语匹配 | ✅ | | 作业片段截断太早 | detect_gap_cutoff 对作业场景太敏感 | 间隔阈值从 15s 放宽到 30s;视频末尾前 2 分钟兜底 | ✅ | | 作业结束不准确 | 无法识别口语化结束表达 | 模糊匹配 30+ 种口语表达("作业.*就这样"、"作业.*就这些"、"下课"等) | ✅ | ### 4. 去重逻辑优化 | 问题 | 根因 | 修复方案 | 状态 | |------|------|----------|------| | 多个知识点在同一区域被丢弃 | 去重逻辑只保留密度最高的,丢弃其他 | 调整边界让相邻知识点共存,而非丢弃 | ✅ | ### 5. GPU 资源管理 | 问题 | 根因 | 修复方案 | 状态 | |------|------|----------|------| | 僵尸进程占用 GPU 显存 | 多次启动转录进程,旧进程未释放 | 转录后 `del model` + `gc.collect()` + `torch.cuda.empty_cache()` | ✅ | | `taskkill` 自杀命令 | 清理命令杀掉了当前进程 | 移除转录前的 taskkill,改为转录后显式释放模型 | ✅ | --- ## 🏗️ 知识点匹配架构 ### 评分公式 ``` score = (base_score + full_bonus) × text_bonus × isolation_penalty × teaching_bonus × review_penalty × defer_penalty × preview_penalty ``` | 因子 | 计算方式 | 作用 | |------|---------|------| | base_score | total_count × avg_rel | 相关 segment 数量 × 平均相关度 | | full_bonus | full_count × 2.0 | 完整关键词出现次数奖励 | | text_bonus | min(total_text_len / 30, 5.0) | 上下文文本量加成 | | isolation_penalty | 1.0 / (1.0 + other_kw_count × 0.5) | 和其他知识点一起出现惩罚 | | teaching_bonus | 1.0 + teaching_density × 2.0 | 讲解词占比加成 | | review_penalty | max(0.1, time_to_homework / 300) | 靠近作业时间惩罚 | | defer_penalty | max(0.1, 1.0 - defer_ratio × 2.0) | 推迟语言惩罚 | | preview_penalty | max(0.1, 1.0 - preview_ratio × 2.0) | 预告语言惩罚 | ### 相关度评分 | 匹配类型 | 分数 | 示例 | |---------|------|------| | 完整关键词 | 3.0 | "附点音符" 匹配 "附点音符" | | 数字归一化 | 2.5 | "十六分音符" 匹配 "16分音符" | | 核心子词 | 2.0 | "双音的支撑" 匹配 "双音支撑" | | 相关词映射 | 1.5 | "升降记号" 匹配 "升号"/"降号" | | 前缀匹配 | 1.5 | "附点音符" 匹配 "附点" | | 核心词 | 1.0 | 3-4 字核心词匹配 | ### 相关词映射 ```python related_terms = { "升降记号": ["升号", "降号", "升记", "降记", "升降", "升半", "降半"], "还原记号": ["还原"], "附点音符": ["附点"], "延音线": ["延音", "同音连线"], "双音的支撑": ["双音", "支撑"], "婚礼进行曲": ["婚礼"], "掀起你的盖头来": ["盖头来", "盖头", "掀起"], "十六分音符": ["16分", "十六分"], "八分音符": ["8分", "八分"], } ``` ### 特征词检测 | 类型 | 关键词 | |------|--------| | 推迟语言 | "等下再说"、"等下讲"、"后面再说"、"稍后再说"、"先不说" | | 预告语言 | "先说一下"、"先讲一下"、"首先说"、"我先说"、"提一下" | | 讲解语言 | "因为"、"所以"、"就是"、"什么意思"、"为什么"、"怎么"、"比如说"、"大家看"、"弹"、"按"、"练" | | 回顾语言 | "刚才"、"刚刚"、"今天学"、"今天讲"、"回顾"、"练习一下"、"复习" | ### 作业结束检测(模糊匹配 30+ 种表达) | 类型 | 匹配模式 | 示例 | |------|---------|------| | 明确下课 | `下课`、`拜拜`、`再见` | "下课" | | 作业完成 | `作业.*就这样`、`作业.*就这些`、`作业.*讲到这里` | "今天的作业就这样"、"关于作业就讲到这里" | | 通用结束 | `就到这里`、`就这样了`、`说完了`、`没什么.*说的` | "就这些了"、"没什么要说的了" | | 群发通知 | `发群`、`到时候.*发` | "到时候我发群里" | --- ## 📂 项目目录结构 ``` projects/piano-lesson-highlights/ ├── data/ # 原始输入数据 │ ├── lesson2/ │ │ ├── video.mp4 │ │ └── course.pptx │ └── lesson3/ │ ├── video.mp4 │ └── course.pptx └── cases/ # 每个案例的工作区 ├── lesson2/ │ ├── config.yaml │ ├── intermediates/ │ │ └── full_transcript.json │ └── output/ │ ├── v1_final.mp4 │ └── subs/ └── lesson3/ ├── config.yaml ├── intermediates/ │ └── full_transcript.json └── output/ ├── v1_final.mp4 └── subs/ ``` --- ## ✅ lesson2 验证结果 | # | 知识点 | 匹配位置 | 实际内容 | 状态 | |---|--------|---------|---------|------| | 1 | 附点音符 | 3126s (52min) | 实际教学区 | ✅ | | 2 | 延音线 | 4040s (67min) | 实际教学区 | ✅ | | 3 | 升降记号 | 4237s (70min) | 实际教学区 | ✅ | | 4 | 还原记号 | 4682s (78min) | 实际教学区 | ✅ | | 5 | 双音的支撑 | 5051s (84min) | 实际教学区 | ✅ | | 6 | 婚礼进行曲 | 5345s (89min) | 实际教学区 | ✅ | | 7 | 作业 | 6515s-6998s (482s) | 作业区 | ✅ | **产出**: - v1_final.mp4: 679秒(11.3分钟),133.3 MB - 380 条字幕(original/terms/ai 三个版本) --- ## ✅ lesson3 验证结果 | # | 知识点 | 匹配位置 | 实际内容 | 状态 | |---|--------|---------|---------|------| | 1 | 八分音符 | 2332s (38min) | 实际教学区 | ✅ | | 2 | 十六分音符 | 2456s (40min) | 实际教学区(数字归一化匹配) | ✅ | | 3 | 反复记号 | 3296s (54min) | 实际教学区 | ✅ | | 4 | 高八度 | 3315s (55min) | 实际教学区 | ✅ | | 5 | 低八度记号 | 3369s (56min) | 实际教学区 | ✅ | | 6 | 作业 | 5272s-5981s (709s) | 作业区(下课 5951s + 30s) | ✅ | | ❌ | 掀起你的盖头来 | 已跳过 | 只在导读出现,无实际教学 | ✅ 正确跳过 | **产出**: - v1_final.mp4: 867秒(14.5分钟),69.8 MB - 417 条字幕(original/terms/ai 三个版本) --- ## ⏳ 待处理 | 项目 | 状态 | 说明 | |------|------|------| | 字幕 AI 纠错验证 | 待人工 | 老莫需要看 v1_ai.srt 的纠错效果 | | 视频质量审核 | 待人工 | 老莫需要看 v1_final.mp4 确认标题卡、转场、字幕同步 | --- ## 💡 关键经验 1. **转录数据可复用**:同一节课的 `full_transcript.json` 可以直接复制使用,不需要重新转录 2. **GPU 是必须的**:CPU 转录 100 分钟视频会超时(100+ 分钟),GPU 约 20-30 分钟 3. **术语纠正很重要**:Whisper 会把"附点"识别为"副点"、"延音"识别为"言音"等 4. **教学 vs 导读 vs 回顾**:需要综合多种特征才能准确区分,单一指标不可靠 5. **数字归一化**:Whisper 可能将中文数字识别为阿拉伯数字("十六"→"16"),需要双向匹配 6. **作业结束检测**:老师口语表达多样,必须用模糊匹配覆盖 30+ 种表达 7. **GPU 资源管理**:转录完成后必须显式释放模型,否则显存泄漏