graphify 视频音视频转写全流程解析:从 detect 结果到知识图谱文档的 Step 2.5

【免费下载链接】graphify Turn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store. 【免费下载链接】graphify 项目地址: https://gitcode.com/GitHub_Trending/graph/graphify

导读

graphify 的核心能力是把任意语料(代码、文档、PDF、图片)转化为可查询的知识图谱,而视频与音频文件无法被直接阅读,必须先把它们转写成文本。本文围绕 graphify 运行手册中的转写参考文档 transcribe.md,完整讲解 "Step 2.5 - Transcribe video / audio files" 的触发条件、Whisper 领域提示词生成策略、可复制的转写命令,以及 graphify-out/.graphify_transcripts.json 与文档合并的下游处理;同时结合 transcribe.pytest_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 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.(见 skill-trae.md

要点非常明确:

  • 按需加载transcribe.md 只在 detect 报告存在一个及以上 video 文件时才被读取;纯文本/代码语料永远不会读到它,也就不会触发额外依赖下载。
  • 转写是"文档化"前奏:视频/音频被转写为 .txt 后,这些 .txt 在 Step 3 中被当作普通 doc 文件参与语义子代理(semantic subagents)的实体与关系抽取,最终成为图谱节点。
  • 单一事实来源:被转写的文件清单来自 graphify-out/.graphify_detect.jsonfiles.video 数组,而不是让智能体再次扫描目录。

此外,references/transcribe.md 在所有 skill 变体(如 agentsclaudekirowindows 等)下均有同构副本,本文以其 Trae 变体为讲解主体。

二、什么时候触发:video 文件的判定范围

模块级扩展名白名单定义在 transcribe.py

VIDEO_EXTENSIONS = {'.mp4', '.mov', '.webm', '.mkv', '.avi', '.m4v',
                    '.mp3', '.wav', '.m4a', '.ogg'}

detect 会把上述格式的文件归入 files.video 分类。test_transcribe.pytest_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 调用:

  1. graphify-out/.graphify_detect.json(或上一次运行遗留的 .graphify_analysis.json)读取 god node(图神节点)标签;
  2. 依据这些标签写出一句领域提示词,例如:
    • 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."
  3. 将其 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_MODELbaseWhisper 模型名,须 export 供子进程读取transcribe.py
GRAPHIFY_WHISPER_PROMPTUse proper punctuation and paragraph breaks.Whisper 初始提示词(领域提示),优先级高于自动拼接transcribe.py
GRAPHIFY_OUTgraphify-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.pyprint 使用 flush=True)都指向 issue #1392——若用 shell 的 > 重定向捕获子进程 stdout,Whisper 的进度输出会混入 JSON 导致文件损坏;因此在 Python 进程内完成写文件,进度信息显式打到 stderr
  • 空转写产物依旧落盘:即便 video_files 为空,也会写出 {"..."} 形式的 JSON(空列表),保证下游读取方行为一致。

五、转写完成后:把 .txt 并入文档管线

文档规定了转写后的四个收尾动作,缺一不可:

  1. graphify-out/.graphify_transcripts.json 读出每个转写稿路径(即 graphify-out/transcripts/<文件名>.txt,见下节输出目录推导);
  2. 在 Step 3B 派发语义子代理之前,把这些 .txt 追加进文档列表(即 runbook 中 all_files 取自 ('document', 'paper', 'image') 三类合并处,转写产物需在派发前并入——见 skill-trae.md 的注释 "Video is transcribed to a document in Step 2.5 first");
  3. 打印统计:Transcribed N video file(s) -> treating as docs
  4. 容错:若单个文件转写失败,打印 warning 后继续处理其余文件,不中断整条流水线。

六、源码级原理:transcribe.py 内部逐层拆解

1) 输出目录与缓存默认行为

_DEFAULT_MODEL = "base"
_TRANSCRIPTS_DIR = str(_out_path("transcripts"))

_out_pathpaths.py 提供的 Path(GRAPHIFY_OUT, *parts),因此默认转写稿落在 graphify-out/transcripts/<音频文件名>.txt,且能正确兼容 GRAPHIFY_OUT 被设为绝对路径覆盖的场景。

transcribe()transcribe.py)的缓存逻辑:若 transcripts/<stem>.txt 已存在且未传 force=True,直接返回缓存路径,不触发 Whisper 加载——这正是增量构建的重要优化,避免大模型反复加载。对应测试 test_transcribe.pytest_transcribe_uses_cachetest_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/bestnoplaylist=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)作为兜底实现,逻辑是:

  1. 无 god node → 返回 _FALLBACK_PROMPT
  2. 环境变量 GRAPHIFY_WHISPER_PROMPT 存在则直接返回(短路,可跳过全部组装逻辑);
  3. 否则取前 10 个 god node 的 label,去空后取前 5 个拼接: Technical discussion about {topics}. Use proper punctuation and paragraph breaks.

test_transcribe.py 用四条用例覆盖了空节点、env 覆盖、正常拼接、无 label 节点跳过四种情形,验证该函数绝不调用 LLM、纯字符串组装即可完成。

七、依赖与安装前置条件

faster-whisperyt-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.pydownload_audio/transcribe/transcribe_all 三个核心函数、test_transcribe.py 的九条行为用例,以及调用该步骤的 skill-trae.md runbook 上下文。

【免费下载链接】graphify Turn any codebase, with its docs, SQL schemas, configs, and PDFs, into a queryable knowledge graph. A /graphify skill for Claude Code, Cursor, Codex, and Gemini CLI: local deterministic AST parsing, every edge explained, no vector store. 【免费下载链接】graphify 项目地址: https://gitcode.com/GitHub_Trending/graph/graphify

Logo

北京人形旗下天工造物具身智能开源社区,聚焦具身天工与慧思开物两大平台

更多推荐