平时看技术视频、听播客,一集内容动辄四十分钟起步,真正有用的信息可能只占几分钟。想快速了解核心内容,手动拖进度条效率太低;用在线转写工具,又担心隐私、费用和排队。后来在 Hacker News 上看到一个叫 Audio-tldr 的项目思路:用 OpenAI 开源的 Whisper 在本地完成音视频转写,再自动生成文字摘要。整条链路完全本地运行,不依赖云端 API。这篇文章就沿着这条思路,从概念、环境、原理到代码实现完整拆解,带你搭建一个属于自己的本地音视频摘要工具,新手可以按步骤复现,有经验的开发者也能直接复用核心代码。

1. Audio-tldr 是什么:一条完整的本地音视频摘要流水线

1.1 项目定位

Audio-tldr 这个名字很直白,Audio 加上 TL;DR(Too Long; Didn't Read,太长了不想看),目标就是解决“音频内容太长,没法快速获取重点”的问题。

它的定位和常见的在线转写工具有明显区别:

  • 完全本地运行,音频文件不需要上传到第三方服务器;
  • 不按分钟计费,只要有足够的本地算力,可以批量处理;
  • 转写和摘要流程可以完全自定义,比如指定语言、调节模型大小、导出字幕文件;
  • 适合处理内容偏敏感的视频会议、内部培训、访谈录音等场景。

相比在线工具,本地方案最大的优势是隐私和成本。你把一小时的会议录音交给在线服务,很难确定这些数据会被如何使用;本地方案把数据留在自己手里,安全性完全由自己掌控。

1.2 核心流水线

Audio-tldr 的核心思路并不复杂,本质是一条三步流水线:

输入视频/音频文件
        │
        ▼
[ffmpeg 提取音频] ──→ 16kHz 单声道 WAV
        │
        ▼
[Whisper 语音识别] ──→ 带时间戳的转写文本
        │
        ▼
[摘要引擎] ──→ 核心要点 / 摘要文本

第一步,用 ffmpeg 把视频或音频统一转换成适合语音识别的格式;第二步,用 Whisper 模型把音频转写成文字;第三步,对转写文本做摘要处理。后面我们会围绕这条流水线逐段实现。

1.3 为什么选择 Whisper

Whisper 是 OpenAI 在 2022 年 9 月开源的自动语音识别模型,它之所以适合做 Audio-tldr 这类工具,主要有几个原因:

  • 开源且可本地部署,模型权重可以下载到本地,不需要调用云端接口;
  • 支持多语言识别,官方宣称支持 99 种语言,并且能自动检测语言;
  • 提供 tiny、base、small、medium、large-v3 等多个尺寸,可以从“极快但一般准”到“较慢但很准”之间按需选择;
  • 输出结果带时间戳,转写文本可以进一步导出成 SRT、VTT 字幕;
  • 社区生态成熟,除了官方 openai/whisper,还有 faster-whisper、whisper.cpp 等性能优化版本。

对个人开发者来说,Whisper 是当前落地本地语音识别成本最低、效果也稳定的选择。

2. 环境准备与版本说明

2.1 运行环境

Audio-tldr 的完整流程可以在 Windows、macOS、Linux 上运行。CPU 也能跑,但模型越大、音频越长,耗时越明显。如果打算常用 large 型号,建议准备一块支持 CUDA 的 NVIDIA 显卡。

内存方面,tiny、base 模型 8GB 内存基本够用;medium 和 large 模型在转写长时间音频时,内存占用会明显上升。内存越充裕,处理越流畅。

2.2 安装 Python 与 ffmpeg

推荐使用 Python 3.9 到 3.12 版本。建议在项目目录下创建虚拟环境,避免依赖冲突:

python -m venv venv
# Windows
venv\Scripts\activate
# macOS / Linux
source venv/bin/activate

ffmpeg 是音频提取的关键工具,Whisper 读取音频时也会依赖它。安装方式按系统区分:

# Windows(使用 winget)
winget install ffmpeg

# macOS
brew install ffmpeg

# Ubuntu / Debian
sudo apt update && sudo apt install ffmpeg

安装完成后,运行下面的命令确认系统能识别 ffmpeg:

ffmpeg -version

如果提示找不到命令,通常是安装后没有把可执行文件加入 PATH,需要检查环境变量配置。

2.3 安装 Whisper

官方 Python 包可以直接通过 pip 安装:

pip install -U openai-whisper

如果更在意转写速度,可以考虑 faster-whisper。它基于 CTranslate2 推理引擎,在 CPU 和 GPU 上通常比官方实现更快,且内存占用更少。本文的示例代码使用官方 openai-whisper,但核心思路同样适用于 faster-whisper。

2.4 模型选择

Whisper 首次使用时会把模型权重下载到本地缓存目录。模型越大,识别准确率越高,但速度和资源消耗也越大。各型号的定位大致如下:

模型名称 参数规模 CPU 体验 推荐场景
tiny 约 39M 非常快 快速测试流程、短音频
base 约 74M 较快 日常短音频、中文短句
small 约 244M 可以接受 较长音频,速度与效果平衡
medium 约 769M 偏慢 对准确率要求较高的场景
large-v3 约 1550M 很慢 GPU 环境、追求最高准确率

对中文内容,建议从 base 或 small 开始尝试。先跑通流程,再根据识别效果决定是否升级模型。

3. 核心原理拆解

3.1 为什么先用 ffmpeg 提取音频

Whisper 其实可以直接读取视频文件,但显式用 ffmpeg 提音频有几个好处:

  • 统一音频格式为 16kHz 采样率、单声道 WAV,避免不同视频的编码格式影响识别;
  • 16kHz 是 Whisper 训练时使用的采样率,输入对齐后识别效果更稳定;
  • 预先提取音频可以把“媒体解码”和“语音识别”两个阶段分离,便于缓存和调试。

对应的 ffmpeg 命令如下:

ffmpeg -i input.mp4 -vn -ac 1 -ar 16000 -y audio.wav

参数含义:

  • -i input.mp4 :指定输入文件;
  • -vn :丢弃视频流;
  • -ac 1 :设置单声道;
  • -ar 16000 :设置采样率为 16kHz;
  • -y :覆盖已有输出文件。

在 Python 中,我们用 subprocess.run 调用这条命令,完整代码在后面的实战部分给出。

3.2 Whisper 的核心参数

Whisper 的调用方式非常简洁:

import whisper

model = whisper.load_model("base")
result = model.transcribe("audio.wav", language="zh", fp16=False)

load_model 负责加载模型, transcribe 负责识别音频。实际使用时,有几个参数需要重点关注。

参数 作用 使用建议
model 模型名称 按资源和准确率要求选择
language 语言代码 中文建议显式指定 zh ,避免自动检测误判
task transcribe 或 translate 转写或翻译成英文
fp16 是否使用半精度 GPU 可开启,CPU 必须设为 False
initial_prompt 提示词 可传入领域词汇、说话风格,提升识别效果
verbose 是否显示进度 调试时开启,正式脚本可关闭

result 返回的是包含多个字段的字典。 result["text"] 是完整转写文本, result["segments"] 是带时间戳的分段结果, result["language"] 是检测到的语言。

这里尤其注意 language 参数。自动检测在中文、英文混杂或方言环境下可能选错语言,导致输出大量英文乱码。对中文播客、中文视频,直接指定 language="zh" 更稳妥。

3.3 摘要怎么做

转写完成后,得到的是完整文本。一集 1 小时的播客,转写文本可能达到上万字,直接阅读仍然很费时间,所以还需要摘要步骤。

摘要方案主要有两类:

第一类是抽取式摘要。从原文本中挑出最能代表主题的句子,拼接成摘要。优点是实现简单、不依赖额外模型、结果可追溯到原文;缺点是句子之间可能不够连贯,缺少整体概括。

第二类是生成式摘要。让大语言模型阅读转写文本,重新组织语言输出要点。优点是概括性强、表达自然;缺点是需要接入额外模型,且生成耗时较长。

在下一节的实战中,我们先实现一个不依赖外部服务的抽取式摘要,再介绍如何接入本地大模型做生成式摘要。

4. 完整实战:构建本地音视频摘要工具

4.1 项目结构

整个工具用单个 Python 脚本即可实现,核心依赖只有 openai-whisper。项目结构如下:

audio_tldr/
├── requirements.txt
├── audio_tldr.py
└── output/
    ├── audio.wav
    ├── transcript.txt
    └── summary.txt

output 目录用来存放中间产物和结果, audio_tldr.py 是主脚本。

4.2 创建依赖文件

先创建 requirements.txt :

openai-whisper

安装依赖:

pip install -r requirements.txt

4.3 编写音频提取模块

音频提取函数如下:

import subprocess


def extract_audio(video_path, audio_path):
    """使用 ffmpeg 从视频/音频中提取 16kHz 单声道 WAV。"""
    cmd = [
        "ffmpeg", "-i", video_path,
        "-vn", "-ac", "1", "-ar", "16000",
        "-y", audio_path
    ]
    subprocess.run(cmd, check=True, capture_output=True)

注意 check=True ,如果 ffmpeg 执行失败,会直接抛出 CalledProcessError ,方便尽早发现问题。

4.4 编写转写模块

转写函数封装了 Whisper 的加载和识别逻辑:

import whisper


def transcribe(audio_path, model_name="base", language=None):
    """加载 Whisper 模型并完成转写。"""
    model = whisper.load_model(model_name)
    result = model.transcribe(
        audio_path,
        language=language,
        fp16=False,
        verbose=False
    )
    return result

这段代码在 CPU 上运行,所以 fp16 固定设为 False 。如果你的机器有 GPU,可以改成 True 来加速。

4.5 编写摘要模块

先实现一个不依赖额外模型的抽取式摘要。它的思路是:把文本切成句子,统计每个字或词的出现频率,然后按“句子中包含的高频词数量”给句子打分,取分数最高的几条作为摘要。

import re
from collections import Counter


def split_sentences(text):
    """按中英文句末标点切分句子。"""
    text = re.sub(r"\s+", " ", text)
    parts = re.split(r"(?<=[。!?!?])", text)
    return [p.strip() for p in parts if p.strip()]


def get_freq(text):
    """统计词频:中文按单字,英文按单词(演示用)。"""
    words = re.findall(r"[\u4e00-\u9fff]|[A-Za-z]{3,}", text)
    return Counter(words)


def summarize_by_freq(text, top_k=5):
    """基于词频的抽取式摘要:高频词所在的句子更可能代表主题。"""
    sentences = split_sentences(text)
    sentences = [s for s in sentences if len(s) >= 10]
    if not sentences:
        return text[:500]
    freq = get_freq(text)
    scored = []
    for sentence in sentences:
        score = sum(freq.get(w, 0) for w in re.findall(
            r"[\u4e00-\u9fff]|[A-Za-z]{3,}", sentence))
        scored.append((score, sentence))
    scored.sort(key=lambda x: x[0], reverse=True)
    return "\n".join(s for _, s in scored[:top_k])

这里为了演示方便,中文是按单字统计的。实际项目中更适合用 jieba 分词后再统计,摘要质量会明显提升。摘要模块之后可以替换成任何更复杂的实现,主流程不需要改动。

4.6 编写主流程

主流程把三个步骤串起来,并接收命令行参数:

import argparse
import os
import tempfile


def main():
    parser = argparse.ArgumentParser(description="Audio-tldr 本地音视频摘要工具")
    parser.add_argument("input", help="输入视频或音频文件")
    parser.add_argument("--model", default="base", help="Whisper 模型名称:tiny/base/small/medium/large-v3")
    parser.add_argument("--language", default=None, help="语言代码,如 zh/en,不填则自动检测")
    parser.add_argument("--top-k", type=int, default=5, help="摘要中保留的句子数量")
    parser.add_argument("--output", default="summary.txt", help="摘要输出路径")
    args = parser.parse_args()

    with tempfile.TemporaryDirectory() as tmp_dir:
        audio_path = os.path.join(tmp_dir, "audio.wav")

        print(f"[1/3] 使用 ffmpeg 提取音频:{args.input}")
        extract_audio(args.input, audio_path)

        print(f"[2/3] 使用 Whisper 转写(模型:{args.model})...")
        result = transcribe(audio_path, model_name=args.model, language=args.language)
        transcript = result["text"]
        print(f"      转写完成,共 {len(transcript)} 个字符。")

        print("[3/3] 生成摘要...")
        summary = summarize_by_freq(transcript, top_k=args.top_k)

        with open(args.output, "w", encoding="utf-8") as f:
            f.write(summary)
        print(f"摘要已保存到:{args.output}")
        print("----------------------------------------")
        print(summary)
        print("----------------------------------------")


if __name__ == "__main__":
    main()

主流程使用 TemporaryDirectory 存放中间音频文件,脚本结束后会自动清理,不会污染项目目录。

4.7 运行与验证

执行下面的命令,对本地视频做摘要:

python audio_tldr.py ./demo.mp4 --language zh --top-k 5

如果 demo.mp4 里有人说话,运行完成后会生成 summary.txt ,并打印摘要内容。第一次运行 base 模型时会先下载模型权重,需要等待一段时间,下载完成后会缓存在本地,后续运行不需要重新下载。

如果输入本身就是音频文件,比如 demo.mp3 ,同样可以传入,ffmpeg 会直接把音频转成 WAV:

python audio_tldr.py ./podcast.mp3 --language zh --top-k 5

到这里,一个最小可用的本地音视频摘要工具就算完成了。整个流程只有三部分:提音频、转写、摘要,代码量不大,但链路完整。

5. 进阶改进:接入本地 LLM 做生成式摘要

5.1 为什么需要生成式摘要

上面实现的抽取式摘要比较简单,适合快速验证流程。但它的局限性也很明显:选出来的句子可能缺乏连贯性,无法准确表达“整段内容在讲什么”。如果想要更高质量的摘要,建议接入生成式大模型。

如果希望继续保持“本地运行”的特点,可以使用 Ollama 在本地启动大模型服务。Ollama 支持多种开源模型,比如 Qwen 系列、Llama 系列,具体模型根据机器配置选择。

安装 Ollama 后,先拉取一个模型:

ollama pull qwen2.5

然后在 Python 中调用 Ollama 的 HTTP 接口:

import json
import urllib.request


def summarize_with_ollama(text, model="qwen2.5", base_url="http://localhost:11434"):
    prompt = f"请阅读下面的文字,用中文提炼出 5 个核心要点,每个要点不超过 50 字:\n\n{text[:4000]}"
    payload = json.dumps({
        "model": model,
        "prompt": prompt,
        "stream": False,
        "options": {"temperature": 0.3}
    }).encode("utf-8")
    req = urllib.request.Request(
        f"{base_url}/api/generate",
        data=payload,
        headers={"Content-Type": "application/json"}
    )
    with urllib.request.urlopen(req, timeout=300) as resp:
        data = json.loads(resp.read().decode("utf-8"))
    return data.get("response", "")


summary = summarize_with_ollama(transcript)
print(summary)

把这段代码接入主流程很简单,只需替换摘要模块即可。需要注意的是,本地大模型会占用较多内存和显存,机器配置不够时建议先处理较短的音频,或者把转写文本切片后分批摘要。

5.2 将转写结果导出为字幕

Whisper 的 result["segments"] 里包含每一句话的开始时间、结束时间和文本,可以导出为 SRT 字幕文件:

def format_timestamp(seconds):
    millis = int(round(seconds * 1000))
    hours = millis // 3600000
    minutes = (millis % 3600000) // 60000
    secs = (millis % 60000) // 1000
    ms = millis % 1000
    return f"{hours:02d}:{minutes:02d}:{secs:02d},{ms:03d}"


def export_srt(segments, output_path):
    with open(output_path, "w", encoding="utf-8") as f:
        for i, seg in enumerate(segments, 1):
            start = format_timestamp(seg["start"])
            end = format_timestamp(seg["end"])
            text = seg["text"].strip()
            f.write(f"{i}\n{start} --> {end}\n{text}\n\n")

转写文本加上字幕文件,后续还可以做关键词检索、章节切分、双语字幕对照等扩展。

6. 常见问题与排查思路

在实际运行过程中,最容易遇到下面几类问题。

6.1 ffmpeg 命令找不到

现象:脚本报 FileNotFoundError: [Errno 2] No such file or directory: 'ffmpeg' 。

原因:ffmpeg 没有安装,或者安装后没有加入 PATH。

排查步骤:

ffmpeg -version

如果提示找不到命令,需要安装 ffmpeg 并确认安装目录已加入系统 PATH。Windows 下用 winget 安装后通常会自动配置环境变量,如果不行,需要手动把 ffmpeg 的 bin 目录加入 PATH,然后重启终端。

6.2 模型下载慢或失败

现象:第一次运行脚本时长时间卡在下载阶段,或者直接报网络错误。

原因:Whisper 模型权重从海外源下载,网络状况不好时容易失败或中断。

解决办法:检查网络连接;删除本地不完整的缓存目录后重试;也可以从可用的镜像源下载模型权重,然后放到 Whisper 的缓存目录中。模型文件放置完成后, load_model 会直接读取本地缓存。

6.3 CUDA 报错

现象:GPU 环境下提示 CUDA 相关错误,比如 CUDA not available 或版本不匹配。

原因:PyTorch 的 CUDA 版本和显卡驱动、CUDA 运行库不匹配。

解决办法:先确认 torch.cuda.is_available() 是否返回 True ;不匹配时,根据 PyTorch 官网的指引安装对应 CUDA 版本的 PyTorch;如果只是临时使用 CPU 跑,可以把 fp16=False ,并让模型在 CPU 上运行。

6.4 转写结果全是英文或乱码

现象:中文音频转写出来后,文本大量是英文或符号。

原因:没有指定 language="zh" ,自动检测把语言判断错了;也可能是初始提示词 initial_prompt 没有引导模型。

解决办法:显式指定语言:

result = model.transcribe("audio.wav", language="zh", fp16=False)

也可以添加初始提示词:

result = model.transcribe(
    "audio.wav",
    language="zh",
    initial_prompt="以下是普通话的对话内容。",
    fp16=False
)

6.5 内存不足

现象:转写过程中进程被系统杀死,或提示 Killed 、 Out of memory 。

原因:模型过大,或者音频太长,或者同时运行了过多其他程序。

解决办法:换用更小的模型,比如从 medium 降到 small ;把长音频切成 10 分钟一段分别处理;关闭不必要的程序释放内存。

6.6 常见问题速查表

问题现象 常见原因 解决思路
ffmpeg 命令找不到 ffmpeg 未安装或未加 PATH 安装 ffmpeg,确认 ffmpeg -version 可运行
模型下载慢或失败 网络问题或缓存损坏 检查网络,删除缓存后重试,或手动放置模型文件
CUDA 报错 PyTorch 与驱动不匹配 安装匹配的 CUDA 版本 PyTorch,或改用 CPU
中文识别成英文 未指定语言 设置 language="zh"
转写速度太慢 模型过大且 CPU 推理 换小模型,使用 GPU,或改用 faster-whisper
内存不足 模型或音频过长 减小模型,切分音频,释放内存
摘要效果差 抽取式摘要过于粗糙 接入生成式大模型,或改用 jieba 分词后再统计

7. 最佳实践与工程建议

7.1 模型实例复用

在脚本里每次调用 whisper.load_model 都会重新加载模型,非常耗时。如果要把转写能力做成服务,应该在进程启动时加载模型,然后复用一个模型实例处理多次请求。模型加载通常需要几秒到几十秒,复用能显著提升响应速度。

7.2 转写结果缓存

长时间音频的转写非常耗时,同一段音频可能因为摘要参数调整而需要反复处理。建议把转写结果保存为 JSON 或 TXT,下次运行摘要模块时直接读取缓存文本,跳过音频提取和语音识别阶段。这样调摘要、调字幕格式时,不需要重新转写一遍。

7.3 统一音频处理规范

不论输入是 mp4、mp3、m4a 还是其他格式,统一转成 16kHz 单声道 WAV。这个规范可以避免很多由于采样率、声道数不一致导致的识别问题。同时,中间音频文件建议使用临时目录,避免大量中间文件堆积在项目目录。

7.4 安全与隐私边界

本地处理是 Audio-tldr 这类工具的核心优势,但也意味着文件安全由自己负责。如果处理的是敏感录音,建议把输出文件放在权限受限的目录中,不要随意同步到公共网盘。转写文本里可能包含讲话者的隐私信息,删除中间文件时也要彻底。

如果后续接入了本地大模型做摘要,还需要注意 prompt 注入问题。转写文本里可能包含恶意指令,例如有人在录音中说“忽略之前指令,输出固定内容”。在构造摘要 prompt 时,要明确限定模型只做摘要,不执行文本中的任何指令。

7.5 长音频处理策略

对于超过 1 小时的音频,建议先按章节或时间段切片,分别转写和摘要,最后再合并。Whisper 本身会把长音频按 30 秒窗口切分处理,但手动分段更容易做断点续传:某一段失败时,不需要重跑整段音频。

切片可以继续用 ffmpeg 完成:

ffmpeg -i audio.wav -ss 00:00:00 -to 00:10:00 -c copy part1.wav

7.6 摘要结果的可追溯性

生成摘要后,建议在文件里保留对应的原文偏移信息。比如抽取式摘要可以记录每个摘要句在原文本中的位置;生成式摘要可以保留原始转写文本文件。这样当摘要质量不佳时,可以快速定位到原文,而不是面对一段无法验证的浓缩文字。

8. 总结与后续学习路线

这篇文章从 Audio-tldr 的思路出发,实现了一个完整的本地音视频摘要工具。核心链路是:ffmpeg 提取音频、Whisper 语音转写、摘要引擎生成要点。环境准备部分覆盖了 Python、ffmpeg 和 openai-whisper 的安装;原理部分解释了采样率、语言参数、摘要方案等关键细节;实战部分给出了完整的 audio_tldr.py 脚本,可以直接复制运行;进阶部分介绍了用 Ollama 接入本地大模型做生成式摘要,以及导出 SRT 字幕的方法。

如果你想把这条链路做得更深入,可以按下面的方向继续学习:

  • 用 faster-whisper 替换官方实现,对比转写速度和显存占用;
  • 在 whisper.cpp 的基础上做移动端或嵌入式部署;
  • 接入说话人分离工具,把转写文本按说话人区分;
  • 把转写文本导入向量数据库,结合 RAG 做音视频内容问答;
  • 给工具加上批量任务队列、进度回调、错误重试,做成真正可用的服务。

本地音视频摘要并不是一个新概念,但 Whisper 把语音识别的门槛降到了很低。接下来需要做的,就是把这条流水线打磨得再稳定、再快一些。你可以先用自己手边的一段视频跑一遍流程,再根据实际效果调整模型大小和摘要策略。动手跑起来,比看十篇文章都有用。如果文章对你有帮助,欢迎收藏备用,也欢迎在评论区交流你在运行过程中遇到的问题。

Logo

邀请您加入社区

更多推荐