首页
/ graphify 的视频转写机制:Whisper 集成、God-Node 领域提示与 Step 2.5 流水线

graphify 的视频转写机制:Whisper 集成、God-Node 领域提示与 Step 2.5 流水线

2026-09-06 13:35:23作者:咎竹峻Karen

本文以 Claude 技能参考文档 transcribe.md 为核心,完整拆解 graphify 把视频/音频语料纳入知识图谱的 Step 2.5 环节:从 detect 检测到 video 文件,到由 god node 标签生成 Whisper 领域提示(domain hint),再到执行本地 faster-whisper 转写、产出 .txt 文稿并回流到文档抽取流程的全过程。读完你可以掌握两个环境变量的正确用法(export 而非赋值)、避免 JSON 输出被 stdout 进度污染的经典陷阱(#1392),并能对照 graphify/transcribe.py 源码理解缓存、URL 下载与容错机制的实现细节。

Step 2.5 在流水线中的定位:仅在检测到视频时触发

graphify 的构建流水线中,转写是一个"按需加载"的分支步骤。技能主文档 graphify/skill.md 明确规定:

Skip this step entirely if detect returned zero video files. When the corpus has video or audio, see references/transcribe.md to transcribe them to text first, then treat the transcripts as doc files in Step 3.

也就是说,语料库里没有任何视频文件时,Agent 根本不会读取 transcribe 参考文档——这是原文档开宗明义的第一句约束。它的触发链是:

  1. Step 2 的 detect 按扩展名把语料分类,视频/音频文件归入 video 桶(判定表见 graphify/detect.py 中的 VIDEO_EXTENSIONS.mp4 .mov .webm .mkv .avi .m4v .mp3 .wav .m4a .ogg,与 graphify/transcribe.py#L11 中的集合保持一致),结果写入 graphify-out/.graphify_detect.json
  2. video 列表非空,进入 Step 2.5:先把视频转成文本,再在 Step 3 中把文稿当作普通文档参与语义抽取;
  3. 同一份参考文档也被复制到其他平台技能目录下(如 graphify/skills/codex/references/transcribe.md),各平台技能的行为是等价的。

领域提示(domain hint):让 god nodes 免费指导 Whisper 的 initial_prompt

参考文档给出的核心策略是:不额外调用任何 API。你自己(编码 Agent)本来就是一个语言模型,直接读取 god node 标签,亲手写一句领域提示,再把它作为 Whisper 的 initial_prompt 传入即可。

数据来源。 god node 标签来自 graphify-out/.graphify_detect.json(首次运行)或上一次运行留下的分析文件。god nodes 本身由 graphify/analyze.py#L109-L130god_nodes(G, top_n=10) 计算:按度(degree)降序取前 10 个真实实体,并排除文件级 hub 节点、概念节点与 JSON 键噪声节点——这些是语料库中"连接最多的核心抽象",天然适合作为话题锚点。

提示的写法。 原文档给出两个示范:

  • 标签为 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."

兜底规则。 如果语料库只有视频文件、没有任何其他文档或代码可供推断领域,直接使用通用兜底提示:

"Use proper punctuation and paragraph breaks."

这个字符串在源码中就是 _FALLBACK_PROMPT 常量(graphify/transcribe.py#L16),保证文档与实现一致。

环境变量传递与源码印证。 提示必须命名为 GRAPHIFY_WHISPER_PROMPT 并且必须 export(不是 shell 里的普通变量赋值),因为转写是在一个子 Python 进程中运行的,只有导出的环境变量才对子进程可见。这一点在源码里有双重印证:

  • graphify/transcribe.py#L95-L115build_whisper_prompt() 会优先检查 os.environ.get("GRAPHIFY_WHISPER_PROMPT"),一旦存在就短路掉基于 god node 的自动拼装;测试用例 test_build_whisper_prompt_env_override(见 tests/test_transcribe.py#L40-L44)专门验证了这个短路行为;
  • 未设置时,build_whisper_prompt() 会取前 10 个 god node 标签中的前 5 个拼成 "Technical discussion about {topics}. Use proper punctuation and paragraph breaks." 作为次级默认。

执行转写:参考文档给出的完整命令

原文档 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 解析出的、真正装有 graphify 依赖的解释器路径,而不是假设的 python3 graphify/skill-agents.md#L98 写入该文件
detect.get('files', {}).get('video', []) 从 detect 输出中取出视频文件列表 graphify-out/.graphify_detect.json 结构
GRAPHIFY_WHISPER_PROMPT 子进程读取的领域提示,缺省时回退到兜底提示 graphify/transcribe.py#L106
GRAPHIFY_WHISPER_MODEL 选择 Whisper 模型,默认 base graphify/transcribe.py#L14-L20_model_name() 读环境变量,缺省 _DEFAULT_MODEL = "base"
用 Python 写 JSON 而非 shell > 重定向 transcribe_all/Whisper 会向 stdout 打印进度,重定向会把进度混进 JSON 文件导致其损坏 文档中标注的 #1392 回归
print(..., file=sys.stderr) 计数信息走 stderr,同样保护 JSON 通道 同上

模型选择。 默认 base;如果用户在命令中传了 --whisper-model <name>(例如 graphify/skill.md#L21 示范的 /graphify <path> --whisper-model medium),则必须 export GRAPHIFY_WHISPER_MODEL=<name> 后再执行上面的命令——再次强调是 export 而非赋值。

转写完成后:文稿如何回流到 Step 3

参考文档对"转写之后"的动作有明确约定:

  • graphify-out/.graphify_transcripts.json 读回所有文稿路径;
  • 在派发 Step 3B 的语义子 Agent 之前,把这些路径并入文档(docs)清单——从此它们与 .md 文档享受同等待遇;
  • 打印 Transcribed N video file(s) -> treating as docs 告知用户数量;
  • 若某个文件转写失败,打印警告并继续处理其余文件,不让单点失败中断整条流水线。

最后一条容错语义由 graphify/transcribe.py#L166-L186transcribe_all() 实现:它逐个调用 transcribe()except Exception 时打印 warning: could not transcribe ... 并跳过,最终只返回成功项的路径列表;空输入直接返回 []。对应的测试 test_transcribe_all_skips_failedtests/test_transcribe.py#L136-L147)验证了"失败即跳过、不抛异常"的行为。

深入 transcribe.py:缓存、URL 下载与本地推理参数

参考文档只描述了调用面,而 graphify/transcribe.py 的实现面还有几个值得了解的机制:

输出与缓存。 文稿默认写入 graphify-out/transcripts/ 目录(_TRANSCRIPTS_DIR 经由 graphify/paths.py#L295-L301out_path("transcripts") 解析,尊重 GRAPHIFY_OUT 覆盖)。文件名取音频文件的 stem 加 .txt;若该文件已存在且未传 force=Truetranscribe() 直接返回缓存路径,完全不加载 Whisper 模型。测试 test_transcribe_uses_cachetest_transcribe_force_reruns 分别覆盖了"命中缓存"与 force 强制重转两条路径。

本地确定性推理。 转写使用 faster-whisper,固定参数 WhisperModel(model_name, device="cpu", compute_type="int8"),转写时 beam_size=5 并传入 initial_promptgraphify/transcribe.py#L150-L155)。从源码结构看,这是一个纯 CPU + int8 量化配置——与 graphify "本地、无向量库、确定性"的整体立场一致,不需要 GPU 或云端 ASR 服务。

URL 支持。 若输入不是文件路径而是 http://https://www. 开头的 URL,transcribe() 会先经 download_audio() 用 yt-dlp 只拉音频流:以 URL 的 SHA-1 前 12 位生成稳定文件名 yt_<hash>.<ext>,命中已有缓存则直接复用;下载前还会调用 graphify.securityvalidate_url() 拦截私有 IP 与非法 scheme(graphify/transcribe.py#L50-L92)。

依赖安装。 视频能力是可选依赖,在 pyproject.toml 中声明为 video extra:faster-whisper(要求 Python ≥ 3.11)与 yt-dlp>=2026.6.9。若未安装,_get_whisper() / _get_yt_dlp() 会抛出带安装提示的 ImportErrorpip install 'graphifyy[video]'test_transcribe_missing_faster_whisper 验证了该异常会向上传播而不是静默吞掉。

小结:这条参考文档的工程价值

transcribe.md 篇幅不长,但每一行都是踩过坑的约定:

  1. 条件加载——没有视频的语料永远不读它,避免无关上下文稀释 Agent 注意力;
  2. 零额外 API 的领域提示——god node 标签 → 一句话 domain hint → Whisper initial_prompt,测试 test_build_whisper_prompt_returns_topic_string 确认了"不发 LLM 请求"的拼装逻辑;
  3. 两个必须 export 的环境变量——GRAPHIFY_WHISPER_PROMPT(可选,缺省回退兜底提示)与 GRAPHIFY_WHISPER_MODEL(默认 base);
  4. JSON 输出走 Python、进度走 stderr——#1392 的教训被固化进了命令注释;
  5. 失败不阻塞——transcribe_all 的逐文件容错保证单个坏文件不拖垮整个图构建。

想继续深入,可以从 tests/test_transcribe.py 的全部用例出发逐条对照源码,或查看 graphify/skill.md 中 Step 2 → Step 2.5 → Step 3 的完整调度关系。

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