2026/10/10 12:31:49

用DeepSeek与Codex打造AI填词PV:完整工程实践

用DeepSeek与Codex打造AI填词PV:完整工程实践 这篇文章想和你分享一个比较有意思的 AI 创作项目标题是《来起舞吧 李文亚教授特供版填词 PV》技术侧标注了 Codex CLI 与 DeepSeek 两套工具落地的产品则是一段带歌词字幕的歌曲视频。这类项目放在过去可能需要词作者、字幕剪辑、视频压制多人协同今天把流程拆开之后很多环节可以由大模型和脚本工具辅助完成质量关口仍然需要人来控制。如果是第一次接触“填词 PV”可能会把它理解为普通字幕视频。其实它和“字幕压制”有一个本质区别普通字幕是翻译现有歌词而填词 PV 需要基于一段原曲旋律重新创作歌词再把歌词逐句放到正确的音乐位置。这个过程中既要考虑语义、押韵又要考虑音节数量和情绪走向建模的复杂度不低。下面我把完整流程整理成一份可复用工程实践适合有一定 Python 基础、对 AI Agent 工具链感兴趣的同学。1. AI 填词 PV 项目到底在做什么1.1 什么是填词 PVPV 在视频语境里经常指 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内容如下版本号可以按实际环境调整openai1.0.0 python-dotenv1.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_KEYsk-your-key DEEPSEEK_BASE_URLhttps://api.deepseek.com DEEPSEEK_MODELdeepseek-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_KEYsk-your-key DEEPSEEK_BASE_URLhttps://api.deepseek.com DEEPSEEK_MODELdeepseek-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_keyos.getenv(DEEPSEEK_API_KEY), base_urlos.getenv(DEEPSEEK_BASE_URL, https://api.deepseek.com), ) def build_messages(): # 读取静态系统提示词 with open(prompt/lyric_system.txt, encodingutf-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( modelos.getenv(DEEPSEEK_MODEL, deepseek-chat), messagesbuild_messages(), response_format{type: json_object}, temperature0.8, ) return json.loads(resp.choices[0].message.content) if __name__ __main__: result request_lyrics() os.makedirs(output, exist_okTrue) with open(output/lyrics.json, w, encodingutf-8) as f: json.dump(result, f, ensure_asciiFalse, indent2) 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(pathoutput/lyrics.json): with open(path, encodingutf-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, encodingutf-8) as f: json.dump(timeline, f, ensure_asciiFalse, indent2) 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_pathoutput/timeline.json, output_pathoutput/subtitle.ass): with open(timeline_path, encodingutf-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, encodingutf-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(fDialogue: 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 scale1920:1080,subtitlesoutput/subtitle.ass:force_styleFontNameMicrosoft YaHei,FontSize72 \ -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输入音频文件。subtitlesoutput/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或者系统缺少对应中文字体。在生成字幕脚本中使用encodingutf-8写入文件在 FFmpeg 字幕滤镜中指定系统中存在的字体。如果字体文件是 .ttc 格式可以直接写字体名不必写文件路径。对于带 BOM 的 UTF-8FFmpeg 也能正常处理但如果确实遇到 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 视频项目经验。