首页
/ graphify 视频与音频转录指南:在 Step 2.5 用 Whisper 将音视频语料转成可建图文本

graphify 视频与音频转录指南:在 Step 2.5 用 Whisper 将音视频语料转成可建图文本

2026-09-07 09:55:46作者:翟江哲Frasier

视频与音频文件无法被直接读取为文本,因此 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 when detect reported one or more video files”)。
  • 检测到视频时,先转录成文本,再在 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.pydetect() 函数,见 detect.py),其返回结构包含 files(按 code/document/paper/image/video 分桶)、total_filestotal_wordsskipped_sensitivescan_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.pyvalidate_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.pyout_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 彻底隔离。

转录完成之后:把转录稿接回文档流水线

参考文档明确了转录成功后的四个收尾动作,它们共同保证转录稿真正进入下游抽取:

  1. graphify-out/.graphify_transcripts.json 读取全部转录稿路径;
  2. 在派发 Step 3B 语义子 Agent 之前,把这些路径并入 docs 文件列表——转录稿从此与 .md/.txt 等文档同等对待;
  3. 打印产物数量:Transcribed N video file(s) -> treating as docs
  4. 若某个文件转录失败,打印警告并继续处理其余文件(对应源码 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 + 语义抽取 → 图合并/构建

进一步阅读

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.14 K
2.75 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
857
1.35 K
docsdocs
暂无描述
Markdown
898
5.82 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
921
1.84 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.8 K
1.02 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
531
596
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
1.02 K
519
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.36 K
1.46 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
548
391