这篇文章想和你分享一个比较有意思的 AI 创作项目,标题是《来起舞吧 李文亚教授特供版填词 PV》,技术侧标注了 Codex CLI 与 DeepSeek 两套工具,落地的产品则是一段带歌词字幕的歌曲视频。这类项目放在过去,可能需要词作者、字幕剪辑、视频压制多人协同;今天把流程拆开之后,很多环节可以由大模型和脚本工具辅助完成,质量关口仍然需要人来控制。
如果是第一次接触“填词 PV”,可能会把它理解为普通字幕视频。其实它和“字幕压制”有一个本质区别:普通字幕是翻译现有歌词,而填词 PV 需要基于一段原曲旋律重新创作歌词,再把歌词逐句放到正确的音乐位置。这个过程中既要考虑语义、押韵,又要考虑音节数量和情绪走向,建模的复杂度不低。下面我把完整流程整理成一份可复用工程实践,适合有一定 Python 基础、对 AI Agent 工具链感兴趣的同学。
1. AI 填词 PV 项目到底在做什么
1.1 什么是填词 PV
PV 在视频语境里经常指 Promotion Video 或 Music Video,可以简单理解成“歌曲视频”。填词 PV 则是在原有曲目的旋律基础上,重新写入一套新的歌词,再配上字幕、背景图和转场效果,最终渲染成一条新的视频文件。
项目标题里的“李文亚教授特供版”,体现的是这类作品最常见的场景:为特定的人定制内容。可能是一次节日赠礼、一份纪念视频,也可能是学生或粉丝群体给老师/偶像做的创意作品。因为收件人明确,所以从歌词风格、文案称呼到字幕样式,都要围绕这个人来定制。这也决定了工程上不能只跑一遍模型就交付,而是需要多轮修改和人工校验。
从技术角度看,填词 PV 的制作链路并不复杂,核心动作只有几步:
- 分析原曲结构和总时长。
- 生成符合旋律节奏的中文歌词。
- 把歌词按句切分,并确定每句出现的时间段。
- 生成字幕文件,例如 ASS 或 SRT 格式。
- 把背景图、音频、字幕一起用 FFmpeg 渲染输出。
整个链路如果不借助 AI,最大的成本在填词环节,因为写出来的词必须音节数量合适、押韵自然、语义通顺。而 DeepSeek 这类大模型比较擅长中文文本生成,正好可以承担歌词初稿工作;Codex CLI 则可以充当“任务编排者”,帮我们把写脚本、调参数、跑命令的过程串联起来。
1.2 项目里的 Codex 与 DeepSeek 分别负责什么
项目标题写的是“Codex/Deepseek harness”,这里的 harness 可以理解成一个轻量级的任务框架,用来把模型能力封装成稳定、可重复的流水线。在本文的实践里,我会把 harness 落地成两层:
第一层是“任务编排层”。由 Codex CLI 承担,它的职责是理解项目目标、生成或修改 Python 脚本、执行命令、检查输出。你不需要让它直接写歌词,而是让它当一个能帮你操作工程文件的“编程助手”。
第二层是“内容生成层”。由 DeepSeek API 承担,它的职责是接收填词提示词,输出结构化歌词数据。因为中文歌词创作更看重语义、押韵和意境,把生成任务交给专门的自然语言模型会更合适。
这种分工在工程上有很多好处。模型输出不稳定是常态,如果把歌词生成逻辑和字幕渲染逻辑混在一起,一旦某次返回格式异常,整个流程都要重跑。分层之后,Codex CLI 负责处理异常、改脚本、重试,DeepSeek 只需要专注于“生成歌词文本”这一件事。
1.3 本文适合哪些读者
这篇文章不是纯理论介绍,也不是晒作品的分享帖。我会尽量把代码、配置和操作流程都写清楚,读完你可以得到几样东西:
- 一套可以本地运行的最小实现,包含 DeepSeek 填词客户端、时间轴生成器、ASS 字幕生成器和 FFmpeg 渲染脚本。
- 一套提示词设计思路,方便你把“普通填词”改成“特供版”。
- 一份常见问题清单,比如 API 返回格式异常、字幕时间轴偏移、中文渲染乱码等。
- 一组工程建议,帮助你在真实项目中控制质量与版权风险。
如果你是零基础,建议先掌握 Python、JSON、FFmpeg 基础概念后再来阅读;如果你已经写过脚本,可以直接跳到第 4 节看完整示例。
2. 环境准备与版本说明
2.1 运行环境与依赖工具
在开始之前,先确认本地环境满足下面这些条件。不同机器上的软件版本可能会有差异,示例以常见环境为准,重点演示配置思路,不必追求所谓“最新版本”。
| 工具 | 用途 | 建议说明 |
|---|---|---|
| Python 3.10+ | 运行生成歌词、生成字幕的脚本 | 需要支持f-string、match等语法 |
| FFmpeg | 合成视频 | 需要带libx264和aac编解码支持 |
| DeepSeek API Key | 调用大模型生成歌词 | 参考官方文档获取并配置环境变量 |
| Codex CLI | 辅助执行脚本、修改代码 | 可选,如果只想跑流程可以忽略 |
| 中文字体文件 | 字幕渲染 | 例如 “微软雅黑”、“思源黑体”,避免中文乱码 |
本文的所有命令默认在 Windows 10/11 或 macOS/Linux 终端中执行。FFmpeg 建议直接安装到系统全局路径,这样ffmpeg -version命令可以在任意目录下正常执行,否则后面调用时会报“command not found”。
2.2 安装 Python 依赖
项目需要用到的第三方库不多,核心是openai和python-dotenv。因为 DeepSeek 的接口兼容 OpenAI SDK,所以我们可以直接使用openai库来发起请求。这样写的好处是:如果后续要切换到其他兼容接口,只需要改环境变量里的base_url和model。
先创建虚拟环境,然后安装依赖。
python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activaterequirements.txt内容如下,版本号可以按实际环境调整:
openai>=1.0.0 python-dotenv>=1.0.0安装命令:
pip install -r requirements.txt2.3 创建项目目录和配置变量
为了不让乱七八糟的文件堆在根目录,建议把不同内容放进独立目录。下面是一个比较清晰的项目结构:
dance-pv-project/ ├── assets/ │ ├── music.mp3 # 原曲音频 │ └── cover.png # 背景图或封面 ├── config/ │ └── pv_config.json # 项目配置:歌曲、收件人、风格 ├── prompt/ │ └── lyric_system.txt # 填词系统提示词 ├── scripts/ │ ├── generate_lyrics.py # 调用 DeepSeek 生成歌词 │ ├── build_timeline.py # 生成歌词时间轴 │ ├── build_ass.py # 生成 ASS 字幕文件 │ └── render_pv.sh # 使用 FFmpeg 渲染输出 ├── output/ │ ├── lyrics.json │ ├── timeline.json │ ├── subtitle.ass │ └── final_pv.mp4 ├── .env.example ├── requirements.txt └── README.md.env.example用来管理敏感配置,例如 API Key。项目中直接用.env文件保存真实值,并把.env加入.gitignore,避免密钥被提交到版本库。
DEEPSEEK_API_KEY=sk-your-key DEEPSEEK_BASE_URL=https://api.deepseek.com DEEPSEEK_MODEL=deepseek-chat PROJECT_RECIPIENT=李文亚教授 PROJECT_SONG_NAME=来起舞吧 PROJECT_STYLE=轻快、励志、致敬创建好结构和配置之后,就开始进入核心链路。
3. 核心链路拆解:从填词到成片
3.1 一条完整的制作主线
填词 PV 不是单次模型调用就能完成的。为了让最终输出可控,我会把整条流程拆成三步,每一步都产生中间文件。
第一步,准备歌词素材。把“歌曲名、收件人、风格、长度、情绪”等信息写成结构化配置,让 DeepSeek 在明确的约束下生成歌词。如果一次生成的效果不理想,可以反复调整提示词,直到歌词的押韵和意境过关。
第二步,生成时间轴。拿到歌词列表后,根据歌曲总时长和句子数量,为每一句分配一个起止时间。这里最容易出现问题的是“日常说话表达和演唱节奏不一致”,所以需要人工标注或额外做节拍检测,不能完全依赖平均切分。
第三步,生成字幕并渲染。把时间轴转成 ASS 字幕,再通过 FFmpeg 把背景图、音频和字幕合成到同一个视频文件中。字幕格式、字体、延迟等都在这一阶段处理。
整个流程可以用下面这串步骤概括:
原曲分析 → 歌词生成 → 分句 → 时间轴分配 → 字幕生成 → 视频渲染 → 人工审核3.2 给 DeepSeek 的提示词设计思路
填词效果好不好,提示词比模型参数更重要。一个好的填词提示词至少要包含:任务目标、原曲情绪、音节限制、押韵要求、输出格式。
所谓“特供版”,本质上是把“收件人”和“场合”注入到提示词里。比如给李文亚教授做视频,系统提示词里可以写:
你正在为《来起舞吧》这首歌创作中文填词,收件人是一位教授。 整体风格需要传递尊重、励志和轻松感。 歌词内容不要使用口语化网络梗,要适合带有纪念性质的祝福场景。输出格式也要提前约束好,因为后续脚本需要读取 JSON 数据。如果 API 返回了多余的解释文字,脚本解析就会失败。比较稳妥的方式是在用户消息中明确要求:
只输出 JSON,不要输出额外说明。 JSON 格式示例: {"title": "来起舞吧", "lyrics": [{"line": "歌词句子", "syllables": 8}]}3.3 音节数量与原曲节奏的关系
中文填词与英文填词最大的区别在于:中文讲究一字一音,歌词需要和旋律的音符数量大致匹配。比如某一个乐句里旋律是 8 个音符,那一句歌词最好不要少于 6 个字,也不要超过 10 个字,否则唱出来会很别扭。
工程上最简单的方式是在提示词里让模型写清楚每个句子的大致字数,随后脚本根据字数做校验,超出范围时打一个警告。这样虽然不能保证完全精准,但能避免“一句 20 个字,下一句 3 个字”这种肉眼可见的问题。
我在示例脚本中会输出每句的syllables字段,方便后续做统计。如果你的原曲节拍非常严格,建议先用人工方式把每段旋律的拍点数标记出来,再把这些拍点数作为上下文数据交给模型,而不是让模型完全自由发挥。
3.4 字幕格式与时间轴的本质
常见的字幕格式有 SRT 和 ASS 两种。SRT 结构简单,适合普通翻译字幕;ASS 支持样式控制、位置调整和更复杂的特效,做填词 PV 时更推荐 ASS。
一行 ASS 字幕核心由时间码和文本组成,例如:
Dialogue: 0,0:00:01.00,0:00:04.00,Default,,0,0,0,,来起舞吧其中0:00:01.00是开始时间,0:00:04.00是结束时间。时间轴生成器要做的就是把这些数值算正确,尤其是处理毫秒与百分秒的转换,这也是很多同学容易踩坑的地方。ASS 时间码使用的是百分秒,而 Python 常用浮点数秒,转换时要小心取整。
4. 完整实战案例:生成“来起舞吧”填词 PV
这一节直接给出可以复制的代码。出于演示目的,这里的项目名和歌曲名都使用标题中的名称,如果你要复用,把配置项替换成自己的歌曲即可。
4.1 创建项目结构
在终端中执行下面命令,生成目录:
mkdir -p dance-pv-project/{assets,config,prompt,scripts,output} cd dance-pv-project然后把assets/music.mp3和assets/cover.png放入对应目录,cover.png可以是 1920x1080 的封面图,最终视频会以此为静态画面。
4.2 配置.env和提示词
创建.env文件:
DEEPSEEK_API_KEY=sk-your-key DEEPSEEK_BASE_URL=https://api.deepseek.com DEEPSEEK_MODEL=deepseek-chat PROJECT_RECIPIENT=李文亚教授 PROJECT_SONG_NAME=来起舞吧 PROJECT_STYLE=轻快、励志、致敬创建prompt/lyric_system.txt:
你是一名中文填词人,擅长为已有旋律创作贴合原曲情绪的新词。 本次填词的项目信息如下: - 收件人:李文亚教授 - 曲目名称:《来起舞吧》 - 风格:轻快、励志、有祝福感 - 语言:中文 - 注意:歌词要朗朗上口,尽量押韵,每句话的字数在 6 到 12 字之间。 请根据原曲的乐句长度,按段落输出歌词,并标明每句的字数。 输出要求:只输出 JSON,不输出额外解释。 JSON 格式: {"title": "来起舞吧", "lyrics": [{"line": "这里是歌词", "syllables": 8}]}这里要注意:PROJECT_RECIPIENT虽然存在于环境变量中,但系统提示词是静态文件,模型并不知道这个变量。实际开发中可以在generate_lyrics.py里读取环境变量并动态拼接提示词,避免每次修改.txt文件。
4.3 编写 DeepSeek 歌词生成脚本
创建scripts/generate_lyrics.py:
# scripts/generate_lyrics.py import json import os from openai import OpenAI from dotenv import load_dotenv load_dotenv() client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL", "https://api.deepseek.com"), ) def build_messages(): # 读取静态系统提示词 with open("prompt/lyric_system.txt", encoding="utf-8") as f: system_prompt = f.read() user_prompt = ( "请根据歌曲《" + os.getenv("PROJECT_SONG_NAME", "来起舞吧") + "》的填词要求,生成完整中文歌词。" "收件人是:" + os.getenv("PROJECT_RECIPIENT", "") + "。" "只输出 JSON。" ) return [ {"role": "system", "content": system_prompt}, {"role": "user", "content": user_prompt}, ] def request_lyrics(): resp = client.chat.completions.create( model=os.getenv("DEEPSEEK_MODEL", "deepseek-chat"), messages=build_messages(), response_format={"type": "json_object"}, temperature=0.8, ) return json.loads(resp.choices[0].message.content) if __name__ == "__main__": result = request_lyrics() os.makedirs("output", exist_ok=True) with open("output/lyrics.json", "w", encoding="utf-8") as f: json.dump(result, f, ensure_ascii=False, indent=2) print("歌词已生成:output/lyrics.json")脚本做了三件事:读取.env配置、把系统提示词和用户请求拼接为聊天消息、解析 DeepSeek 返回的 JSON 结果并写入output/lyrics.json。response_format参数表示让模型尽量返回 JSON 对象,减少解析失败的几率,具体是否支持需要看你的模型和接口版本。
运行:
python scripts/generate_lyrics.py生成的output/lyrics.json大概长这样:
{ "title": "来起舞吧", "lyrics": [ { "line": "晨光点亮了窗台", "syllables": 8 }, { "line": "我们相约向未来", "syllables": 8 } ] }4.4 生成时间轴
这里先实现一个简单版本:假设歌曲总时长为 210 秒,每一句平均分配时间。真实场景中这种平均分配不够准确,但作为最小可用版本,可以先把流程跑通,之后再根据节拍结构调整。
创建scripts/build_timeline.py:
# scripts/build_timeline.py import json SONG_TOTAL_SECONDS = 210.0 def load_lyrics(path="output/lyrics.json"): with open(path, encoding="utf-8") as f: return json.load(f) def build_timeline(lyrics, total_seconds): lines = lyrics.get("lyrics", []) if not lines: raise ValueError("歌词列表为空,无法生成时间轴") segment = total_seconds / len(lines) timeline = [] for i, item in enumerate(lines): start = round(i * segment, 2) end = round((i + 1) * segment, 2) timeline.append({ "index": i + 1, "start": start, "end": end, "text": item.get("line", ""), "syllables": item.get("syllables", 0), }) return timeline if __name__ == "__main__": lyrics = load_lyrics() timeline = build_timeline(lyrics, SONG_TOTAL_SECONDS) with open("output/timeline.json", "w", encoding="utf-8") as f: json.dump(timeline, f, ensure_ascii=False, indent=2) print(f"时间轴生成完成,共 {len(timeline)} 句")运行:
python scripts/build_timeline.py4.5 生成 ASS 字幕文件
创建scripts/build_ass.py:
# scripts/build_ass.py import json def to_ass_time(seconds): """把浮点数秒转换为 ASS 时间码,格式为 H:MM:SS.CC""" seconds = max(0, seconds) h = int(seconds // 3600) m = int((seconds % 3600) // 60) s = int(seconds % 60) cs = int(round((seconds - int(seconds)) * 100)) if cs == 100: s += 1 cs = 0 return f"{h}:{m:02d}:{s:02d}.{cs:02d}" def build_ass(timeline_path="output/timeline.json", output_path="output/subtitle.ass"): with open(timeline_path, encoding="utf-8") as f: timeline = json.load(f) header = """[Script Info] ScriptType: v4.00+ PlayResX: 1920 PlayResY: 1080 [V4+ Styles] Format: Name, Fontname, Fontsize, PrimaryColour, SecondaryColour, OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut, ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow, Alignment, MarginL, MarginR, MarginV, Encoding Style: Default,Microsoft YaHei,72,&H00FFFFFF,&H000000FF,&H00000000,&H80000000,0,0,0,0,100,100,0,0,1,3,0,2,60,60,120,1 """ with open(output_path, "w", encoding="utf-8") as f: f.write(header) for item in timeline: start = to_ass_time(item["start"]) end = to_ass_time(item["end"]) text = item["text"].replace("\n", "\\N") f.write(f"Dialogue: 0,{start},{end},Default,,0,0,0,,{text}\n") if __name__ == "__main__": build_ass() print("字幕已生成:output/subtitle.ass")这个脚本比较关键的是to_ass_time函数,它要处理整数秒和百分秒的进位,避免出现00:00:60.00之类的非法时间码。ASS 字幕中的字体名称依赖系统环境,如果 Windows 没有 Microsoft YaHei,可以改成系统中已有的中文字体名称。
运行:
python scripts/build_ass.py假设歌词有两句,生成的output/subtitle.ass关键内容如下:
Dialogue: 0,0:00:00.00,0:01:45.00,Default,,0,0,0,,晨光点亮了窗台 Dialogue: 0,0:01:45.00,0:03:30.00,Default,,0,0,0,,我们相约向未来注意这只是平均分配的演示数据,真实项目中 1 分 45 秒才出一句会非常奇怪,需要结合歌曲结构手动调整。
4.6 使用 FFmpeg 渲染成视频
创建scripts/render_pv.sh:
#!/usr/bin/env bash set -euo pipefail ffmpeg -y \ -loop 1 -i assets/cover.png \ -i assets/music.mp3 \ -vf "scale=1920:1080,subtitles=output/subtitle.ass:force_style='FontName=Microsoft YaHei,FontSize=72'" \ -c:v libx264 -tune stillimage \ -c:a aac -b:a 192k \ -shortest \ output/final_pv.mp4解释一下参数:
-loop 1:让背景图循环播放。-i assets/cover.png:输入静态背景图。-i assets/music.mp3:输入音频文件。subtitles=output/subtitle.ass:把 ASS 字幕烧录到视频画面中。-tune stillimage:优化静态图像视频编码。-shortest:输出时长以音频和视频中较短者为准,避免无限循环。
如果是在 Windows 终端运行,建议把脚本改为一行式命令,或者使用 PowerShell 转义规则。在 macOS 和 Linux 下,先添加执行权限:
chmod +x scripts/render_pv.sh bash scripts/render_pv.sh等待 FFmpeg 执行完成后,output/final_pv.mp4就是含字幕的填词 PV 视频。
4.7 整体运行与验证
因为每个脚本都会生成中间文件,所以依次执行即可:
python scripts/generate_lyrics.py python scripts/build_timeline.py python scripts/build_ass.py bash scripts/render_pv.sh如果每一步都没有报错,使用视频播放器打开output/final_pv.mp4,检查下面几个点:
- 歌词内容是否和原曲情绪一致。
- 字幕是否在对应时间出现。
- 中文是否正常显示,而不是乱码。
- 音频是否完整,没有截断。
只要有一项不符合预期,回到对应步骤修改配置或重新生成,不需要从头开始。这也是中间文件分层带来的好处。
5. 常见问题与排查思路
5.1 API 调用问题
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 请求超时 | 网络不稳定或接口响应慢 | 检查网络策略,在代码中增加重试机制 |
| 401 认证失败 | API Key 没有配置或已失效 | 核对.env中的DEEPSEEK_API_KEY |
| 返回内容不是 JSON | 提示词约束不够严格 | 在用户消息里强调“只输出 JSON” |
| 歌词风格不对 | 系统提示词缺少有效约束 | 补充场景、对象、情绪、节奏要求 |
如果网络环境存在访问限制,不要把密钥提交到公共仓库,也不要在配置里写死。推荐在每次请求前检查环境变量是否加载成功:
if not os.getenv("DEEPSEEK_API_KEY"): raise RuntimeError("缺少 DEEPSEEK_API_KEY,请检查 .env 文件")5.2 歌词质量问题
歌词是填词 PV 的核心,如果模型生成的词读起来别扭,最直接的方法是修改提示词。下面这些方向可以依次尝试:
- 指定押韵方式,例如“押 a 韵”。
- 指定每段情绪,比如“主歌平稳叙述,副歌昂扬”。
- 把原曲的节奏标记成大括号结构,让模型按段落填词。
- 多跑几次,选出质量最高的一版,再人工替换局部句子。
不建议把模型的第一次输出直接当作最终结果。歌词质量本身是主观问题,模型只能提供候选,最终审核必须由人来完成。
5.3 字幕不同步
常见原因有两种:一是总时长设置错误,二是歌词句数和实际乐句不匹配。通过平均分配时间轴本来就是近似方案,遇到副歌密集或长间奏时,误差会很明显。
改进方向是先把歌曲中出现的人声段落标记出来,再用脚本按段落分配时间。可以先人工用音频编辑软件标注时间点,然后把时间点写入配置文件:
{ "segments": [ {"start": 0, "end": 8, "lines": 4}, {"start": 8, "end": 20, "lines": 6} ] }再让build_timeline.py读取这个结构,按段落精确分配时间,而不是全曲平均分。这个方案虽然多了一步人工操作,但能显著提升最终效果。
5.4 字幕中文乱码
乱码多半是因为 ASS 文件编码不是 UTF-8,或者系统缺少对应中文字体。在生成字幕脚本中,使用encoding="utf-8"写入文件;在 FFmpeg 字幕滤镜中,指定系统中存在的字体。如果字体文件是 .ttc 格式,可以直接写字体名,不必写文件路径。对于带 BOM 的 UTF-8,FFmpeg 也能正常处理,但如果确实遇到 BOM 导致的乱码,可以改成不带 BOM 的 UTF-8 保存。
5.5 视频渲染失败
FFmpeg 报错原因非常多。常见的是libx264编码器不存在,或者subtitles滤镜不可用。如果是精简版 FFmpeg,可能缺少字幕库,建议安装完整版本。测试时可以先渲染一个 5 秒的小片段,确认参数没问题后再渲染完整视频。
6. 最佳实践与工程建议
6.1 把模型输出当候选,不直接当交付物
无论 DeepSeek 生成多漂亮的歌词,它只是初稿。生产交付前至少要做一遍人工审校,重点检查语义是否通顺、是否有不合适的用词、是否匹配收件人身份。给特定教授的视频尤其要注意称呼和措辞,不能用网络梗,也不能过于随意。
工程上可以为每个版本的歌词保留文件,例如lyrics_v1.json、lyrics_v2.json,方便对比和回滚。不要直接覆盖原始文件,否则改坏之后很难找回来。
6.2 提示词要沉淀成资产
优秀的提示词不应该只写在终端里。prompt/lyric_system.txt这种文件应该当成项目资产管理。可以把不同风格的提示词分类保存:
- 正式致敬风格。
- 轻松幽默风格。
- 励志成长风格。
- 古风押韵风格。
这样以后再接到其他定制项目,可以直接复制提示词模板,只修改收件人和曲目信息,效率会高很多。
6.3 版权与合规风险
填词 PV 属于二次创作,必须警惕版权问题。原曲的旋律、编曲、录音都可能受版权保护。如果只是私下赠给老师,问题不大;如果发布到公开平台或者用于商业用途,需要确认原曲的授权范围。
建议在视频简介中明确标注:本视频为同人创作,仅用于学习交流,原曲版权归原作者所有。同时不要用带有版权风险的画面素材,背景图尽量使用自己制作的图片,或者使用可商用授权的素材。AI 生成的歌词同样存在版权争议,发布前最好确认平台对 AI 生成内容的规则。
6.4 工程规范建议
代码层面,尽量把每个环节写成独立命令,避免一个脚本做所有事。这样某一步失败时,可以只重跑那一步。日志输出也很重要,建议在每个脚本里打印关键信息,例如生成了几条歌词、写入哪个文件。
配置层面,不要把歌曲名、收件人、时间长度写死在代码里。统一放到.env或config/pv_config.json中,这样换一首歌时只需改配置,不需要动代码。
安全层面,API Key 只能放在服务端或本地环境变量中,绝不能出现在前端页面或公开仓库。如果使用 Git 管理项目,记得把.env加入.gitignore。
7. 总结与学习路线
这一套流程跑完,你已经实现了最基本的“DeepSeek 生成歌词 + Codex/脚本编排 + FFmpeg 渲染”工作流。整个项目虽然规模不大,但它覆盖了 AI 视频创作中非常典型的一条链路:模型生成内容,脚本处理数据,工具完成渲染,人工控制质量。
进一步学习可以从几个方向深入:
- 自动节拍检测:用音频处理库分析 BPM 和乐句边界,代替人工平均分句。
- 卡拉 OK 字幕效果:在 ASS 中实现逐字变色或波浪形歌词,提高 PV 表现力。
- 多模型协作:把歌词生成、字幕翻译、封面绘图分别交给不同模型,形成多 Agent 管线。
- 更完整的 Harness 封装:把 Codex CLI 的执行过程固化为可复用的任务描述文件,让每个项目都能一键重跑。
不同模型和工具版本变化比较快,本文的示例偏工程思路而非官方 API 文档,实际使用中建议以你手中版本为准。如果你在运行过程中遇到新的报错,优先检查配置项和依赖版本,再对照这条链路逐步定位问题。希望这篇填词 PV 工程拆解对你有帮助,也欢迎在评论区交流你的 AI 视频项目经验。