
做视频课程知识化这件事我一直觉得是音视频算法和工程能力的分水岭。市面上讲ASR的、讲LLM的文章都不少但真正把“一段讲课视频丢进去自动产出结构化摘要和知识点大纲”的完整流水线讲清楚的确实不多。这篇文章就是我对这套ASR LLM流水线的一次系统复盘包含选型思路、工程实现细节、成本优化以及我在实际部署中踩过的坑。如果你正在搭建自己的视频课程处理系统或者准备把大模型接入音视频工作流这篇文章应该能帮你省下不少试错时间。这个需求的核心其实很朴素长视频没法直接塞进大模型的上下文窗口纯靠抽帧又丢掉了讲课内容里最关键的口语信息。所以必须先用ASR把语音转成文本再通过合理的分块与摘要策略让LLM在有限上下文内完成高质量的知识提取。下面我会按流水线的每个环节拆解我当时的决策过程和最终落地方案。1. 整体架构与选型思路1.1 为什么非要用ASR LLM组合而不是抽帧或者纯文本视频课程的难点在于信息密度不均匀。老师可能对着PPT讲十分钟也可能在黑板前推导二十分钟公式画面信息其实是碎片的。单纯抽帧喂给多模态模型一方面成本高得吓人另一方面对课堂这类场景来说口语才是信息的主要载体。所以我把流程拆成了两层ASR负责把音频变成可靠的时间戳文本LLM负责在文本基础上做语义理解与知识重构。这套组合的关键在于职责分离。ASR不管语义只管把语音转成字、带上时间戳LLM不管音频只处理文本。这样每个环节都能独立优化、独立替换。比如今天ASR效果不好了我只需要换一个引擎LLM的prompt完全不用动反过来我换了更强的LLMASR转写的结果照样能直接喂进去。这种解耦在工程上的价值比单个模型选得多好更重要。1.2 ASR引擎怎么挑考虑的不只是准确率选ASR引擎时很多人第一反应是比WER词错率。但真实工程里时间戳精度和分片稳定性往往比那零点几个百分点的准确率更关键。因为下游知识点提取时每个知识点都要回链到视频的对应时间位置如果时间戳对不上整个检索体验就崩了。我当时对比了三条路线云端通用ASR比如各家云厂商的录音文件识别。优点是省事缺点是长音频费用高而且音频上传带宽会成为瓶颈。开源本地部署优先考虑了Whisper系列和FunASR的Paraformer。Whisper在中文场景下对口语和专有名词表现不错但显存占用高Paraformer推理快但口音复杂时稳定性略差。端侧/流式与离线混合对录播课来说流式意义不大离线一次性转写更简单可靠。最终我选了Whisper large-v3作为主力引擎原因是它对课堂口语、中英混说和公式朗读的容错率最高同时保留了FunASR的接口作为备用引擎专门处理那些需要高并发低成本转写的内部粗筛任务。选型不是选“最好的”而是选“最不容易出问题的”。1.3 LLM选型背后的成本与延迟博弈大模型选型同样不是无脑上最强。对“自动摘要与知识点提取”这个场景我的实际需求是结构化输出稳定、中文摘要通顺、长文本理解不丢关键信息。市面上闭源API的中文综合能力最省心但一小时视频产生的转写文本常常超过1.5万字成本会直线上升。预算有限或数据敏感时本地部署的开源模型反而更适合做批量任务。我在生产环境里做了双轨方案摘要与知识点生成走高质量模型输入经过精心分块保证输出大纲质量。标题生成、章节润色、低级清洗走低成本快速模型用同一套prompt模板效果差距可接受。这么做的好处很实际大批量历史课程入库时成本能压到纯高配方案的1/3以下。关键是用对位置不是用对最强的模型。2. 流水线核心细节从音频到文本的可靠转写2.1 视频处理第一步音频抽取与降噪ASR对音频采样率和声道有硬性要求。我统一使用ffmpeg抽取音频并做标准化处理采样率固定为16kHz单声道编码用AAC或PCM。为什么是16kHz人类语音的主要频段集中在300Hz到3400Hz16kHz采样率覆盖了足够的频宽又能显著降低数据量和ASR引擎的推理耗时。命令大致这样ffmpeg -i input.mp4 -vn -acodec pcm_s16le -ar 16000 -ac 1 -f wav audio.wav降噪部分我做得比较克制只用了轻量的静音裁剪。因为课堂视频里老师的停顿、学生的提问其实也是语义边界过度降噪会把语言间的顿挫抹平反而影响LLM对章节结构的判断。真正需要降噪的场景是室外拍摄或带有明显风扇声的录屏那时我会接一个RNNoise做预处理其他情况一律跳过。2.2 长音频分片策略既要喂得动ASR又要保住时间戳Whisper这类模型有窗口长度限制直接喂一小时音频必然会截断或丢失信息。我采用了一套两阶段分片方案第一阶段VAD切片用VAD模型检测语音活动点把长音频切成“有声音”的片段并去掉开头和结尾的静音。第二阶段按ASR窗口聚合将VAD切出的小片段按不超过30秒的长度合并成推理单元同时保留每个单元在原始音频中的绝对时间偏移。这里有个重要的细节分片边界尽量避开句子的中间。我会做一个简单的能量检测在切片边界附近找能量最低点作为切割位置这样ASR不容易把半句话丢到两个片段里。实测下来这种“能量感知切分”比固定时长切片能减少约15%的跨片断句错误。2.3 转写结果的时间戳归一化与对齐ASR返回结果通常是分片级的带相对时间戳。我需要在任务层把这些相对时间转换为整条视频的绝对时间。这个逻辑不复杂但非常容易出bug尤其是音频经过裁剪、重采样、分段后偏移量需要逐层累加计算。我会在进入ASR之前给每个分片生成一个global_start_offset转写后对每一条识别结果做偏移补偿写入统一的JSON结构里{ text: 这一节我们讲解矩阵的逆, start: 312.04, end: 315.67, speaker: null }这里start和end的单位是秒精确到小数点后两位。对于课程视频来说这个精度足够支持按句跳转、按知识点定位回放。如果未来要做更细粒度的字级对齐我会再套一层强制对齐工具但那是可选的增强项不影响主链路。2.4 ASR转写的并发与容错设计批量处理上百节课程时ASR的吞吐能力就是瓶颈。我做了三层并发控制音频分片级并发每条视频内的分片可以并行转写因为分片之间相互独立。视频任务级限流同时处理的视频数量控制在GPU显存能承受的范围内避免OOM。失败重试队列单个分片转写失败不影响整条视频失败分片会进入重试队列重试三次仍失败则标记人工待处理。容错这块有个容易忽略的点就是结果校验。ASR偶尔会返回空文本或明显过短的转写结果不能直接放过去。我用一个简单规则过滤分片有效音频时长超过5秒但转写文本少于3个字符判定为异常触发重新转写或告警。3. LLM摘要与知识点提取的实现核心3.1 分块Token预算计算过程与上下文窗口的博弈LLM处理长文本时核心矛盾是上下文窗口有限。按中文估算1小时课程转写文本大约在1.2万到1.8万字之间折合约2万到3万Token。如果直接整段塞进模型轻则丢失中间细节重则直接超出窗口报错。我的分块策略是按语义段落切分以ASR转写结果中的句号、问号和换行为边界切成小段落块。每块控制在3000字以内保证单块内容完整、上下文不散。相邻分块保留200字重叠避免切分点正好落在关键论述的中间导致语义断裂。每块文本送入LLM时我按输入3000字约等于4500 Token的换算关系做预算。加上输出摘要的保留空间单次请求总量控制在6000 Token以内。这个预算既能让摘要保持详细又不会频繁触发截断。这里有一个我反复强调的原则宁可多算一点Token也不能让模型静默截断。截断的摘要看起来完整实际会漏掉后面的知识点而这种错误在自动流水线里极难发现。所以我的prompt里专门加了一句“如果内容超过输出限额优先保留定义、结论和公式说明”。3.2 摘要Prompts设计让模型忠实转述而非自由发挥自动摘要最大的质量风险是模型“编造”。视频里老师没讲的内容LLM可能因为推理补全而写进摘要里。我设计prompt时用了三层约束角色限制要求模型以“课程笔记整理助手”身份工作只整理原文中出现的内容不引入外部知识。输出格式固定用Markdown格式输出只允许出现指定层级的小标题和要点列表不允许自由发挥段落。语义压缩指令要求模型把口语化的表达改写成书面化、精炼的表达但不改变原意。我实际使用的摘要prompt模板大致是请根据以下课程转写文本生成结构化学习笔记。 要求 1. 只整理文本中明确提到的知识点不要做额外扩展 2. 按课程讲解顺序整理不要跳跃 3. 每个知识点用 H3 小标题概括内容控制在3-5条要点 4. 保留关键公式、定义和结论 5. 输出格式为 Markdown。 【课程文本开始】 {chunk_text} 【课程文本结束】不要小看这个模板里“按讲解顺序整理”这句话它非常重要。因为去掉这句话后模型会把内容重新组织成更“漂亮”的结构但这种重组会破坏时间线后续做知识点到视频片段的映射就会乱套。3.3 知识点提取的结构化输出与JSON Schema摘要解决的是“讲了什么”知识点提取解决的是“有哪些值得学”。我让LLM在生成摘要的同时额外返回一个结构化知识点列表每个知识点包含四个字段字段含义示例topic知识点名称矩阵逆的定义summary一句话概括介绍了可逆矩阵及其唯一性start_time对应视频起始时间312.04end_time对应视频结束时间315.67[topic、summary的生成直接依赖ASR转写的时间戳信息我需要先把带了绝对时间戳的转写句段按语义聚合再把聚合后的文本块交给LLM这样才能保证模型输出的起止时间不是编造的。为了保证输出稳定我强烈建议开启函数调用功能并定义好严格的输出JSON格式。如果模型支持JSON Schema约束就直接声明一个KnowledgePoint数组。不支持的话就在prompt里给出一个精确的JSON示例让模型照格式输出。我两者都试过结论是凡是让模型自由输出JSON格式的十个里有三个会飘凡是给了明确Schema的基本都很稳。3.4 长视频跨窗口摘要合并策略做了分块摘要之后随之而来的问题是每个分块的摘要合起来不是完整的大纲。这里我用了两阶段的合并策略第一层合并相邻分块的摘要两两合并重新生成一个更高层的章节摘要。这样每一层都是对下一层的压缩。第二层合并所有章节摘要合入最终大纲生成整节课程的总体摘要与知识点索引。这个分层合并的思路很像地图缩放。拉远看是课程全貌拉近看是章节细节每一层都是可以独立使用的内容。比一次性全量塞入的效果稳定得多而且成本也可控。合并时的prompt核心就一句话“以下是多个章节摘要请合并为一份无重复、无遗漏的完整课程笔记。”4. 调度、成本控制与系统稳定性4.1 异步任务队列与状态机设计视频处理是典型的耗时任务不能做成同步接口。我用消息队列把整个流程拆成了多个任务节点每个节点都有独立的状态pending排队中 → running执行中 → succeeded成功 → failed_retry可重试失败 → dead_letter不可自动恢复每个节点都会把输入和结果写入对象存储节点之间传递的是文件路径而不是大段文本。这样做的好处是任务可以随时从失败节点恢复不用重新跑前序步骤。比如LLM摘要这一步超时了只需要把同一个分块重新投递一次不需要重新转写音频。4.2 Token成本核算与缓存命中率的优化成本控制不是事后算账而是结构设计出来的。我在三个地方做了成本拦截转写文本去重缓存同一门课程、同一节内容入库后再次触发处理时直接读取已有结果不再重复调用ASR和LLM。分块摘要缓存按分块文本的哈希值缓存摘要结果课程微调后未变化的分块直接复用旧摘要。低配模型预筛选对不需要高质量摘要的简单内容比如课程介绍、片头片尾直接用低成本模型处理。我算过一笔账假如一天处理20小时课程按高质量模型API价格摘要成本约占总成本的70%左右。加入分层合并和低配模型分流后整体成本能下降约40%而且质量损失基本感知不到。省下来的预算可以投到更多课程的增量入库上。4.3 监控指标与告警阈值流水线跑起来之后不能等到用户投诉才发现问题。我重点监控五个指标指标告警阈值说明ASR转写失败率 2%超过说明音频分片或引擎出了问题LLM调用超时率 5%排查上下文过长或模型负载问题摘要输出为空率 1%一般由prompt异常或模型输出格式错误导致队列积压数超过阈值持续10分钟说明消费能力跟不上生产速度Token消耗环比上涨30%检查是否有重复调用或缓存失效这些指标归一在同一个看板里任何一个角色的告警能直接定位到具体视频、分片和日志。强烈建议从第一天就开始记录这些数据不然后面排查问题就像在一团乱麻里找线头。5. 常见问题与排查技巧实录5.1 转写文本里的“吞字”导致摘要残缺这个问题特别隐蔽。表面上看ASR转写正常但实际分片边界刚好卡在句子的中间导致句子被切成两半前半句丢了主语后半句丢了谓语。LLM拿到这种残缺文本后摘要会莫名地缺少某个知识点。排查方法是做跨分片n-gram波纹检查相邻分片开头和结尾的字符如果有明显拼接痕迹就重新做一次能量感知切分。我后来把切分逻辑优化为“优先在标点处切割”问题率大幅下降。5.2 知识点时间戳定位不准一开始时间戳偏差严重后来发现是ASR分片的global_start_offset在音频重采样之后被重置了。重采样命令虽然没改变音频内容但部分工具会生成新的时间基导致偏移量错乱。解决方案是严格保持音频处理链路中“时间基一致性”所有分片偏移量都在原始WAV文件的时间轴上计算重采样后只做采样率换算不做时间轴修复。现在GPS级的偏移误差基本能控制在0.5秒以内做视频定位跳转完全够用。5.3 LLM输出JSON时偶尔出现格式漂移某些模型在复杂JSON Schema下偶尔会输出多余的反引号或者缺失括号。这个问题不能靠提高temperature解决核心是两件事开启函数的严格模式让模型在工具参数的约束下生成而不是自由文本生成。增加一层轻量的输出清洗用正则和JSON解析器双重校验解析失败就重试一次。重试时把上一次错误的输出也放进prompt里让模型知道哪里错了。这个方法虽然不“高级”但实际生产中它能挽回掉大量任务。5.4 大批量入库时的并发瓶颈与资源争抢同一时间推入50条视频ASR的GPU显存和LLM的API配额都可能被瞬间打满。我的策略是给每个视频任务设置一个可控的并行度同时做两级令牌桶限流一口吃不成胖子限流反而让整体吞吐更稳。这套流水线我自己陆陆续续迭代了几个月从最初每天手动处理几节课到现在能稳定跑批量入库。核心的收获是工程化的重点不在于选到最聪明的模型而在于把可靠的分片、容错的重试、精确的时间戳、稳定的输出结构这些基础环节做扎实。任何一个环节偷懒下游LLM拿到的就是垃圾输入再强的模型也救不回来。如果你准备在自己的项目里落地这套方案试着从一条视频开始跑通全链路再逐步增加课程量。过程中你会发现很多细节必须结合你自己的数据特点来调。我个人最深的体会是自动摘要这件事模型负责上限工程负责下限。把下限做高模型发挥得才会更稳。