graphify 视频与音频转录全解析:将 Whisper 产物接入知识图谱流水线的 Step 2.5 参考实现
graphify 把"代码、文档、PDF、图片乃至视频/音频"统一映射进一张可查询的知识图谱。其中视频与音频这类无法直接读取的媒体,由 graphify-out 产物目录与 graphify.transcribe 模块负责先转录成文本、再当作普通 doc 参与语义抽取。本篇文章以仓库中随各平台 skill 分发的 graphify/skills/windows/references/transcribe.md 参考文档为骨架,结合 graphify/transcribe.py、graphify/detect.py 与 tests/test_transcribe.py 的源码级证据,完整讲解这条 Step 2.5 转录链路的触发条件、领域提示词策略、可运行命令、环境变量契约与底层实现原理。读完你可以掌握如何在任一 coding agent 会话中把视频语料低成本地并入 graphify 图谱,也能理解 Whisper 模型选择、缓存与失败兜底的内部机制。
参考文档的角色与加载时机
这份 transcribe reference 属于 graphify skill 的按需加载片段:仅当 detect 报告了至少一个 video 文件时才读取;一个不含视频的语料库永远不会读到它。它所在的主流水线描述于各平台 skill 中,例如 graphify/skill-windows.md 的 ### Step 2.5 - Video and audio (only if video files detected) 一节:如果 detect 返回零个 video 文件,应整体跳过本步骤、直接进入 Step 3 的实体与关系抽取;若语料含视频/音频,则先按本 reference 转写为文本,再将转录稿当作 doc 文件参与 Step 3。
从 graphify/detect.py 可以看到分类的判定基础:FileType.VIDEO = "video",文件类型由扩展名集合决定(detect.py):
.mp4 .mov .webm .mkv .avi .m4v # 视频
.mp3 .wav .m4a .ogg # 音频
值得注意,这份集合在 graphify/transcribe.py 中按同一字面量维护,并在 detect 落盘的结果(graphify-out/.graphify_detect.json)里以 files.video 数组形式暴露给后续步骤——这正是本步骤命令的输入来源。此外 README 的隐私一节明确说明:视频/音频在本地用 faster-whisper 转录,数据不离开机器。
为什么必须先转写:媒体 → 文本 → doc 的降级策略
视频与音频文件无法被直接解析成实体与关系。参考文档给出的处理范式是:
- 先转录为纯文本;
- 把转录稿当作与
.md/.txt同等的 doc 文件,交回 Step 3 的语义子代理做知识抽取。
也就是说,Step 2.5 是一个"文本化预处理层",它让 graphify 的语义抽取对媒体输入透明。这一设计也解释了 graphify/skill-windows.md 中 Step 3 对文件类别的说明:语义抽取只针对 document、paper、image 展开,视频必须先经本步骤转换为文档。
Step 1:用语料自身充当提示词来源,写出 Whisper 初始提示
参考文档的核心巧思在于:不再额外调用一次 LLM API。执行转录的 coding agent(Claude Code、Codex、Gemini CLI 等)本身就是语言模型,可以直接从 detect 结果或历史分析文件中读取顶层 god node 标签,自行"脑补"出一句领域提示(domain hint),再把它作为 initial_prompt 喂给 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."
特例:若语料只有视频文件、没有任何其他 doc/代码(无 god node 可用),则使用通用兜底提示词:
Use proper punctuation and paragraph breaks.
环境变量契约:GRAPHIFY_WHISPER_PROMPT 必须 export
写好的领域提示句必须导出为 GRAPHIFY_WHISPER_PROMPT(这是转录器读取的精确名称),并且在 bash 里必须使用 export 而不是简单赋值,否则子进程 Python 看不到该变量。文档强调这一步不能省。
在 graphify/transcribe.py 中,这一环境变量约定在 build_whisper_prompt()(transcribe.py)中得到印证:它优先读取 os.environ.get("GRAPHIFY_WHISPER_PROMPT") 作为 override,随后才退回到由 god node label 拼接的模板 "Technical discussion about <topics>. Use proper punctuation and paragraph breaks."。测试 tests/test_transcribe.py 中 test_build_whisper_prompt_env_override 专门验证了环境变量可短路程序化拼接;test_build_whisper_prompt_no_nodes 与 test_build_whisper_prompt_nodes_without_labels 则保证空输入或缺 label 键的节点不会导致异常。
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):skill 流水线在 Step 1 会把真正装有 graphify 的 Python 解释器路径写入graphify-out/.graphify_python,随后的每个 Python 步骤都必须经它执行,保证所用解释器确实能import graphify。参见 graphify/skill-windows.md 的解释器约定;该文件还记录了一个 Windows 特有的细节——写.graphify_python时必须用无 BOM 的 UTF-8(WriteAllText),避免 BOM 混入路径导致构建失败。 -
JSON 必须由 Python 写盘,禁用 shell
>重定向:transcribe_all与底层 Whisper 会把进度信息打印到 stdout,若再用 shell 重定向落盘,进度噪声会污染 JSON 文件(文档内标注的问题编号 #1392)。因此示例将进度打印显式送到sys.stderr,路径清单则用Path.write_text()写为graphify-out/.graphify_transcripts.json(ensure_ascii=False保证非 ASCII 路径不被打乱)。 -
transcribe_all的入参即 initial_prompt:领域提示只构造一次,对该批全部文件共享。
转录完成后的收尾动作
按参考文档,转录结束后必须依次执行:
- 从
graphify-out/.graphify_transcripts.json读取转录稿路径; - 在向 Step 3B 派发语义子代理之前,把这些路径追加进 docs 列表;
- 打印创建数量:
Transcribed N video file(s) -> treating as docs; - 单个文件转录失败时打印 warning 并继续处理其余文件,而不是中止整个流水线。
"失败不中断"在源码中是显式保证的:transcribe_all()(transcribe.py)对每个文件包一层 try/except,失败时打印 warning: could not transcribe {vf}: {exc} 后继续;tests/test_transcribe.py 的 test_transcribe_all_skips_failed 用 mock 抛错验证了失败文件会被跳过且返回列表为空。
Whisper 模型选择与运行环境前提
参考文档明确了模型契约:
- 默认模型为
base;若用户传了--whisper-model <name>,则必须export GRAPHIFY_WHISPER_MODEL=<name>后再执行上述命令。 - 各平台 skill 的用法示例中均有对应 CLI 说明,例如
/graphify <path> --whisper-model medium表示"用更大的 Whisper 模型换取更高转录准确率"。
在 graphify/transcribe.py,_DEFAULT_MODEL = "base" 与 _model_name() 实现了同一契约:读 GRAPHIFY_WHISPER_MODEL,缺省回落到 base。模型加载与推理参数集中在 transcribe()(transcribe.py):
model = WhisperModel(model_name, device="cpu", compute_type="int8")
segments, info = model.transcribe(
str(audio_path),
beam_size=5,
initial_prompt=prompt,
)
即使用 faster-whisper 的 WhisperModel,默认 CPU + int8 量化运行、beam_size=5、initial_prompt 传入领域提示;转录文本按非空 segment 分行拼装后写入 graphify-out/transcripts/<音频文件stem>.txt(输出目录由 out_path("transcripts") 决定,见 transcribe.py,其中 out_path 封装了默认输出目录 graphify-out,可被 GRAPHIFY_OUT 环境变量整体覆盖,参见 graphify/paths.py)。模块头部注释也点明:转录产物随后进入图谱抽取流程。
两个额外的重要机制:
- 依赖提示:
_get_whisper()在缺少faster_whisper时抛出明确错误,提示执行pip install 'graphifyy[video]'。README 的 extras 表把video能力对应为faster-whisper + yt-dlp,安装方式为uv tool install "graphifyy[video]"。 - 缓存与强制重转写:
transcribe()若发现同名.txt已存在且未传force=True,直接返回缓存路径,不重复运行 Whisper。这一点由 tests/test_transcribe.py 的test_transcribe_uses_cache与test_transcribe_force_reruns双重验证,是增量运行节省时间/算力的基础。
扩展能力:URL 输入与安全防线(源码佐证)
参考文档正文面向"本地 video 文件清单",而 graphify/transcribe.py 提供的实现能力略超于此,可作为理解后续行为的背景:is_url() 依据 http://、https://、www. 前缀识别 URL;download_audio() 用 yt-dlp 拉取纯音频流,并用 URL 的 SHA-1 前 12 位生成稳定的 yt_<hash>.<ext> 缓存名,避免以视频标题作文件名带来的长度与字符风险。README 的用法示例中 /graphify add <video-url> 即对应"转录并加入一个视频"的场景。
值得强调的安全设计:download_audio() 在执行 yt-dlp 之前会调用 graphify.security.validate_url(),用于拦截私网 IP 与非法 scheme(见 transcribe.py)。这是语料可能来自共享/克隆文件夹时的纵深防御。
平台差异说明:为何这份 reference 挂在 "windows" 下却全文通用
本参考文档的生成副本位于 tools/skillgen/expected/graphify__skills__windows__references__transcribe.md,其内容与源码副本一致。从仓库结构看,references/transcribe.md 在 windows、agents、claude、codex 等各平台 skill 目录下逐字复刻,说明该步骤的领域逻辑是平台无关的——真正的平台差异只体现在调用外壳上:POSIX 风格 skill 用 export 与 $(cat ...),而 graphify/skill-windows.md 采用 PowerShell 的 & (Get-Content graphify-out\.graphify_python) 直呼保存的解释器。若在 PowerShell 主机上执行本步骤,可将两条 export 等价改写为 $env:GRAPHIFY_WHISPER_MODEL = "base"、$env:GRAPHIFY_WHISPER_PROMPT = "<domain hint>",使子进程 Python 同样能读到这两个变量,再以相同的方式读取 .graphify_detect.json、调用 transcribe_all 并写盘 .graphify_transcripts.json。
参考文档在仓库中的血缘与回归保障
tools/skillgen/expected/ 下保存着每次 skill 生成的期望快照(golden),tools/skillgen/expected/graphify__skills__windows__references__transcribe.md 即本 reference 的回归基准,防止生成器漂移。与该文档对应的实现侧保障链条可沿三条线索展开阅读:
- 语义与提示词逻辑:graphify/transcribe.py(
build_whisper_prompt、transcribe、transcribe_all、download_audio); - 检测与分类契约:graphify/detect.py 的
FileType.VIDEO与VIDEO_EXTENSIONS,以及 README 对videoextra、隐私承诺与/graphify <video-url>用法的说明; - 行为契约:tests/test_transcribe.py(缓存命中、
force重转写、缺依赖抛错、空输入安全、批量失败跳过、环境变量 override 等)。
简言之,Step 2.5 是 graphify 把"不可读媒体"纳入统一知识图谱的桥梁:detect 负责发现,agent 负责用 god node 标签生成零额外 API 成本的领域提示,graphify.transcribe 负责以本地 faster-whisper 完成转录、缓存与失败隔离,最终转录稿作为 doc 回流 Step 3,与代码、PDF、图片一起沉淀为可查询的图谱节点。
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 StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00