如果你用过 Manim,大概率有过这种体会:渲染一段函数动画只需要几分钟,但为了想清楚“这段动画到底要展示什么、顺序怎么排、画面里放几条曲线、颜色怎么搭”,可能已经过去两个小时。我在开源项目 math-concept-film 上反复验证了一个判断——数学概念短片生成的瓶颈从来不在渲染引擎,而在“把抽象概念拆解成可执行分镜”的能力。这也是我没有把它做成一个独立命令行工具,而是做成一个 Agent Skill 的原因。它不是一段代码,而是给 AI Agent 装配的“导演 + 动画师 + 剪辑师”三合一技能包。
这套 Skill 的目标很简单:你扔给它一个数学概念,它自动完成脚本撰写、公式推导动画、旁白配音、字幕对齐和视频合成,最终产出一条可以直接放进课堂或 B 站的教学短片。它面向三类人:想批量产出数学可视化内容的老师或视频创作者、研究 Agent 工作流的开发者、以及想在开源社区里找一个“既有落地场景又不过度复杂”的 Agent Skill 做参考的玩家。
1. 数学概念短片的痛点,以及 Agent Skill 为何能解
1.1 数学可视化的“最后一公里”问题
数学里大量概念天然不适合用静态文字表达。导数讲的是变化率和切线逼近,极限讲的是无限趋近,积分讲的是累积求和,拓扑连续讲的是“拉伸不撕裂”的变形——这些东西光靠板书和幻灯片,学生很难建立起直觉。
业内常用的动态可视化方案大致有四类:
| 方案 | 优点 | 短板 |
|---|---|---|
| PPT/Keynote动画 | 上手快,教师普遍会用 | 制作纯手动,复杂运动效果难做,批量产出不现实 |
| GeoGebra | 交互性强,几何、代数联动好 | 偏探索工具,不适合输出完整叙事短片 |
| Manim | 数学动画工业级,3Blue1Brown同款 | 写代码门槛高,分镜和文案还得自己设计 |
| 传统视频剪辑 | 素材自由度大 | 对数学公式渲染无能为力,工作量大 |
我自己见过很多老师的做法:用 PPT 录屏 + 鼠标手写标注,再丢进剪辑软件加字幕。一条五分钟的短片,录制加剪辑至少要四五个小时,而且改一处逻辑就要重录。Manim 解决了“程序化生成动画”的问题,但动画只是短片的一部分,完整成片还需要脚本、旁白、字幕、节奏控制,这些恰恰是最耗精力的环节。
1.2 Agent Skill 到底是什么
Agent Skill 可以理解为一个“专业技能包”。它不是一个独立运行的程序,而是一套标准化的目录结构和一个用于说明能力的文档,AI Agent 拿到这个目录后,就知道自己“会”一门什么样的手艺,以及调用哪些脚本和资源来完成这门手艺。
我常用的类比是:Agent 是一个厨师,Prompt 是他在某个瞬间收到的口头订单,而 Skill 是固定放在厨房抽屉里的菜谱卡 + 刀工训练记录 + 出餐摆盘规范。订单每天在变,但手艺沉淀成了可复用的资产。
一套标准 Skill 通常包含几个部分:
- 一个
SKILL.md,以简洁文字声明适用场景、操作流程、输入输出约定、边界条件和注意事项; - 一组可执行脚本,放在
scripts/下,承担确定的、可重复的环节; - 一些资源模板,比如字体、配色配置、背景音乐、字幕样式;
- 示例产物或测试用例,用于让 Agent 理解“什么叫做完成”。
这个结构与普通 Prompt 最大的区别在于可版本管理、可测试、可组合。Prompt 改一版就丢失了历史轨迹,而 Skill 放进 Git 仓库里,每次迭代都有 diff,整个团队都能看到某个环节为什么调整了。
1.3 为什么“生成数学短片”特别适合 Skill 化
不是所有任务都适合封装成 Skill。判断标准我总结为三条:流程是否可标准化、中间是否有确定性工具兜底、产出是否有明确质量线。
生成数学短片恰好三条都满足。
流程高度标准化:概念拆解、脚本设计、分镜规划、公式渲染、配音、字幕、合成,这一步一步非常固定,不存在展示日常对话那样需要随机应变的场景;中间环节有确定性工具兜底:公式渲染交给 Manim,视频合成交给 ffmpeg,语音交给 TTS,AI 只负责“需要判断力”的部分,不负责“需要精度的部分”;质量线很明确:有一帧画面、有一段音频、有字幕、时长合理,这就是可验收的产物。
我见过不少人想做一个“AI 生成一切”的短片系统,把画面生成也交给扩散模型,结果每条短片时长、画风、公式准确率都不可控。而数学内容是绝对不能出现公式错误的领域。老老实实把 LLM 用在它擅长的地方——拆解、组织、润色、调度,把真正需要精确计算和精确渲染的部分交给传统工具,这个 Skill 才敢真的投入使用。
2. 拆解 math-concept-film 的动作链路:从概念输入到成片输出
2.1 Skill 接收什么形式的任务描述
在设计输入格式时,我最担心的是“看起来像聊天,但实际需要结构化参数”这类接口。所以 math-concept-film 在SKILL.md里明确要求 Agent 在调用前先解析出一个结构化任务对象,包含四个字段:
{ "concept": "导数的几何意义", "audience": "高中学生", "duration": "180秒", "language": "zh-CN" }audience决定了讲解深度,duration决定分镜数量,language决定 TTS 音色和字幕语言。用户给的自然语言描述千奇百怪,比如“给我做一个讲极限 epsilon-delta 的短片,别太难,五分钟左右”,Agent 会先把它归一化为上面的 JSON,再进入流水线。
这个约定很重要。如果让后续每一个环节都直接面对用户的原始描述,任何一点措辞偏差都会被放大。先做结构化,后面的脚本调用就不需要再做二次理解。
2.2 七步生成流水线
整个流水线我分成七个环节,每个环节产出一个中间文件,方便在任何一步人工介入修正:
第一步:概念模型构建。LLM 基于主题词生成一个知识图谱式的大纲,包括前置知识、核心定义、直观例子、常见误解共四个部分。比如讲导数,前置知识是“极限与斜率”,核心定义是“瞬时变化率”,直观例子是“汽车速度表”,常见误解是“导数就是切线”。这个大纲会作为后续所有环节的依据。
第二步:脚本生成。把大纲变成旁白文案,要求口语化、控制语速,避免长难句。数学短片的文案和普通科普文案不太一样,它需要刻意留出“顿悟时刻”。我要求生成时遵循一个节奏模板:抛出问题(15%)→ 直觉铺垫(30%)→ 严格定义(25%)→ 动画演示(20%)→ 总结升华(10%)。
第三步:分镜清单生成。每一句旁白对应一个或多个镜头,输出一个包含scene_id、duration、description、formula、animation_type的列表。这一步是整个 Skill 的智力核心,也是推理成本最高的地方,它决定了画面里放什么、镜头怎么运动、动画节奏怎样。
第四步:公式提取与 LaTeX 化。LLM 从文案中抽取出所有需要渲染的数学表达式,统一转成 LaTeX。这个环节最容易出错,我在 3.1 节会单独展开。
第五步:Manim 渲染。根据分镜清单逐个生成 Manim 场景代码,批量渲染为视频片段。为了控制单次任务的失败率,我严格限制一个场景只对应一个.py文件、只渲染一个画面核心,绝不在一个场景里堆三个动画。
第六步:TTS 配音与字幕时间戳。旁白文本先行送入 TTS 生成音频,同时拿到每个词的起止时间戳,再根据时间戳切分字幕句。
第七步:合成。ffmpeg 将视频片段、完整配音、字幕、片头片尾合并成最终 MP4,并额外导出一份纯字幕的.srt文件。
2.3 每一步的人机分工与兜底逻辑
这七步里,第一、二、三步完全由 LLM 驱动,第四步是 LLM 生成 + 程序校验,第五、六、七步全部由确定性脚本执行。我坚持一个原则:能代码算的绝不让模型猜。
以第四步为例,LLM 给出的 LaTeX 有可能少写花括号、用错数学环境,如果直接喂给 Manim,渲染会失败。我的兜底是:脚本先调用latex编译验算,编译通过才进入渲染,不通过就把错误日志返回给 Agent,让它自己纠正后重试。
第六步同理。TTS 返回的时间戳是按“词”或“子词”切分的,字幕不直接用它按词显示,而是做一个二次对齐,保证每行字幕对应完整的一句话,避免字词间断开造成阅读跳帧。
这种“LLM 决策 + 工具执行 + 脚本校验”的模式,是整套 Skill 能稳定产出的关键。如果你在构建自己的 Agent 工作流,强烈建议先把这条思想固化下来,而不是把每一步都交给模型自由发挥。
3. 关键模块实现细节:公式渲染、镜头节奏与语音合成的落地取舍
3.1 公式解析:从自然语言到 LaTeX 再到 Tex 渲染
公式是整个流水线里出错率最高的点。我遇到过 LLM 把∫写成\int没问题但忘加被积函数、把lim_{x \to 0} sin x / x的分子分母括错导致渲染结果完全变样,等等。
我在SKILL.md里对公式提取做了硬性规定:
- 每个公式单独成行,不与正文混排;
- 所有变量一律使用 LaTeX 数学体,
x不能出现在普通字符串里; - 相对复杂的公式(分式、积分、极限)必须使用
\[ ... \]展示环境,不能写成行内$...$; - 生成公式后必须用一个小脚本做括号配平检查。
落到 Manim 里,核心就是Tex或MathTex类。Manim 0.18 之后对 LaTeX 包管理更严格,需要保证系统里装了完整的 TeX 发行版,并且把宏包路径写对。有一个容易踩的坑是:.gif或高分辨率预览时,MathTex默认字号偏小,放进 1080p 画布会显得很挤。我在渲染前会对所有公式统一做scale(1.2),并设置统一的color=YELLOW作为强调色。
一旦 LaTeX 编译失败,我的降级策略是先试dvisvgm转换,再不行就退化为 Matplotlib 的mathtext渲染高分辨率 PNG,作为静态帧插入视频。这个兜底虽然牺牲了动画效果,但保证了“公式绝对不会因为渲染失败而缺失”。
3.2 镜头与节奏设计:数学短片的“镜头语言”规范
数学动画和普通动画片不一样,没有人物表演,镜头语言只能靠运动方式、缩放节奏和画面元素的新增/消失来组织。我总结了几个硬性规范,写进了 Skill 的提示词里:
- 单个画面停留不超过 8 秒。大脑处理陌生数学符号的负荷很高,超过 8 秒没有任何视觉变化,观众就会走神。8 秒原则是底线,大多数镜头控制在 4 到 6 秒。
- 动线遵循“先整体后局部”。讲函数曲线时,先展示完整坐标系和完整曲线,再用聚焦框拉近到局部区域,不要一上来就放大到某个点,否则观众不知道它在整个问题框架里的位置。
- 变化幅度与语义强度匹配。强调极限逼近时用缓慢匀速逼近,讲“瞬间变化率”时突然切到一个极短的高亮闪帧,这种节奏对照能产生记忆点。
- 用颜色变化表达逻辑变化。切线的颜色变成强调色,就暗示观众“重点来了”;被积函数区域填充色加深,就暗示“面积在累加”。颜色是数学动画里的“音效”。
这套规范不是凭空想出来的,我参照了不少 3Blue1Brown 的经典视频,把它们的共同规律抽出来验证过。新手做数学动画最容易犯的错误是“为了动而动”,镜头一直在旋转缩放,观众反而抓不住信息。克制,比炫技重要。
3.3 配色、字体与排版规范:让短片不出戏的基础
数学短片内容已经很抽象,配色再杂就彻底没法看了。我的默认方案是深色背景 + 单一强调色。背景用深蓝灰(#1E1E2E),主要图形用白色或浅灰,核心概念用高亮黄(#FFD700),错误或对比展示用红色(#EF5350),只允许这四种颜色同时出现在一个画面里。
中文数学短片还有一个特殊问题:字体。Manim 默认的 Latin Modern 字体不支持中文,直接用Text("导数")会渲染成方块。解决方法是先注册系统中文字体,再在Text类里指定font参数:
from manim import * text = Text("导数的几何意义", font="Noto Sans CJK SC", font_size=48, color=WHITE) self.play(Write(text))排版上我有一条不成文的规定:公式和中文文字永远分属画面上半区和下半区,公式居左上或居中,中文注释靠底部,避免视觉抢位。视频平台观众多数在手机上观看,元素间距宁可大一点,也不要挤在一起。
3.4 语音合成与字幕对齐:TTS 选型和预处理
配音环节最核心的指标不是音色好听,而是公式朗读正确率。我对比过多个 TTS 引擎后发现,直接喂 LaTeX 给 TTS 几乎必出问题:\frac{1}{2}会被读成“斜杠 frac 1 斜杠 2”,sin x会被读成“s 加 i 加 n 加 x”甚至“sin 乘以 x”。
正确的做法是预处理:先把 LaTeX 公式翻译成适合朗读的自然语言形式,再送入 TTS。
import re def latex_to_spoken(text): replacements = { r"\\frac\{([^}]+)\}\{([^}]+)\}": r"\1 分之 \2", r"\\sqrt\{([^}]+)\}": r"\1 的平方根", r"\\int": "积分", r"\\lim": "极限", r"\\to": "趋近于", r"sin": "正弦", r"cos": "余弦", r"\\cdot": "乘以", } for pattern, repl in replacements.items(): text = re.sub(pattern, repl, text) return text做完整套替换之后,再由人工或 LLM 复核一遍,确保“积分从 0 到 1”这种有上下限的读法不会被替换规则破坏。这一步很烦琐,但没有捷径,准确率只能靠规则表和抽样试听一点点刷上来。
字幕对齐方面,我要求 TTS 返回词级时间戳,然后按句号、逗号切分,将时间戳映射到句子。对于“导数等于 x 的平方趋近于零时的变化率”这种长句,我会让 LLM 在脚本生成阶段提前加入逗号,避免 TTS 一口气念完导致字幕一行放不下。
4. 实测翻车与参数调优:我修过的六个隐藏问题
开源项目最大的价值在踩坑记录。这里我把开发 math-concept-film 过程中印象最深的六个问题完整写出来,每个都包含现象、定位过程、解法。
4.1 Manim 版本差异:一夜之间 API 全变
最早我用 Manim 0.17,后来升级到 0.18,发现Tex类的字体注册方式变了,原来能跑的代码直接抛异常。排查过程很折磨,因为报错信息非常隐晦:LaTeX error: File 'manim' not found。后来查 issue 才知道新版本强制要求显式声明PACKAGES列表。
解决办法:requirements.txt里锁死版本号,并且把常用宏包配置写进一个manim_config.py,不再依赖 Manim 自带的包发现机制。给后来者的建议是,Math 类动画项目不要频繁追新版本,稳定优先。
4.2 中文字体渲染成方块
第一次渲染“导数的几何意义”标题时,输出视频里的中文全是“□□□□”。定位过程相对快,因为在渲染日志里看到了Missing character: There is no ...... in font。
解决分两步。第一步确认系统中文字体路径:
fc-list | grep -i "noto sans cjk"第二步在 Manim 代码中按字体文件路径注册:
from manim import * font_path = "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc" Text.__init__.__globals__["registered_fonts"] = {} register_font(font_path)这个坑几乎每个做中文 Manim 内容的人都会遇到,好在搜索结果很丰富,不算冷门,但如果你自己写 Skill,一定要把字体检查脚本放进初始化流程。
4.3 复杂公式渲染时间指数级爆炸
处理矩阵求逆动画时,Manim 对每个矩阵元素都做了独立的 LaTeX 渲染,一个 4×4 矩阵加若干动画帧,渲染耗时接近三小时。这个完全不可接受。
我的调优思路是拆分:把矩阵本身渲染成高分辨率静态 SVG,动画只做“放大、位移、高亮某一行”这类简单变换,由SVGMobject加载,避免逐帧重排公式。调优后单个场景渲染时间降到 40 秒,效果几乎无损。核心原则是静态元素和动态元素分开渲染,别让 Manim 反复计算没有变化的部分。
4.4 TTS 把公式读成乱码
我测试“sin x / x 的极限”时,TTS 读出了“s 加 i 加 n 括号 x 除以 x 的极限”。这是预处理漏网的问题。具体原因是我的正则只处理了 LaTeX 形式的\sin,没处理直接写成的sin纯文本。
修复方式是扩充替换表,让正则同时匹配带反斜杠和不带反斜杠的版本,并在送入 TTS 前先用肉眼检查一遍。这里我必须强调:任何规则表都需要一个抽样试听测试集,不能只在接新用例时临时打补丁。
4.5 音画不同步:高频打脸现场
有一次生成的短片里,配音明显比画面快 0.8 秒左右。一开始我以为是 ffmpeg 合成时丢帧,检查半天发现是 Manim 渲染的帧率和 TTS 音频的时间戳基准不一致。Manim 默认 30 帧率,而 TTS 返回的时间戳精确到毫秒,我在做字幕切分时直接用了毫秒值,但视频时间轴用的是帧计数,两套时间基没有对齐。
解决方式是在合成前统一换算:
ffmpeg -i video.mp4 -i audio.mp3 -c:v libx264 -c:a aac \ -video_track_timescale 90000 -audio_track_timescale 90000 \ -shortest output.mp4同时写了一个脚本,从视频文件里读取实际时长,与音频时长做差,超过 200 毫秒就报红并触发重新合成。
4.6 Skill 上下文膨胀:LLM 把整个仓库读进去了
这是 Agent 开发里非常典型的问题。早期实现里,Agent 为了生成一段动画,会把scripts/下所有源码都读进上下文,一次任务消耗的 token 高得离谱,而且很容易被无关代码干扰判断。
后来我重构了 Skill 目录,明确约定:SKILL.md里只写调用方式和入口命令,不写实现细节;Agent 只需要知道“运行python run_pipeline.py --task task.json就能出片”,具体的 Manim 代码由脚本自己维护,根本没有进入上下文的机会。这一步让单次任务 token 消耗降低了大约 70%,同时因为 Agent 不再“思考”代码细节,出错率也下降了。
math-concept-film/ ├── SKILL.md ├── config/ │ ├── colors.json │ ├── fonts.json │ └── tts_settings.json ├── scripts/ │ ├── run_pipeline.py │ ├── generate_script.py │ ├── generate_storyboard.py │ ├── render_scenes.py │ ├── synthesize_audio.py │ └── compose_video.py ├── templates/ │ ├── intro.scene.py.tpl │ └── concept.scene.py.tpl ├── resources/ │ ├── fonts/ │ └── music/ ├── tests/ │ └── sample_tasks/安装依赖时给一个最小清单:
pip install manim==0.18.1 pip install edge-tts ffmpeg-python pydub sudo apt install ffmpeg texlive-full fonts-noto-cjk给 Agent 装配时,只需要在 Agent 的配置里把math-concept-film目录注册为一个可用 Skill,再声明一句“当用户提出制作数学概念相关短片时,使用 math-concept-film”,Agent 就能在后续对话中自动识别意图并调用流水线。整个过程不需要修改 Agent 核心代码,这也是 Skill 机制最吸引人的地方。
5.3 可扩展的方向
目前的实现面向的是“单条概念短片”,但我认为这个架构可以平移到几个更有想象力的方向。
第一个方向是多语言化。脚本和分镜是语言无关的,只需替换 TTS 音色和字幕语言配置,就能生成英文、日文版的同一数学短片。我在测试中跑通过英文版,效果不错,因为 Manim 的数学渲染天然跨语言。
第二个方向是交互式短片。现在输出是 MP4,下一版可以尝试输出为 HTML + WebGL 的交互页面,让观众在观看“函数的切线斜率逼近”时拖拽滑块改变切线位置。这需要把动画引擎从 Manim 切换成 D3.js 或 Three.js,工程量不小,但教育价值很高。
第三个方向是批量化课程生产。把一学期的微积分知识点做成一个清单文件,批量跑流水线,生成一整套系列短片。这就把 Skill 从“单条生产线”升级成“工厂”,每个视频的片头、口播、画风完全一致,对做在线课程的人来说非常实用。
5.4 开源协作中最需要什么样的人
如果你对这个项目感兴趣,我先说清楚哪些人贡献价值最大。
第一位是数学老师或教研员。这个项目不缺技术能力,缺的是“这个知识点最容易理解错在哪”的教学判断。目前分镜模板是我根据个人理解写的,肯定有盲区。如果有老师能提供“学生最常问的十个问题”这类素材,对分镜设计会是巨大的提升。
第二位是音频处理爱好者。TTS 预处理规则表目前只有几十条,远远不够覆盖所有数学表达式。愿意系统整理“数学式子的自然读法对照表”的人,会让这是项目实用度上一个台阶。
第三位是Agent 框架玩家。Skill 机制的边界到底在哪,怎么与其他 Agent 配合,这些还需要更多实践验证。多一个人接入不一样的 Agent 框架,就能多一份兼容性反馈。
写在最后:我的一点使用体会
开发这套 Skill 的过程中,我最深的感觉是:技术难点从来不是渲染本身,而是“设计一个可靠的流程”,并且忍住不让 LLM 去执行那些明明可以用代码精确完成的环节。数学内容的特殊性决定了它承受不了“看起来合理”的错误,所以每一处校验都值得多写一行代码。
如果你想尝试,我的建议是先从微积分里的 3 到 5 个基础概念入手,比如“极限的直观定义”“连续与间断”“定积分的几何意义”。这几个概念既能覆盖大部分动画模板,又不会因为数学太难让调试变得痛苦。跑通一条之后再慢慢扩展知识点库,你会发现自己再也没有耐心回到纯手剪视频的老路上去。