Communitygithub.com

kevin-burns/transcribe-summarize

本地优先的会议转录技能,在您自己的机器上运行 Whisper(支持 macOS、Windows、Linux),过滤掉静音上凭空生成的片段,最终生成事实性会议记录和 PDF。付费 API 为可选,且会在发送前说明发送内容和费用。

transcribe-summarize 是什么?

这是一个面向 Claude Code 和 Codex 等 AI 代理的本地优先会议转录与摘要技能。它的核心工作流完全在用户本地机器上运行:首先使用 ffmpeg 进行音频归一化和静音修剪,然后调用 Whisper 模型(根据平台自动选择 Apple Silicon 上的 mlx-whisper 或其他平台上的 faster-whisper)进行转录。该技能的关键创新在于:它会自动检测并过滤掉 Whisper 在静音段凭空生成的虚构片段(例如在一段沉默中重复同一单词55次),并保留过滤指标以供审计。此外,它支持从平台已有的 VTT/SRT 字幕文件开始(无需重新转录),并提供了严格的会议记录撰写规范(仅记录事实,禁止分析、推断或下一步行动),最终可生成 PDF 文档。对于需要更高质量或有云端偏好的用户,它也支持 Groq、OpenAI、ElevenLabs 和 Gemini 等网络后端,但会严格遵守“先披露、再发送”的安全原则。

兼容平台✓Claude Code✓Codex CLI~Cursor✓Gemini CLI
npx skills add kevin-burns/transcribe-summarize

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

transcribe-summarize 是做什么的?

输入音频,输出转录文本,并可选择输出一组会议记录。默认路径完全在用户机器上运行。

有两件事使该技能与直接调用 Whisper 有所不同,这两件事都源于一天的测量而非设计:

  1. Whisper 会在静音上凭空生成语音,且更大的模型也无济于事。 在一次包含 40 秒接入静音的真实通话中,large-v3 生成了一个 18 秒的片段,将一个单词重复了 55 次。此技能会过滤掉这些片段,并将它们连同拒绝指标保存在一个侧车文件中,以便决策可审计。
  2. 清洁音频胜过选择模型。 归一化和静音修剪将凭空生成的片段从 2 个减少到 0 个,并修复了 large-v3 和 turbo 都译错的一个单词。此功能默认开启。

安装

ffmpeg 和 ffprobe 必须在 PATH 中——macOS 使用 brew install ffmpeg,Windows 使用 winget install Gyan.FFmpeg,Linux 使用 apt install ffmpeg。

其他所有内容均为标准库。后端自带其库,且只需选择其中一个,因此请使用 uv run --with 来挑选:

后端平台--with 规格
mlx-whisper — Apple Silicon 默认仅 macOS arm64'mlx-whisper>=0.4.2'
faster-whisper — 其他平台默认mac / Windows / Linux'faster-whisper>=1.2'
parakeet — 可选Apple Silicon 已验证'parakeet-mlx'
groq、openai、elevenlabs、gemini — 可选,网络任意无需安装

如果缺少库,错误信息会打印确切的命令。不要猜测。

运行

在每一个命令块顶部声明辅助函数——shell 状态不会在工具调用之间持续:

ts() {
  UV="$(command -v uv || ls "$HOME/.local/bin/uv" "$HOME/.cargo/bin/uv" \
        /opt/homebrew/bin/uv /usr/local/bin/uv 2>/dev/null | head -1)"
  "$UV" run --with "$1" --script "$HOME/.claude/skills/transcribe-summarize/scripts/transcribe.py" "${@:2}"
}

# Apple Silicon
ts 'mlx-whisper>=0.4.2' meeting.m4a

# Windows / Linux
ts 'faster-whisper>=1.2' meeting.m4a --backend faster-whisper

# 将解码器偏向已知术语,并修正它总是弄错的一个名称
ts 'mlx-whisper>=0.4.2' meeting.m4a --prompt 'Terragrunt, EKS' --replace 'north wind=Northwind'

请使用绝对路径。相对路径仅在当前工作目录恰好是技能目录时才能正确解析。

输出约定

<输出目录>/
  <文件名>.md                    转录文本,带有 [hh:mm:ss] 锚点
  <文件名>-artifacts/
      <文件名>.srt               字幕
      <文件名>.json              每个片段,包括被抑制的片段
      <文件名>.run.json          后端、模型、修剪偏移、守卫计数

--json 会将这些路径以 JSON 格式打印到标准输出。向用户报告绝对路径;需要用户自己寻找的文件不是可交付成果。

时间戳基于原始录音的时钟。 修剪会在解码期间缩短音频,并且在写入任何内容之前会将偏移映射回去,因此 .srt 与用户自己的文件对齐。

网络规则——明确说明,然后遵守

只有当用户在本次调用中明确要求时,才能访问网络后端。绝不能作为回退,绝不能在本地上游失败后使用,绝不能因为这样更快。--backend auto 无法访问网络后端;不存在这样的代码路径。

在发送任何内容之前,工具会打印提供商、端点、文件大小、时长和预估费用,然后等待。使用 --dry-run 在提交前向用户展示该信息块。在非交互式会话中,未经确认的上传将被拒绝。

密钥来自环境变量,每个后端一个,且不能来自其他地方。绝不要接受其作为标志,绝不要回显它,绝不要将其写入文件。

后端密钥模型费用($/小时)说话人分离
groqGROQ_API_KEYwhisper-large-v3-turbo0.04否
groqGROQ_API_KEYwhisper-large-v3 (默认)0.111否
elevenlabsELEVENLABS_API_KEYscribe_v20.22最多 32 人
geminiGEMINI_API_KEYgemini-3.5-transcribe0.306最多 3 人
openaiOPENAI_API_KEYwhisper-10.36否

--backend gemini —— 运行前需要说明的两件事

ts '' /path/to/audio.m4a --backend gemini --dry-run     # 展示信息块,不发送任何内容
ts '' /path/to/audio.m4a --backend gemini               # 然后运行

无需安装——它使用的是标准库 http.client,因此 --with 参数为空。在运行之前,有两件事需要告知用户,因为它们在命令行中不可见:

  • 音频会上传两次。 Gemini 没有单次请求的端点:文件首先上传到 Google 的 Files API,然后转录请求仅携带返回的 URI。Google 会保留该上传 48 小时。 披露块会打印此信息;不要跳过它。
  • 上限是 30 分钟,而不是 Google 自己页面宣传的 1 小时。1 小时仅适用于不包含词级时间戳或说话人分离的情况,而后端始终会请求这两者——没有词级偏移就无法映射回用户的文件时钟。更长的录音会在上传开始前被拒绝。

不要提供 Gemini 的 smart 模式。它没有被实现,是有意为之。 如果用户要求使用它——这是该模型的头条功能,确实可以移除填充词并重写口语中的自我修正——那么回答就是 Google 的 API 会拒绝同时返回词级时间戳和说话人标签,因此它会产生没有 .srt 和没有说话人信息的输出,并且会改变原词。填充词移除改在笔记步骤中进行,在那里它被标记为摘要并接受检查。参见 references/backends.md。

质量守卫

默认开启。被抑制的片段会保留在 .json 中,并附带原因和指标;它们会从 .md 和 .srt 中删除。--guard mark 会将其内联标记并保留,--guard off 会禁用它。

它并非适用于每个后端。 强规则读取的是 Whisper 解码器指标。faster-whisper、Groq 和 OpenAI 都返回所有三个指标,因此守卫使用相同的代码。Parakeet、ElevenLabs 和 Gemini 不返回任何指标,因此仅回退到与后端无关的规则——工具会在运行时说明。参见 references/backends.md。

如果用户询问为什么缺少一段转录文本,请阅读 .json:答案就在那里,并附有导致该结果的数字。

从已有的转录文本开始

大多数会议已经被平台转录过,而且常常根本没有音频文件。 Teams 和 Zoom 会提供 .vtt(有时是 .srt)文件,但作为散文阅读体验极差:每隔几秒就有提示,句子在提示之间被截断,说话人的名字在每一行重复,时间码遍布全文。

这是一个受支持的入口点。不会重新进行转录——音频路径完全被跳过:

"$HOME/.claude/skills/transcribe-summarize/scripts/normalise_transcript.py" meeting.vtt
# -> meeting.md,提示被重新合并成段落,说话人归属,每个段落一个锚点

然后就像转录文本是在此处解码的一样继续:阅读它,询问会议事实,并在 references/notes-register.md 下撰写笔记。

--no-speakers 会在来源标签错误或不必要时删除归属。

它只是一个格式转换,仅此而已。 不改变任何词语,不删除填充词,不修正语法。决定说的是什么属于摘要步骤,并且它有自己的一套规则。如果用户给您一份杂乱的转录文本并要求“整理”,请在此处标准化格式,然后在笔记中进行判断——不要在应该记录他人话语的文档中悄悄重写他们的词句。

说话人信息会保留在转录文本中,但禁止出现在笔记中,这并不矛盾:转录文本是一个工作产物,可能带有来源信息;而笔记是一份记录,则不能。notes_check.py 仍然会拒绝笔记中的原始 Speaker 2 标签。

如果有人将 .vtt 文件传给 transcribe.py,它会停止运行并指向此处,而不是尝试解码它。

视频文件

.mp4 实际上是 Teams 或 Zoom 下载的内容,并且可以直接处理——无需先提取音频。已验证支持的格式:.mp4、.mov、.mkv、.webm,以及所有音频容器(.m4a、.mp3、.wav、.flac、.opus)。ffmpeg 会提取音轨;视频被忽略且永远不会上传。

一个没有音轨的文件——例如静音屏幕录制——会在提供任何网络后端之前被拒绝。该检查会在出口网关之前运行,专门确保不会上传和计费静默视频。

审查是一个步骤,而非可选项

转录输出是草稿。请明确说明,并让用户检查它。

机器转录会以没有阈值能捕捉的方式自信地出错。在本项目的真实音频上测量:large-v3 默默地遗漏了一个说话人对一个数字的口头更正,每个后端都将 "board pack" 听成了 "board packs up"。两者读起来都非常流畅。质量守卫能捕捉静音上的虚构内容;但它无法捕捉看似合理的误听,您通过阅读输出也无法捕捉到。

因此工作流是转录 → 用户检查 → 然后笔记:

  1. 转录文本 .md 以 "Worth checking" 部分结尾——按最可能值得回听的程度排序的时间戳(最不自信的片段,任何包含数字的片段)。明确指向该部分给用户。
  2. 姓名、数字和日期是需要验证的内容。 它们是最先退化的,并且在记录中出错代价最高。
  3. 修正内容手动写入 .md,或使用 --replace 重新运行。然后从它重新生成任何构建产物。

永远不要将转录文本或笔记作为已验证的内容呈现给您没有听过音频;用户听过。说出解码了什么,说出哪里最不确定,然后让他们确认。

编辑和重新生成

.md 是源文件。编辑它,然后重新构建 PDF——无需重新转录:

"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notes.md
"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py"  notes.md

在每次编辑后重新运行检查器。手动编辑正是最容易引入来源泄露或推断的“下一步行动”行的时候。

撰写笔记

当用户想要会议记录而不是转录文本时,您来撰写——阅读转录文本并自己生成文档。有一个 scripts/summarize.py 回退方案,它会调用一个网络模型,但这仅用于在 Claude Code 会话之外运行此管道;在会话内部,它只是一个毫无意义的上传。

先询问——三件音频无法告诉您的事情

这里几乎没有一个后端能识别说话人身份。 每个 Whisper 引擎和 Parakeet 都不返回任何说话人字段。两个例外:--backend elevenlabs (Scribe) 最多分离 32 个说话人,--backend gemini 最多 3 个。两者都返回位置标签而非姓名——分别是 speaker_0 和 spk:0,这两个格式是 2026-09-04 和 2026-09-06 从实时响应中测量得到的——并且登记册禁止在笔记文档中出现原始标签,无论哪种格式。因此它们能告诉您有多少人说了话以及哪些行属于一起;但仍然无法告诉您是谁。归属仍然来自用户;说话人分离只是使从转录文本(而非记忆)中进行映射成为可能。

在撰写笔记之前,请询问:

  1. 谁出席了会议。 没有姓名,则撰写无人归属的笔记——用非人称的“据称,……”。绝不虚构姓名、角色或位置标签。如果转录文本中能明显看出是谁在说话(某人被点名),您可以进行归属;歧义意味著不添加姓名。
  2. 会议日期。 今天的日期是默认值,而非事实。音频通常会在对话几天后才被整理成文,事实记录上的错误日期比没有日期更糟糕。请确认它。
  3. 会议主题——以参与者自己的术语表述的一个标题。

在一个消息中询问所有三项,而不是逐一询问,并提供今天的日期作为默认值,这样用户只需更正。如果用户拒绝提供与会者,则无归属地进行,并说明您已如此操作。

在写下一行之前,阅读 references/notes-register.md。 它很短,而且是完整的规范。它并非风格建议;其中的规则是通过从一份真实文档中手工删除部分内容而学到的,原因是法律而非美学。

它所开启的两条规则:

  • 仅限事实摘要。 说了什么。没有分析,没有暗示,没有“下一步行动”,没有“未覆盖的内容”,没有填补空白。
  • 读起来像是一位在场的人撰写的协议记录。 绝不提及录音、转录文本、音频、模型或任何解码器产物——没有时间码,没有 Speaker 2 标签。

然后每次都要检查——登记册并非自我强制执行,要求进行摘要的模型会在没有提示的情况下添加分析:

"$HOME/.claude/skills/transcribe-summarize/scripts/notes_check.py" notes.md

退出码 0 表示干净;退出码 1 会针对每个违规列出 文件:行号。在将文档交出之前修复它们。也需对 PDF 运行它——仅存在于渲染文件中的泄露仍然算数。

欢迎提供指导,但应在笔记之后且在文档之外进行。 在对话中提供您的解读,或将其写入单独的文件。绝不要将其附加到文档中。

PDF 生成

"$HOME/.claude/skills/transcribe-summarize/scripts/render_pdf.py" notes.md

Headless Chrome、Chromium 或 Edge,自动发现。如果未安装任何浏览器,它会写入 HTML,说明情况,并以退出码 0 退出——HTML 仍然是一个可交付产物。

来源

本地解码使用 mlx-whisper (MLX) 或 faster-whisper (CTranslate2);音频准备使用 ffmpeg。网络后端通过标准库 urllib 直接调用 Groq 和 OpenAI 的转录端点——无需供应商 SDK。

将来源信息排除在笔记文档之外,改变的是文档的内容,而非发生的事实。未经同意录制通话在许多司法管辖区是刑事犯罪;该技能不会帮助做这件事。您的数据,您的机器,您的法律。

相关技能