graphify 视频与音频转录指南:在 Step 2.5 用 Whisper 将音视频语料转成可建图文本
视频与音频文件无法被直接读取为文本,因此 graphify 的处理流水线专门预留了一个 Step 2.5——转录阶段:先用 faster-whisper 把检测到的 video 文件转写成带标点与分段规整的纯文本,再把转录产物当作 doc 文件交给后续 Step 3 做实体与关系抽取,最终汇入知识图谱。本篇以 graphify 各 Agent Skill 中随附的 transcribe 参考文档 为主体骨架,结合 transcribe.py 的源码实现,完整还原转录的触发条件、Whisper 域提示词编写策略、可直接复制的转录命令、产物落盘与后续接线方式,让读者既能照抄执行,也能理解每条命令背后的设计理由。
转录在 graphify 流水线中的位置
graphify 对任意语料的建图流程是一个多步流水线:检测文件 →(Step 2.5 转录)→ AST 结构抽取 + 语义抽取 → 图合并与构建。Step 2.5 是按需执行的步骤,其唯一触发条件是 Step 2 的检测结果里存在 video 类文件:
- 语料中没有视频时,跳过整个 Step 2.5,
transcribe参考文档甚至不会被读取(“Load this only whendetectreported one or morevideofiles”)。 - 检测到视频时,先转录成文本,再在 Step 3 中把转录稿当作 doc 文件处理。
在 skill-droid.md 等各 Agent 的 Skill 主文件里,这一逻辑被精炼为一行话:“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.” 换句话说,Step 2.5 是 doc 语义抽取(Step 3B)之前唯一的“预加工”关卡。
Step 2 的检测结果会由 Agent 用 Python 写入 sidecar 文件 graphify-out/.graphify_detect.json。该 JSON 的 files.video 数组保存了本次扫描发现的所有视频/音频文件路径,转录步骤正是以它为输入起点。检测阶段的底层实现位于 graphify/detect.py(detect() 函数,见 detect.py),其返回结构包含 files(按 code/document/paper/image/video 分桶)、total_files、total_words、skipped_sensitive、scan_root 等字段,Skill 文档中给出的检测摘要示例也据此展示为:
Corpus: X files · ~Y words
code: N files (.py .ts .go ...)
docs: N files (.md .txt ...)
papers: N files (.pdf ...)
images: N files
video: N files (.mp4 .mp3 ...)
转录原理:graphify/transcribe.py 的实现细节
参考文档给出的命令最终都落在 graphify/transcribe.py 的公开 API 上,理解这个模块能帮助读懂命令的每个参数。
支持的文件形态与范围
模块顶层定义了识别的音视频扩展名与 URL 前缀:
VIDEO_EXTENSIONS = {'.mp4', '.mov', '.webm', '.mkv', '.avi', '.m4v', '.mp3', '.wav', '.m4a', '.ogg'}
URL_PREFIXES = ('http://', 'https://', 'www.')
可见 “video 文件” 实际包含两类输入:
- 本地媒体文件:常见的容器格式(mp4/mov/webm/mkv/avi/m4v)与纯音频格式(mp3/wav/m4a/ogg);
- URL:以
http://、https://、www.开头的串会被is_url()识别为网络资源,而非本地路径。
对 URL 输入,download_audio() 会先用 yt-dlp 拉取音频流(format 优先 bestaudio[ext=m4a]/bestaudio/best,无需 ffmpeg 后处理),再进入转录;下载文件名基于 URL 的 SHA-1 前 12 位哈希生成稳定的 yt_<hash>.<ext>,同一 URL 再次出现时直接命中缓存,不会重复下载。下载前会调用 graphify/security.py 的 validate_url() 做安全校验——从源码注释可见其职责是“在 yt-dlp 运行前阻止私有 IP 与危险 scheme”。
转录引擎、默认参数与缓存
转录本身由 faster-whisper 驱动,transcribe() 的实现要点包括:
WhisperModel = _get_whisper()
model_name = _model_name() # 读 GRAPHIFY_WHISPER_MODEL,缺省 "base"
prompt = initial_prompt or _FALLBACK_PROMPT # 缺省 "Use proper punctuation and paragraph breaks."
model = WhisperModel(model_name, device="cpu", compute_type="int8")
segments, info = model.transcribe(str(audio_path), beam_size=5, initial_prompt=prompt)
lines = [segment.text.strip() for segment in segments if segment.text.strip()]
transcript = "\n".join(lines)
transcript_path.write_text(transcript, encoding="utf-8")
几点值得注意:
- 模型名:
_model_name()读取环境变量GRAPHIFY_WHISPER_MODEL,缺省为base(见 transcribe.py)。这就是参考文档要求“必须export该变量”的原因——Python 子进程正是通过进程环境读它的。 - 运行参数:模型固定以 CPU + int8 量化运行(
device="cpu", compute_type="int8"),解码采用beam_size=5,域提示词经initial_prompt传入。 - 产物路径与命名:转录稿输出目录为
graphify-out/transcripts(经 paths.py 的out_path("transcripts")解析,受GRAPHIFY_OUT环境变量影响),文件名取音频文件的主干名加.txt后缀,UTF-8 编码。 - 缓存:若同名
.txt已存在且未传force=True,直接复用缓存稿,不重复转录(对增量重跑非常友好)。 - 失败隔离:
transcribe_all()逐文件try/except,单个文件失败只打印warning: could not transcribe <path>: <exc>后继续,返回其余成功的路径列表——这正是参考文档“失败则告警并继续”行为背后的实现。
依赖与安装前提
_get_whisper() 与 _get_yt_dlp() 在缺失依赖时会抛出带安装提示的 ImportError:转录需要 faster-whisper,URL 下载需要 yt-dlp。在 pyproject.toml 中两者被收进可选依赖组:
video = ["faster-whisper; python_version >= '3.11'", "yt-dlp>=2026.6.9"]
即 pip install 'graphifyy[video]' 即可补齐转录能力(注意 faster-whisper 要求 Python 3.11+,这是 graphify 自身 requires-python = ">=3.10" 之上的额外约束);all 组也包含这两项。完整地讲,一次需要转写含视频语料、且要走 URL 下载的运行,需要同时具备 graphifyy[video] 依赖与可用的 yt-dlp。
Step 1:编写 Whisper 域提示词(Domain Hint)
Whisper 支持 initial_prompt(初始提示词),可以用一句领域描述显著提升术语、人名、缩写与专业词汇的识别准确率。参考文档给出的策略是:不要让 Agent 额外调用一次 LLM 来生成提示词——运行中的模型本身就是语言模型,直接基于图谱侧已有的 god node 标签自写一句即可,零额外 API 成本。
具体操作:从 graphify-out/.graphify_detect.json(若上轮已产出分析文件则读分析文件)读取顶层 god node 标签,据此写一句短领域提示。文档示例:
- 标签
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."——它要求 Whisper 输出规整的标点与自然分段,这对后续把转录稿当作 doc 文件切块、建图至关重要。
有一个例外:如果语料只有视频、没有任何其他文档/代码,就不存在可供参考的 god node 标签,此时直接使用通用兜底提示词:
"Use proper punctuation and paragraph breaks."
这也是 _FALLBACK_PROMPT 常量在 transcribe.py 中的取值。源码里的 build_whisper_prompt(god_nodes) 实现了同款逻辑的确定性版本:取前 10 个 god node 的标签、拼接前 5 个成主题串,拼成 "Technical discussion about {topics}. Use proper punctuation and paragraph breaks.";无标签或节点为空时回落兜底提示词。若设置了 GRAPHIFY_WHISPER_PROMPT 环境变量,则以该变量值为准(即覆盖优先)。
写完这句领域提示后,必须通过如下方式传递给转录命令:
export GRAPHIFY_WHISPER_PROMPT="<the one-sentence domain hint you composed in Step 1>"
参考文档特别强调:变量名必须精确为 GRAPHIFY_WHISPER_PROMPT(transcriber 读取的名字),并且必须 export,因为转录运行在子进程 Python 中,只有导出到环境才能真正被读到。
Step 2:执行转录(可直接复制的完整命令)
参考文档给出了整段可复制的 bash 块,逐段拆解如下:
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):graphify 各 Skill 在 Step 1 会探测/引导出能 import graphify 的 Python 解释器并把路径写入graphify-out/.graphify_python,后续每个 bash 块都用该文件内容作为解释器,保证版本与依赖一致。 - 模型导出:
GRAPHIFY_WHISPER_MODEL默认base;若用户本次通过 CLI 传了--whisper-model <name>(例如各 Skill 文档中的/graphify <path> --whisper-model medium),则必须export GRAPHIFY_WHISPER_MODEL=<name>后执行——只做 shell 变量赋值(VAR=x cmd或普通赋值)而不导出都会导致子进程读不到。 - 提示词导出:
GRAPHIFY_WHISPER_PROMPT把 Step 1 写好的句子带进子进程;Python 侧用os.environ.get(..., 默认值)读取,因此即使漏导出也会安全回落为通用兜底提示词,不会让整段命令崩溃。 - 从检测 JSON 取文件列表:
detect['files']['video']是唯一输入源,与 Step 2 的检测结果严格对应。 - 为什么用 Python 写 JSON 而不是 shell 重定向:代码注释与文档都点明了原因——
transcribe_all()/Whisper 会持续向 stdout 打印进度,若用>重定向,这些进度文本会混入 JSON 导致文件损坏(代码中对应 issue #1392)。因此结果文件graphify-out/.graphify_transcripts.json必须由 Python 进程用Path.write_text(json.dumps(...))原子写入;连进度信息也刻意用file=sys.stderr输出,与 stdout/JSON 彻底隔离。
转录完成之后:把转录稿接回文档流水线
参考文档明确了转录成功后的四个收尾动作,它们共同保证转录稿真正进入下游抽取:
- 从
graphify-out/.graphify_transcripts.json读取全部转录稿路径; - 在派发 Step 3B 语义子 Agent 之前,把这些路径并入 docs 文件列表——转录稿从此与
.md/.txt等文档同等对待; - 打印产物数量:
Transcribed N video file(s) -> treating as docs; - 若某个文件转录失败,打印警告并继续处理其余文件(对应源码
transcribe_all()的逐文件异常隔离)。
这一步是“视频语料能进知识图谱”的关键桥梁:只有转录稿被加入 docs 列表,Step 3B 的语义抽取(非结构化文档的实体/关系/hyperedge 抽取)才会把它们纳入分块与建图范围;对纯代码语料则可整体跳过。同时注意,skill-droid.md 在缓存检查步骤里也明确把 video 排除在语义抽取的原始文件之外——因为“Video is transcribed to a document in Step 2.5 first”,避免子 Agent 重复读取源媒体文件。
转录与图谱数据流小结
把参考文档与源码串起来,一次含视频语料的 graphify 建图在转录环节的数据流是:
detect(root)
│ 写入 graphify-out/.graphify_detect.json
│ files.video = [.mp4/.mp3/... / URL]
▼
Step 2.5 Agent 读 god node 标签 → 写一句领域提示(GRAPHIFY_WHISPER_PROMPT)
│
├─ URL 输入 → security.validate_url() → yt-dlp 下载音频到 downloads/
└─ 本地文件
│ ▼
transcribe_all(files, initial_prompt)
│ faster-whisper (base 默认, cpu/int8, beam_size=5)
│ 产物: graphify-out/transcripts/<stem>.txt(同名则缓存复用)
│ ▼
graphify-out/.graphify_transcripts.json(由 Python 写入,杜绝 stdout 污染)
│ ▼
转录稿并入 docs 列表 → Step 3 AST + 语义抽取 → 图合并/构建
进一步阅读
- transcribe 参考文档(skill 自带,各 Agent 目录同构):本篇主体,也是被生成到各 Skill 包中的操作手册;对应生成产物参见 tools/skillgen/expected/graphify__skills__droid__references__transcribe.md。
- transcribe.py:转录、URL 下载、提示词构建与缓存逻辑的唯一实现。
- skill-droid.md:Step 2.5 在整条流水线中的上下文与调用入口(各 Agent 的 skill 主文件内容一致)。
- detect.py:
detect()产生含files.video的检测结果。 - paths.py:
out_path()决定graphify-out/transcripts与全部 sidecar 的落盘位置。 - pyproject.toml:
video可选依赖组(faster-whisper + yt-dlp)与 Python 版本约束。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0629
MiniCPM5-2BMiniCPM5-2B 是一款面向端侧、本地部署和资源受限场景的 2B 稠密 Transformer,能够达到同尺寸开源模型 SOTA 水平。Markdown00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
HivisionIDPhotos⚡️HivisionIDPhotos: a lightweight and efficient AI ID photos tools. 一个轻量级的AI证件照制作算法。Python07
DragonOSDragonOS is an operating system developed from scratch using Rust, with Linux compatibility. It is designed for **Serverless** scenarios. 使用Rust从0自研内核,具有Linux兼容性的操作系统,面向云计算Serverless场景而设计。Rust00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00