首页
/ graphify 视频/音频转写指南:Whisper 提示词策略与 transcribe 管线全解析

graphify 视频/音频转写指南:Whisper 提示词策略与 transcribe 管线全解析

2026-09-06 17:50:22作者:乔或婵

在 graphify 中,视频与音频文件无法像代码或文档那样直接参与抽取——AST 解析器读不了二进制音视频。为此,graphify 的构建流程在 detect 之后插入了一个独立的 Step 2.5:先用 faster-whisper 把音视频转写成文本,再把转写稿当作普通文档文件进入后续语义抽取。本文以 transcribe.md 参考文档为主体,完整讲解"何时触发转写、如何自拟 Whisper 领域提示词、如何导出环境变量并执行批量转写"的实战流程,并结合 transcribe.py 源码揭示缓存、模型配置与容错机制等文档背后的一手实现细节。读完本文,你将能在含音视频的语料库上正确配置并运行 graphify 转写管线,并理解每一步设计的原因。

触发时机:只有 detect 报告了 video 文件才读取本参考

参考文档开篇即明确了加载条件:

Load this only when detect reported one or more video files. A corpus with no video never reads this.

也就是说,Step 2.5 是条件步骤detect 返回的 video 类别文件数为零时,整个转写步骤被完全跳过。这由 skill-pi.md 主流程中的 Step 2.5 章节显式引用该参考文档来触发(references/transcribe.md 仅在语料含视频/音频时被读取)。

从源码结构看,video 类别的判定依据是 detect.py 中的扩展名白名单:

VIDEO_EXTENSIONS = {'.mp4', '.mov', '.webm', '.mkv', '.avi', '.m4v', '.mp3', '.wav', '.m4a', '.ogg'}

_classify 逻辑(detect.py)将命中该集合的文件归类为 FileType.VIDEO,随后写入 graphify-out/.graphify_detect.jsonfiles.video 键——这正是转写步骤读取的输入来源。值得注意的是,音频扩展名(.mp3/.wav/.m4a/.ogg)与视频扩展名共用同一分类,因此纯音频播客、录音也能走同一条转写路径。

策略核心:用 god nodes 自拟一句话领域提示词,而非额外 API 调用

参考文档给出的策略(Strategy)是全文最有设计感的部分:

Read the god nodes from graphify-out/.graphify_detect.json (or the analysis file if it exists from a previous run). You are already a language model — write a one-sentence domain hint yourself from those labels. Then pass it to Whisper as the initial prompt. No separate API call needed.

其要点是:

  1. 数据来源:读取 detect 输出或上一次运行留下的分析文件中的 god node 标签;
  2. 提示词生成:正在执行管线的编码代理本身就是语言模型,由它根据 god node 标签直接"脑内"写出一句话领域提示(domain hint),不需要为生成提示词单独调用一次 LLM API;
  3. 兜底规则:如果语料库只有视频文件、没有任何其他文档/代码,则没有 god nodes 可用,改用通用兜底提示词 "Use proper punctuation and paragraph breaks."

Step 1 —— 亲手写出 Whisper 提示词

文档给出了两个标签到提示词的映射示例,可直接复制改写:

  • 标签 transformer, attention, encoder, decoder"Machine learning research on transformer architectures and attention mechanisms. Use proper punctuation and paragraph breaks."
  • 标签 kubernetes, deployment, pod, helm"DevOps discussion about Kubernetes deployments and Helm charts. Use proper punctuation and paragraph breaks."

这一步的本质是给 Whisper 提供 initial_prompt——Whisper 模型会把该提示词的风格(标点习惯、分段落习惯、甚至领域术语偏好)延续到转写输出中,从而减少专有名词乱写、标点缺失等问题。

源码印证:build_whisper_prompt 的三级降级

transcribe.py 中有一个辅助函数 build_whisper_prompt,它的优先级顺序恰好印证了文档策略的三层结构:

def build_whisper_prompt(god_nodes: list[dict]) -> str:
    if not god_nodes:
        return _FALLBACK_PROMPT                      # 1) 无 god nodes → 兜底提示词

    override = os.environ.get("GRAPHIFY_WHISPER_PROMPT")
    if override:
        return override                              # 2) 环境变量 → 代理自拟的提示词

    labels = [n.get("label", "") for n in god_nodes[:10] if n.get("label")]
    if not labels:
        return _FALLBACK_PROMPT
    topics = ", ".join(labels[:5])
    return f"Technical discussion about {topics}. Use proper punctuation and paragraph breaks."

即:环境变量 GRAPHIFY_WHISPER_PROMPT 优先于 god node 自动拼接;god node 为空或标签缺失时回落到 _FALLBACK_PROMPTtranscribe.py 中定义为 "Use proper punctuation and paragraph breaks.")。tests/test_transcribe.py 中的 test_build_whisper_prompt_env_overridetest_build_whisper_prompt_returns_topic_string 分别验证了这两条路径。

为什么必须 export

文档对导出环境变量特别强调了两遍(原文加粗):

Export it as GRAPHIFY_WHISPER_PROMPT (the exact name the transcriber reads — and it must be exported so the child Python process sees it)

原因是转写命令以 $(cat graphify-out/.graphify_python) -c "..." 形式启动子进程 Python 解释器;未 export 的 shell 局部变量不会进入子进程环境,os.environ.get("GRAPHIFY_WHISPER_PROMPT") 会读到空值。这是本文档最容易被忽略的实操陷阱。

Step 2 —— 批量转写命令(完整可复制)

文档给出的转写命令原样继承如下,可直接复制执行:

export GRAPHIFY_WHISPER_MODEL=base  # or whatever --whisper-model the user passed (must be exported)
export GRAPHIFY_WHISPER_PROMPT="<the one-sentence domain hint you composed in Step 1>"
$(cat graphify-out/.graphify_python) -c "
import json, os, sys
from pathlib import Path
from graphify.transcribe import transcribe_all

detect = json.loads(Path('graphify-out/.graphify_detect.json').read_text(encoding=\"utf-8\"))
video_files = detect.get('files', {}).get('video', [])
prompt = os.environ.get('GRAPHIFY_WHISPER_PROMPT', 'Use proper punctuation and paragraph breaks.')

transcript_paths = transcribe_all(video_files, initial_prompt=prompt)
# Write the JSON from Python (NOT a shell '>' redirect): transcribe_all/Whisper
# print progress to stdout, which would otherwise corrupt the JSON file (#1392).
Path('graphify-out/.graphify_transcripts.json').write_text(json.dumps(transcript_paths, ensure_ascii=False), encoding=\"utf-8\")
print(f'Transcribed {len(transcript_paths)} file(s)', file=sys.stderr)
"

逐行解读关键设计:

  • $(cat graphify-out/.graphify_python):Step 1 已把解析出的正确 Python 解释器路径写入该 sidecar,后续所有命令都复用它,避免 uv tool / pipx / venv 环境下解释器错位;
  • detect.get('files', {}).get('video', []):只处理 detect 分类为 video 的文件,与触发条件闭环;
  • JSON 由 Python 写出而非 shell > 重定向:文档注释明确说明 transcribe_all/Whisper 会向 stdout 打印进度,若用 > file.json 重定向,进度文本会混入 JSON 把文件搞坏(引用 issue #1392);因此把 Transcribed N file(s) 摘要打到 stderr,让 stdout 只留给 Whisper 自身的进度显示。

源码印证:transcribe_all 的批量与容错行为

transcribe.pytranscribe_all 的行为与文档要求逐条对应:

def transcribe_all(video_files, output_dir=None, initial_prompt=None):
    """initial_prompt is shared across all files — built once from corpus god nodes."""
    if not video_files:
        return []
    transcript_paths = []
    for vf in video_files:
        try:
            t = transcribe(vf, output_dir, initial_prompt=initial_prompt)
            transcript_paths.append(str(t))
        except Exception as exc:
            print(f"  warning: could not transcribe {vf}: {exc}")
    return transcript_paths
  • 空输入直接返回空列表;
  • 单文件失败不中断整体:捕获异常、打印 warning 后继续下一个文件——正对应文档"如果某个文件转写失败,打印警告并继续其余文件"的要求;tests/test_transcribe.pytest_transcribe_all_skips_failed 验证了失败文件被跳过、结果列表不含它。

单文件转写的底层细节

深入 transcribe 函数(transcribe.py)可以看到文档未展开的几个实现事实:

配置项 源码取值 说明
模型名 os.environ.get("GRAPHIFY_WHISPER_MODEL", "base") 默认 base;用户传 --whisper-model <name> 时必须 export 该变量
设备 device="cpu" 固定 CPU 推理,compute_type="int8" 量化
解码 beam_size=5 beam search 解码
输出目录 graphify-out/transcripts/ out_path("transcripts") 决定
缓存 transcript_path = out_dir / (audio_path.stem + ".txt") 已存在即直接返回,除非 force=True
输出内容 各 segment 文本去空后用换行拼接 另打印检测到的语言与 segment 数

缓存行为有专门测试覆盖:test_transcribe_uses_cache(存在缓存时不调用 Whisper,直接返回缓存路径)与 test_transcribe_force_rerunsforce=True 时即使缓存存在也重新转写,见 tests/test_transcribe.py)。

依赖安装:video 额外依赖

转写依赖 faster-whisper(转写引擎)与 yt-dlp(URL 音频下载),二者均属于 video 可选依赖组。从 pyproject.toml 可见:

video = ["faster-whisper; python_version >= '3.11'", "yt-dlp>=2026.6.9"]

安装方式即 pip install 'graphifyy[video]'(或 uv tool install 'graphifyy[video]')。这与源码中 _get_whisper() 的 ImportError 提示一致(transcribe.py):

raise ImportError(
    "Video transcription requires faster-whisper. "
    "Run: pip install 'graphifyy[video]'"
)

注意 faster-whisperpython_version >= '3.11' 标记——在 Python 3.11 以下环境中即使安装了 video 依赖组也不会引入 faster-whisper,转写会因 ImportError 失败。此外,转写固定使用 CPU + int8(WhisperModel(model_name, device="cpu", compute_type="int8")),长视频的转写时间应据此预期,可通过 --whisper-model 在模型大小(如 medium,见 skill-pi.md Usage 中的 --whisper-model medium 示例)与精度之间权衡。

转写完成后的接入:转写稿作为文档进入 Step 3

参考文档"After transcription"小节规定了四条收尾规则:

  1. graphify-out/.graphify_transcripts.json 读回转写稿路径列表;
  2. 在 Step 3B 分发语义子代理(semantic subagents)之前,把这些路径加入 docs 列表——转写稿自此完全按文档处理,走既有的语义抽取缓存、chunking 与合并流程;
  3. 打印数量摘要:Transcribed N video file(s) -> treating as docs
  4. 单文件失败打印警告并继续(源码层已保证,见上文 transcribe_all)。

这一"转写→文档"的接力在主 skill 中也有呼应:skill-pi.md 的 Part B Step B0 注释写明 "Video is transcribed to a document in Step 2.5 first",即语义抽取阶段的文件集合由 detect 的 document/paper/image 三类加上转写稿构成,代码文件仍只由 AST 路径覆盖。

附:URL 语料的前置下载与安全防护

虽然 transcribe.md 参考文档聚焦本地文件,但源码中 transcribe() 对 URL 输入有完整支持(transcribe.py):is_url() 判定为 URL 时先经 download_audio() 用 yt-dlp 下载纯音频流。实现上做了两点值得了解的工程处理:

  • 稳定命名与缓存:下载文件名基于 URL 的 SHA-1 前 12 位(yt_<hash>.<ext>),避免 yt-dlp %(title)s 带来的长文件名;已下载文件直接复用并打印 cached audio:transcribe.py);
  • SSRF 防护:下载前先调用 validate_url(url) 拦截私有 IP 与异常 scheme,且 postprocessors: [] 意味着不需要 ffmpeg,直接取原生音频流。

从源码结构看,这为语料中直接混入视频链接(而非本地文件)的场景提供了兜底,但在本文的 skill 流程中,常规路径仍是本地音视频文件。

小结:参数与行为速查

取值 / 行为 依据
触发条件 detect 的 files.video 非空 transcribe.mddetect.py
支持扩展名 .mp4 .mov .webm .mkv .avi .m4v .mp3 .wav .m4a .ogg transcribe.py
Whisper 模型 默认 baseexport GRAPHIFY_WHISPER_MODEL=<name> 覆盖(必须 export) transcribe.py
提示词 优先 export GRAPHIFY_WHISPER_PROMPT=...;无 god nodes 时用 "Use proper punctuation and paragraph breaks." transcribe.py
推理配置 CPU + int8,beam_size=5 transcribe.py
输出 graphify-out/transcripts/<stem>.txt,同 stem 缓存命中直接返回 transcribe.py
清单 graphify-out/.graphify_transcripts.json,由 Python 写出(防 stdout 污染,#1392) transcribe.md
容错 单文件失败打印 warning 并继续 transcribe.py
后续接入 转写稿并入 docs 列表进入 Step 3B 语义抽取 skill-pi.md

掌握以上内容后,你就能在含音视频语料的 graphify 构建中:正确判断转写是否触发、自拟并导出领域提示词、执行批量转写、并把转写稿无缝接入后续的知识图抽取流程。

登录后查看全文
热门项目推荐
相关项目推荐