graphify 音视频转写全指南:将视频语料经 Whisper 转为文档并接入知识图谱
本指南以 graphify 的技能参考文档 transcribe video and audio(即流水线中的 Step 2.5)为核心,系统讲解如何把 .mp4、.mov、.mp3 等视频与音频文件先转写成文本,再当作普通文档参与后续实体抽取与建图。读完你将掌握:转写环节的触发条件、基于图分析结果(god node 标签)自动生成 Whisper 领域提示词的方法、完整可复现的转写命令、--whisper-model/环境变量的正确用法,以及 graphify/transcribe.py 底层的缓存、推理与错误处理机制。
Step 2.5 在 graphify 流水线中的位置与触发条件
graphify 的默认构建流程由技能主文档 graphify/skill.md 编排,依次经过安装校验(Step 1)、文件检测(Step 2)、音视频转写(Step 2.5)、实体与关系抽取(Step 3)、建图与聚类(Step 4)等环节。Step 2.5 是一个完全条件化的步骤:
- 只有当 Step 2 的
detect返回了 ≥1 个video文件时才执行; - 若检测结果为 0 个视频,整步直接跳过,主流程跳入 Step 3;
- 技能参考文档开头明确写着"Load this only when
detectreported one or morevideofiles. A corpus with no video never reads this."——即宿主 Agent 应做到"按需加载"参考文档,无视频语料时连这份参考都不必读取。
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 ...) ← detect 摘要中的 video 类别
video 类别是如何被识别的
detect 阶段通过文件扩展名把音视频统一归类为 FileType.VIDEO。在 graphify/detect.py 中定义了枚举成员 VIDEO = "video",而扩展名白名单同时出现在 detect 与转写模块中,二者保持严格一致(见 graphify/detect.py 与 graphify/transcribe.py):
| 类别 | 扩展名 |
|---|---|
| 视频容器 | .mp4 .mov .webm .mkv .avi .m4v |
| 音频文件 | .mp3 .wav .m4a .ogg |
classify_file() 在扩展名命中该集合时返回 FileType.VIDEO(见 graphify/detect.py)。检测结果随后写入 graphify-out/.graphify_detect.json,其中 files.video 字段即是后续转写的输入清单。
为什么要先转写:音视频无法被直接抽取
视频与音频文件是二进制媒体流,既不能像代码那样走确定性的 AST 抽取(Part A),也不能像 Markdown 那样交给语义子代理直接阅读。因此参考文档给出的核心策略是:
先把它们转写成纯文本(transcript),然后在 Step 3 中把转录文本当作普通文档文件对待。
这一步保证了下游的抽取(Extraction)、聚类(Cluster)、查询(Query)对"视频"与"文档"一视同仁——转写前它们是 video 类别,转写后它们以 .txt 形式进入 document 处理管线。
策略核心:让宿主模型自述"一句话领域提示",再交给 Whisper
语音识别系统在给定领域上下文时准确率更高。graphify 的做法很有特色:不额外调用一次外部 LLM 服务,而是让正在执行任务的宿主 Agent(它本身就是语言模型)基于已有图分析结果,用一句话描述语料领域,然后作为 initial_prompt 喂给 Whisper。
提示词素材从哪里来:god node 标签
参考文档建议宿主模型从以下位置读取"顶层 god node 标签":
graphify-out/.graphify_detect.json(本次检测结果);graphify-out/.graphify_analysis.json(若存在,来自上一次运行的图分析 sidecar,其中gods字段记录了各社区的枢纽节点)。
然后据此写出一句式的领域提示,例如:
| detect/分析给出的标签 | 自动生成的领域提示句 |
|---|---|
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 输出带标点、有段落分隔的规范文本,便于后续按行/段切分抽取。
纯视频语料的回退
参考文档特别强调一个例外:如果语料中只有视频文件、没有任何其他文档或代码,则没有 god node 可用,应直接使用通用回退提示词:
"Use proper punctuation and paragraph breaks."
这个回退策略与源码中的常量一一对应——graphify/transcribe.py 定义了 _FALLBACK_PROMPT = "Use proper punctuation and paragraph breaks."。
源码中的兜底:build_whisper_prompt
当宿主 Agent 不在场(例如纯脚本/CLI 调用)时,模块提供了自动兜底函数 build_whisper_prompt(god_nodes)(见 graphify/transcribe.py),其逻辑顺序为:
god_nodes为空 → 返回_FALLBACK_PROMPT;- 存在环境变量
GRAPHIFY_WHISPER_PROMPT→ 直接返回该覆盖值(等价于宿主模型已写好领域提示); - 否则取前 10 个 god node 的
label,去空后拼出Technical discussion about {前 5 个标签}. Use proper punctuation and paragraph breaks.。
这解释了参考文档中"必须 export 为 GRAPHIFY_WHISPER_PROMPT,转写器读取的就是这个名字"的指令来源——它是 build_whisper_prompt 与 transcribe() 之间传递定制提示的唯一通道。
实际操作:从环境变量导出到执行转写
Step 1:导出 Whisper 领域提示
将上面写好的领域提示句以环境变量形式交给下一步命令。必须使用 export(而非普通赋值),因为转写发生在独立的 Python 子进程中,未导出的 shell 变量不会被子进程继承:
export GRAPHIFY_WHISPER_PROMPT="<你写好的那句领域提示>"
Step 2:执行批量转写
参考文档给出的完整命令(此处保留原样,仅附加逐段注释):
export GRAPHIFY_WHISPER_MODEL=base # 或用户在命令行传入的 --whisper-model 值(必须 export)
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)
# 由 Python 写 JSON(而不是 shell 的 '>' 重定向):transcribe_all/Whisper
# 会向 stdout 打印进度,重定向会污染 JSON 文件 (#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)
"
命令逐段拆解
| 片段 | 作用 |
|---|---|
export GRAPHIFY_WHISPER_MODEL=base |
指定 Whisper 模型。参考文档默认 base;若用户传入 --whisper-model <name>,则改为对应名称,且同样需要 export。 |
$(cat graphify-out/.graphify_python) |
展开为 Skill Step 1 探测并写入的正确 Python 解释器路径(兼容 uv tool、pipx、venv、系统安装),保证 import graphify 命中的与安装时一致。 |
detect.get('files', {}).get('video', []) |
从 Step 2 写入的检测 sidecar 读取待转写文件清单。 |
os.environ.get('GRAPHIFY_WHISPER_PROMPT', …) |
读取 Step 1 导出的领域提示;缺失时回退到通用提示词。 |
transcribe_all(video_files, initial_prompt=prompt) |
对每个视频/音频文件执行转写,返回转录 .txt 路径列表。 |
用 Python write_text 写 JSON |
关键实现细节:Whisper/转写器会向 stdout 打印进度,若改用 shell > 重定向,这些输出会混入 JSON 导致解析失败。测试 test_transcribe_force_reruns 依赖转写器 stdout 行为,此设计在源码与参考中均有注释(#1392)。 |
print(..., file=sys.stderr) |
统计信息显式走 stderr,避免污染任何重定向到 stdout 的结果文件。 |
转写完成后:把转录文档接入 Step 3 语义抽取
transcribe_all() 结束后,宿主 Agent 需要完成以下收尾动作(参考文档原文要求):
- 从
graphify-out/.graphify_transcripts.json读回转录文件路径(该 JSON 的内容是list[str],每个元素是一个.txt绝对或相对路径); - 将这些路径追加进文档列表,再分发 Step 3B 的语义子代理;
- 在输出中打印转写统计:
Transcribed N video file(s) -> treating as docs; - 容错要求:若单个文件转写失败,打印一条 warning 并继续处理其余文件,绝不整体中断。
这与源码行为一致:transcribe_all()(见 graphify/transcribe.py)对每个文件用 try/except 包裹 transcribe(),任何异常仅打印 warning: could not transcribe {vf}: {exc} 并继续;空输入直接返回 []。
转录文本实际落在 graphify-out/transcripts/ 目录(默认输出根来自 graphify/paths.py 的 out_path(),可被 GRAPHIFY_OUT 环境变量整体迁移),命名规则为 原文件名去除扩展名 + ".txt"。
Whisper 模型选择与依赖安装
支持的命令行参数
技能主文档的 Usage 区块展示了端到端入口:
/graphify <path> --whisper-model medium # use a larger Whisper model for better transcription accuracy
当用户带 --whisper-model 运行时,宿主 Agent 必须把它转换成 GRAPHIFY_WHISPER_MODEL 环境变量并 export,供 _model_name() 读取(见 graphify/transcribe.py):
def _model_name() -> str:
return os.environ.get("GRAPHIFY_WHISPER_MODEL", _DEFAULT_MODEL) # _DEFAULT_MODEL = "base"
也就是说模型名的优先级是:环境变量 GRAPHIFY_WHISPER_MODEL > 内置默认 base。参考文档与 skill.md 均以 base 为默认、以 medium 作为"更高精度"的示例,说明该参数接受的是 Whisper/faster-whisper 的模型规格名(tiny/base/small/medium/large 等),具体可用集合取决于已安装的 faster-whisper 版本。
依赖安装(按需 optional extra)
转写相关依赖属于可选特性,见 pyproject.toml 中 [project.optional-dependencies] 的 video extra:
video = ["faster-whisper; python_version >= '3.11'", "yt-dlp>=2026.6.9"]
- faster-whisper:负责本地 Whisper 推理,且声明
python_version >= '3.11',即低版本 Python 下无法启用本特性; - yt-dlp:负责从 YouTube 等 URL 下载音频流。
源码采用懒加载策略(见 graphify/transcribe.py):仅当真正遇到视频文件、transcribe()/download_audio() 被调用时才 import,缺依赖时抛出带安装指引的 ImportError:
Video transcription requires faster-whisper. Run: pip install 'graphifyy[video]'
YouTube/URL download requires yt-dlp. Run: pip install 'graphifyy[video]'
因此一个"纯代码语料"的常规 /graphify . 即使没装任何 extra 也完全不受影响。测试 tests/test_transcribe.py 专门验证了 faster-whisper 缺失时该异常会正确向上抛出。
源码级原理:transcribe() 的一次完整调用
核心函数 transcribe()(见 graphify/transcribe.py)的执行链如下:
transcribe(path, output_dir=None, initial_prompt=None, force=False)
├─ 1. 确定输出目录:默认 GRAPHIFY_OUT/transcripts,自动 mkdir
├─ 2. 若是 URL 前缀(http:// https:// www.)→ 先 download_audio() 下载音频
├─ 3. 命中缓存:transcript 已存在且未 force → 直接返回缓存路径(不推理)
├─ 4. 懒加载 faster-whisper → WhisperModel(model, device="cpu", compute_type="int8")
├─ 5. model.transcribe(audio, beam_size=5, initial_prompt=prompt)
├─ 6. 逐 segment 去空行拼接为文本 → 写 <stem>.txt (UTF-8)
└─ 7. 打印 lang / segments 信息,返回 transcript 路径
值得注意的工程细节:
- 缓存机制:转录路径以音频文件
stem命名。已存在的转录直接复用,force=True才强制重转。测试 tests/test_transcribe.py 与 tests/test_transcribe.py 分别覆盖了"命中缓存跳过推理"与"force 强制重跑"两条路径——后者通过 mock_get_whisper验证写入的是新内容。 - 本地 CPU 推理:
WhisperModel(model_name, device="cpu", compute_type="int8")说明开箱即为 CPU + INT8 量化推理,无需 GPU;beam_size=5是转录解码参数。 - 语言自检:转录完成后从
info.language读取检测到的语言并打印,便于人工核验。 - 容错:单文件异常在
transcribe_all()层被捕获并降级为 warning。
对 URL 语料的支持:download_audio
除本地文件外,参考文档未展开但源码完整支持 URL 语料:transcribe() 依据前缀(http://、https://、www.,见 graphify/transcribe.py)判定输入是否为 URL,是则调用 download_audio()(见 graphify/transcribe.py):
- 先经 graphify/security.py 的
validate_url()校验,拦截私有 IP、非法 scheme 等目标后才交给 yt-dlp(这是 graphify 运行于任意克隆/共享语料时的安全基线); - 用 URL 的 SHA-1 前 12 位生成稳定文件名
yt_<hash>.<ext>,避免 yt-dlp 使用不可控的视频标题作文件名; - 检查缓存目录中是否已有
.m4a/.opus/.mp3/.ogg/.wav/.webm同名音频,命中则复用("cached audio"),否则以bestaudio格式下载。
该函数同样被 URL 摄入链路复用——在 graphify/ingest.py 中,当 /graphify add <url> 遇到 youtube 类型 URL 时即调用 download_audio() 将音频存入语料,随后进入同样的检测→转写流程。
可靠性保障:参考文档的版本化与测试覆盖
- 多平台技能分发:这份
transcribe.md参考文档会随 skillgen 渲染进各 Agent 平台的 skill 包中;tools/skillgen/expected/graphify__skills__agents__references__transcribe.md 正是该参考在agents平台下的期望产物快照,用于测试多平台安装一致性(见 tests/test_install_references.py)。 - 单测覆盖关键契约:tests/test_transcribe.py 验证了以下与本文档强相关的行为——扩展名集合的构成、领域提示的三种生成路径(空节点回退 /
GRAPHIFY_WHISPER_PROMPT覆盖 / 标签拼接)、transcribe_all的空输入、缓存命中与force语义、失败文件跳过不中断。这些测试共同构成了"参考文档承诺、源码实现、测试锁定"的闭环。
小结:视频语料进图的完整链路
把零散音视频变成可查询知识图谱,本质是一条"降维"链路:
- detect 识别
.mp4/.mov/.mp3/.wav…等为video类别并写入graphify-out/.graphify_detect.json; - Step 1(宿主 Agent)依据 god node 标签自撰一句领域提示并
export GRAPHIFY_WHISPER_PROMPT,同时export GRAPHIFY_WHISPER_MODEL(默认base); - Step 2 以
$(cat graphify-out/.graphify_python)启动 Python,调用graphify.transcribe.transcribe_all()批量转写,结果.txt落盘graphify-out/transcripts/,路径清单由 Python 写入graphify-out/.graphify_transcripts.json; - 宿主 Agent 将转录路径并入文档清单,交给 Step 3 语义抽取,随后与代码的 AST 抽取结果合并、聚类、建图、生成报告。
全程无需额外 LLM API 调用(提示词由宿主模型自产),依赖项按需安装(graphifyy[video]),默认 CPU/INT8 即可运行,并以缓存、按文件容错与输出 JSON 的写入纪律保证可重复性与可审计性——这正是 graphify 把"任何语料"(code、docs、papers、images、videos)统一纳入知识图谱的关键一环。
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 StartedRust0624
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