引用:源 → 转录稿 → 引文 → 提示与片段
工具:agent-citations/citekit.py(标准库 Python 加上 mlx_whisper 和 ffmpeg)。它永远不会覆盖输入文件,并且在没有 --force 参数的情况下拒绝替换自己的输出。
批次目录结构
每个主题一个文件夹,位于 active_edits/<主题>/ 下:
sources/video_sources.txt 用户的 URL 列表
sources/audio/<id>.m4a .info.json 来自 yt-dlp(步骤 1)
sources/transcripts/<id>.json .md 步骤 2
podcast/<ep>.json .md 我们自己的剧集字幕,已导入(步骤 2b)
sources/quotes.json 步骤 3,由你编写
sources/citations/ 步骤 4 的输出
1. 下载(由用户运行)
YouTube 在此处阻止了匿名 yt-dlp。请将以下命令提供给用户,而不是直接运行它。 它需要 Firefox cookies 和 tv/safari 客户端,并保持音频较小(Whisper 只需要 16 kHz 单声道):
cd active_edits/<主题>/sources && for id in ID1 ID2; do yt-dlp --cookies-from-browser firefox --extractor-args "youtube:player_client=tv,web_safari,mweb" -f "wa/w" -x --audio-format m4a --postprocessor-args "ExtractAudio:-ac 1 -ar 16000" --write-info-json --write-auto-subs --sub-langs en --sub-format json3 --no-overwrites -o "audio/%(id)s.%(ext)s" "https://youtu.be/$id"; done
对于频道的过往视频,先列出列表,然后挑选:yt-dlp --flat-playlist --print "%(id)s %(duration_string)s %(title)s" https://www.youtube.com/@频道名/videos。
如果列表很长,可以使用 Jev(vibe-classification)对标题进行分类。
2. 转录
citekit.py transcribe sources/audio/<id>.m4a:使用 mlx-whisper large-v3-turbo 模型,带词级时间戳。初始提示为视频的标题和描述,这能修正大多数名字的拼写。在此 Mac 上运行速度约为 85 倍实时(一段 15 分钟的音频大约需要 35 秒;模型会下载一次)。citekit.py captions sources/audio/<id>.en.json3:YouTube 的自动字幕。即时生成,但没有标点符号。tv 客户端通常不会保存它们。- 2b. 我们自己的剧集:
citekit.py itt EP.itt --id podcastN --media EP.mp4 --out active_edits/<主题>/podcast --fix 'REGEX=>Right'。 FCP 的字幕会弄乱名字。对于第 1 集:--fix '\bver ?m+[iu]l+e?\b=>Vermeule' --fix '\bvermil\b=>Vermeule' --fix '\bAsian Vermeule=>Adrian Vermeule'。 第 1 集还有词级修正记录在podcast/podcast1.fixes.json中(例如 Schmidt→Schmitt 等),重新导入时会回放。
随时修正误听(不要先问)
当你发现 确定的 误听(例如名字、混乱的引文、Whisper 因口吃而重复的词语)时,请修正它:
citekit.py fix TRANSCRIPT '旧词=>新词' [...] [--near SECONDS]。它会就地编辑 <id>.json/.md 文件(新词共享旧时间段的计时,匹配可以跨段进行,末尾的标点符号会保留下来),将未修改的引擎输出保存为 <id>.raw.json,并将每次修正记录在 <id>.fixes.json 中。transcribe 和 itt 命令会回放该日志,因此修正能在重新运行时保留。不确定?请重新听取该片段,而不是猜测:
ffmpeg -ss T -to T2 -i audio.m4a x.wav && mlx_whisper x.wav --output-format txt。先修正,再引用修正后的词语:locate 命令会根据修正后的转录稿进行匹配。
3. 挖掘引文(需要你的判断)
- 如果有我们的剧集转录稿,请阅读它,并列出主持人关于该主题提出的观点,每个观点附上其在剧集中的时间。这些时刻就是片段可以支持或反驳的论点。
- 对于每个观点,使用
citekit.py search '正则表达式' sources/transcripts/来寻找候选内容,然后阅读命中结果周围的.md文件。也要通读整个讲座:最好的台词并不总是主持人提到的那些。 - 一个好的引文应该简短(说话时长 5–25 秒)、自包含、明确是发言者本人的立场(而不是他总结对手的观点),并且可以放在底部三分之一处。请保留转录稿中的原词。不要清理它们,否则
locate命令将无法找到它们。 - 编写
sources/quotes.json:
{"project": "Ep1 Vermeule 引用", "fps": 29.97, "episode": "../podcast/podcast1.json",
"quotes": [{"id": "publius-weak-exec", "source": "Bw9M01S8MFI", "approx": 1234,
"text": "转录稿中的原话",
"episode_at": 2575, "episode_context": "主持人谈论汉密尔顿和罗马独裁官",
"why": "他本人对论点的陈述", "tags": ["executive", "Federalist"],
"citation": {"author": "~Adrian Vermeule", "title": "The Publius Paradox", "date": "2018年6月5日",
"publication": "Chorley Lecture, LSE"}}]}
episode(相对于 quotes.json 的路径)是我们剧集的转录稿。使用它,每个引文的 episode_at(大致是主持人提出观点的时间)就成为一个插入点:即该时间点正在运行的主持人句子的结尾。
引用完整的思路。如果下一句完善了画面或论点,请将其包含在内(目标是一个 5–25 秒的片段,不是硬性限制)。
citation 默认为视频标题、上传日期和频道。请在顶层 "sources": {"<id>": {"citation": {...}}} 中为每个讲座设置一次真实的演讲地点和日期(来自视频描述)。引文自己的 citation 会覆盖该默认值。作者名字前面加上节目的 ~ 前缀,而 \n 会将长标题分行显示。
检查他 引用的 任何内容(汉密尔顿、卡夫卡、《学说汇纂》)是否与原文一致。Whisper 会听到似是而非的错误词语:在 Publius 讲座中,它将卡夫卡的“牺牲的祭品”(原文为"sacrificial pitchers")听成了“图片”("pictures")。
4. 提示与片段
citekit.py cues sources/quotes.json 会将每个引文对齐到词级时间戳(模糊匹配;如果匹配分数低于 0.8 则会发出警告),并写入 sources/citations/:
| 文件 | 用途 |
|---|---|
citations.md | 按剧集顺序整理的审阅表:插入时间(记录时间和更早插入后时间)、就在切换前主持人的话、引文、来源/片段时间、YouTube ?t= 链接 |
clips.json | 供 AI 剪辑使用:来源入/出点、填充后入/出点(默认每边 5 秒,通过 --pad-before/--pad-after 可配置,每个引文可设置 pad_before/pad_after)、insert_at、insert_at_after_earlier_inserts、insert_after |
citation_cues.json | 供 agent-motion/titles_fcpxml.py 使用:针对已插入片段后的剧集定时生成的引用 v2 底部三分之一字幕,在他开始说话时出现。未放置的引文会首尾相连排列 |
download_clips.sh | 用户运行它:仅下载这些填充范围的全质量视频(使用 --download-sections) |
然后执行 citekit.py audio-clips sources/citations/clips.json 从本地音频中剪切出带填充的音频预览,以便通过耳朵检查。若要在 FCP 中放置底部三分之一字幕,请遵循 motion-graphics 技能的说明:
python3 agent-motion/titles_fcpxml.py sources/citations/citation_cues.json -o citations.fcpxml。
备注
-
transcribe会自行解码为 16 kHz WAV,并拒绝解码后长度远短于视频的音频。使用不同-f参数恢复的 yt-dlp 下载会将两种编解码器拼接到一个 .m4a 文件中(2026-10-18 日观察到:先是 AAC-LC 然后是 HE-AAC),而 ffmpeg 会无声地丢弃切换后的所有内容。请使用--no-continue重新下载。切勿在不同格式之间恢复下载。 -
请通过耳朵验证片段,或者低成本验证:
mlx_whisper citations/audio_clips/<id>.m4a --output-format txt应该能显示引文以及几秒的前导和尾随空白。 -
Whisper 的时间戳在词级上精确到约 ±0.3 秒。这就是片段填充存在的意义。FCP 的 iTT 字幕仅为整行计时,因此
itt会将词语均匀分布在每行中。 -
Wispr Flow 连接器仅能读取会议记录器转录稿。它不能转录文件。