graphify 视频音视频转写全流程解析:从 detect 结果到知识图谱文档的 Step 2.5
graphify 视频音视频转写全流程解析:从 detect 结果到知识图谱文档的 Step 2.5
导读
graphify 的核心能力是把任意语料(代码、文档、PDF、图片)转化为可查询的知识图谱,而视频与音频文件无法被直接阅读,必须先把它们转写成文本。本文围绕 graphify 运行手册中的转写参考文档 transcribe.md,完整讲解 "Step 2.5 - Transcribe video / audio files" 的触发条件、Whisper 领域提示词生成策略、可复制的转写命令,以及 graphify-out/.graphify_transcripts.json 与文档合并的下游处理;同时结合 transcribe.py 与 test_transcribe.py 源码级验证缓存、URL 下载、容错等底层实现。读完后,你将掌握在任意含音视频的语料上打通"音视频 → 文本转写 → 知识图谱文档"整条管线的完整实操方案。
一、这张参考文档在流水线中的位置
graphify 将编码智能体(Trae、Claude Code、Codex、Gemini CLI 等)的工作流编排为一套分步 runbook。在 skill-trae.md 中,流水线从 Step 1 环境检测开始,Step 2 调用 detect.py 扫描语料并写出 graphify-out/.graphify_detect.json,随后进入提取阶段:
### Step 2.5 - Video and audio (only if video files detected)——Skip this step entirely ifdetectreturned zerovideofiles. When the corpus has video or audio, seereferences/transcribe.mdto transcribe them to text first, then treat the transcripts as doc files in Step 3.(见 skill-trae.md)
要点非常明确:
- 按需加载:
transcribe.md只在detect报告存在一个及以上video文件时才被读取;纯文本/代码语料永远不会读到它,也就不会触发额外依赖下载。 - 转写是"文档化"前奏:视频/音频被转写为
.txt后,这些.txt在 Step 3 中被当作普通 doc 文件参与语义子代理(semantic subagents)的实体与关系抽取,最终成为图谱节点。 - 单一事实来源:被转写的文件清单来自
graphify-out/.graphify_detect.json的files.video数组,而不是让智能体再次扫描目录。
此外,references/transcribe.md 在所有 skill 变体(如 agents、claude、kiro、windows 等)下均有同构副本,本文以其 Trae 变体为讲解主体。
二、什么时候触发:video 文件的判定范围
模块级扩展名白名单定义在 transcribe.py:
VIDEO_EXTENSIONS = {'.mp4', '.mov', '.webm', '.mkv', '.avi', '.m4v',
'.mp3', '.wav', '.m4a', '.ogg'}
即 detect 会把上述格式的文件归入 files.video 分类。test_transcribe.py 的 test_video_extensions_set 也验证了 .mp4/.mp3/.wav/.mov 在列而 .py 不在。值得注意的是该集合同时包含视频与纯音频格式,因此本步骤实际覆盖"任何无法被直接读取的视听媒体"。
URL 也在支持范围内(见下文的 yt-dlp 下载),transcribe.py 用前缀白名单区分 URL 与本地路径:
URL_PREFIXES = ('http://', 'https://', 'www.')
三、策略核心:自己写一句 Whisper 领域提示词
这是本参考文档最有技巧性的一节。Whisper 转写质量高度依赖 initial_prompt(用于纠正术语、标点与断句),而 graphify 的编排方式是由编码智能体自己充当语言模型来生成这句提示词,不额外发一次 LLM API 调用:
- 从
graphify-out/.graphify_detect.json(或上一次运行遗留的.graphify_analysis.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."
- 将其
export为环境变量GRAPHIFY_WHISPER_PROMPT供下一步骤使用。
两个关键约束:
- 变量名必须精确为
GRAPHIFY_WHISPER_PROMPT——这是转写模块实际读取的名字; - 必须用
export(而非普通赋值),因为转写命令是通过$(cat graphify-out/.graphify_python)启动的子 Python 进程,只有导出的环境变量才能被子进程看到。
兜底分支:如果语料里只有视频、没有其他文档/代码可供提炼领域信息,则使用通用兜底提示词:
Use proper punctuation and paragraph breaks.
该兜底串在源码中同样存在,定义于 transcribe.py(_FALLBACK_PROMPT),并在 build_whisper_prompt() 中作为无 god node 时的返回值。
环境变量速查表
| 环境变量 | 默认值 | 作用 | 源码位置 |
|---|---|---|---|
GRAPHIFY_WHISPER_MODEL | base | Whisper 模型名,须 export 供子进程读取 | transcribe.py |
GRAPHIFY_WHISPER_PROMPT | Use proper punctuation and paragraph breaks. | Whisper 初始提示词(领域提示),优先级高于自动拼接 | transcribe.py |
GRAPHIFY_OUT | graphify-out | 输出目录根,转写产物默认落在其下 transcripts/ | paths.py |
四、Step 2 - 转写命令完整拆解
文档给出了可直接复制的 shell 命令,这里逐段拆解(原样继承,并标注每条参数的意义):
# 模型选择:默认 base;若用户传入 --whisper-model <name>,则必须 export 该值
export GRAPHIFY_WHISPER_MODEL=base
# Step 1 中由你撰写并导出的领域提示词
export GRAPHIFY_WHISPER_PROMPT="<the one-sentence domain hint you composed in Step 1>"
# 使用 detect 阶段写下的解释器路径启动子 Python,避免依赖冲突
$(cat graphify-out/.graphify_python) -c "
import json, os, sys
from pathlib import Path
from graphify.transcribe import transcribe_all
# 读取 detect 产物,仅取 video 分类
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.')
# 批量转写,返回每个文件的 .txt 转写稿路径
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)
"
解读几个工程细节:
$(cat graphify-out/.graphify_python):detect阶段(runbook Step 1/2)会把正确的 Python 解释器路径写入该文件,后续所有 bash 块都用它替换裸python3,保证与detect处于同一解释器环境(见 skill-trae.md 的约束)。- JSON 必须由 Python 写:文档注释与代码注释(
transcribe.py的print使用flush=True)都指向 issue #1392——若用 shell 的>重定向捕获子进程 stdout,Whisper 的进度输出会混入 JSON 导致文件损坏;因此在 Python 进程内完成写文件,进度信息显式打到stderr。 - 空转写产物依旧落盘:即便
video_files为空,也会写出{"..."}形式的 JSON(空列表),保证下游读取方行为一致。
五、转写完成后:把 .txt 并入文档管线
文档规定了转写后的四个收尾动作,缺一不可:
- 从
graphify-out/.graphify_transcripts.json读出每个转写稿路径(即graphify-out/transcripts/<文件名>.txt,见下节输出目录推导); - 在 Step 3B 派发语义子代理之前,把这些
.txt追加进文档列表(即 runbook 中all_files取自('document', 'paper', 'image')三类合并处,转写产物需在派发前并入——见 skill-trae.md 的注释 "Video is transcribed to a document in Step 2.5 first"); - 打印统计:
Transcribed N video file(s) -> treating as docs; - 容错:若单个文件转写失败,打印 warning 后继续处理其余文件,不中断整条流水线。
六、源码级原理:transcribe.py 内部逐层拆解
1) 输出目录与缓存默认行为
_DEFAULT_MODEL = "base"
_TRANSCRIPTS_DIR = str(_out_path("transcripts"))
_out_path 是 paths.py 提供的 Path(GRAPHIFY_OUT, *parts),因此默认转写稿落在 graphify-out/transcripts/<音频文件名>.txt,且能正确兼容 GRAPHIFY_OUT 被设为绝对路径覆盖的场景。
transcribe()(transcribe.py)的缓存逻辑:若 transcripts/<stem>.txt 已存在且未传 force=True,直接返回缓存路径,不触发 Whisper 加载——这正是增量构建的重要优化,避免大模型反复加载。对应测试 test_transcribe.py 的 test_transcribe_uses_cache 与 test_transcribe_force_reruns 分别验证了命中缓存与 force=True 强制重转写两条路径。
2) Whisper 调用参数
真正的推理参数在 transcribe() 内:
model = WhisperModel(model_name, device="cpu", compute_type="int8")
segments, info = model.transcribe(
str(audio_path),
beam_size=5,
initial_prompt=prompt,
)
可确认的实现事实:模型基于 faster-whisper,默认 CPU + int8 量化(无 GPU 也能跑,速度可接受),beam_size=5,领域提示词经 initial_prompt 注入。分段结果逐条 strip() 过滤空段后以换行连接、按 UTF-8 写盘,并回显语言与段数(lang={...}, {len(lines)} segments)。
3) URL 语料:yt-dlp 下载 + 安全校验 + 缓存
is_url() 判定为 URL 时,transcribe() 会先调 download_audio()(transcribe.py)拉取音轨:
- 下载前先经
graphify.security.validate_url()校验,拦截私网 IP 与非法 scheme,再放行给 yt-dlp(防止 SSRF 类风险); - 以 URL 的 SHA-1 前 12 位生成稳定文件名
yt_<hash>.<ext>,规避视频标题过长/含特殊字符导致的问题; - 下载产物缓存在
transcripts/downloads/,命中已有扩展名(.m4a/.opus/.mp3/.ogg/.wav/.webm)直接复用,并打印cached audio: ...; - yt-dlp 选项采用
bestaudio[ext=m4a]/bestaudio/best,noplaylist=True,无需 ffmpeg 后处理(postprocessors: [])。
该能力同样被普通文档摄取复用:graphify/ingest.py 在第 245 行附近直接 from graphify.transcribe import download_audio,说明"URL 转本地音频"是跨流程共享的基础工具。
4) transcribe_all 的批处理容错
transcribe_all()(transcribe.py):
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}")
initial_prompt对所有文件共享(由 god node 一次性生成);- 单个文件抛错仅打 warning、跳过该文件,其余继续;
- 空输入直接返回
[]。
对应测试 test_transcribe_all_skips_failed 用 mock 抛 RuntimeError 验证"失败的输入不会污染返回列表";test_transcribe_all_uses_cache 验证已转写文件秒回缓存路径。
5) build_whisper_prompt:自动化版本的提示词函数
虽然 runbook 让智能体自己造句,源码仍提供了纯函数版 build_whisper_prompt(god_nodes)(transcribe.py)作为兜底实现,逻辑是:
- 无 god node → 返回
_FALLBACK_PROMPT; - 环境变量
GRAPHIFY_WHISPER_PROMPT存在则直接返回(短路,可跳过全部组装逻辑); - 否则取前 10 个 god node 的
label,去空后取前 5 个拼接:Technical discussion about {topics}. Use proper punctuation and paragraph breaks.
test_transcribe.py 用四条用例覆盖了空节点、env 覆盖、正常拼接、无 label 节点跳过四种情形,验证该函数绝不调用 LLM、纯字符串组装即可完成。
七、依赖与安装前置条件
faster-whisper 与 yt-dlp 都是懒加载依赖:只有当语料确实含音视频时才会被 import。若缺失,_get_whisper() / _get_yt_dlp() 会抛出带安装提示的 ImportError(见 transcribe.py),提示原文为:
Video transcription requires faster-whisper. Run: pip install 'graphifyy[video]'
(注:这是模块内错误消息的逐字原文,包含 graphifyy 这一拼写,非本文笔误。)test_transcribe_missing_faster_whisper 专门断言了缺依赖时 ImportError 会原样向上传播,便于上层 catch 后优雅降级——因此,仅当你的语料库含 mp4/mp3 等视听文件时,才需要安装该 extra;纯代码/文档项目完全不受影响。
八、错误排查与注意事项清单
- JSON 文件损坏(#1392):不要用
>把 Python 子进程输出重定向到.graphify_transcripts.json,必须在 Python 内write_text(json.dumps(...)),进度信息输出到stderr。 - 环境变量未生效:
GRAPHIFY_WHISPER_MODEL/GRAPHIFY_WHISPER_PROMPT若仅赋值不export,子 Python 进程将读取不到,导致回落到默认base/ 兜底提示词。可用os.environ.get('GRAPHIFY_WHISPER_PROMPT', ...)的默认值语义反向确认是否注入成功。 - 转写性能:默认
device="cpu", compute_type="int8",长视频耗时显著;已转写的.txt会按文件名缓存,二次运行同一语料直接命中缓存(transcribe.py),重复执行成本极低。 - URL 语料的安全边界:任何
http(s):///www.开头的"文件"都会先过validate_url的私网 IP/非法 scheme 校验,规避把转写工具变成 SSRF 代理。
九、小结
视频/音频是知识图谱语料中的"盲区",graphify 用 Step 2.5 将它们转写为 .txt 文档再汇入语义抽取,从而让一次 /graphify 运行可以同时覆盖代码、文档、PDF、图片与演讲/教学视频。整条链路的关键经验可以总结为三条:其一,让编码智能体依据 god node 标签自行撰写 Whisper 领域提示词并以 export GRAPHIFY_WHISPER_MODEL/GRAPHIFY_WHISPER_PROMPT 传入子进程,省去一次额外 LLM 调用;其二,通过 Python 进程写 JSON 规避 stdout 污染,保证 .graphify_transcripts.json 的原子性;其三,以 graphify-out/transcripts/<name>.txt 为产物并按文件名缓存,天然支持增量重建。若需进一步深入,可继续阅读 transcribe.py 的 download_audio/transcribe/transcribe_all 三个核心函数、test_transcribe.py 的九条行为用例,以及调用该步骤的 skill-trae.md runbook 上下文。
更多推荐
所有评论(0)