Core workflow
WORKFLOW = [
# 步骤1: 数学建模
{
"step": 1,
"name": "analyze_problem",
"input": "题目图片/文本",
"output": "math_analysis.md",
"tasks": ["推导数学事实", "建立几何模型", "确定图形构建方法"]
},
# 步骤2: HTML可视化
{
"step": 2,
"name": "html_visualization",
"input": "math_analysis.md",
"output": "数学_{日期}_{题目}.html",
"tasks": ["SVG画图形", "展示画图过程", "标注关键要素"]
},
# 步骤3: 分镜脚本
{
"step": 3,
"name": "storyboard",
"input": "HTML内容",
"output": "{日期}_{题目}_分镜.md",
"tasks": ["定义幕结构(不限制幕数)", "设计画面/字幕/读白", "音频清单(时长留空)"]
},
# 步骤4: TTS生成
{
"step": 4,
"name": "generate_tts",
"input": "分镜脚本",
"output": "audio/audio_{三位幕号}_{幕名}.wav + audio_info.json",
"command": "python scripts/generate_tts.py audio_list.csv ./audio --voice xiaoxiao"
},
# 步骤5: 验证更新
{
"step": 5,
"name": "validate_audio",
"input": "分镜.md + audio/",
"output": "更新后的分镜.md(填充时长) + audio_info.json",
"command": "python scripts/validate_audio.py 分镜.md ./audio",
"check": ["音频存在性", "时长>0", "数量匹配"]
},
# 步骤6: 脚手架
{
"step": 6,
"name": "scaffold",
"input": "分镜.md + audio_info.json",
"output": "script.py (伪代码框架)",
"template": "templates/script_scaffold.py",
"must_include": [
"calculate_geometry() - 几何建模",
"assert_geometry() - 几何验证(题目条件+精度) + 画布范围检查(边界+中心)",
"COLORS - 颜色定义",
"SCENES[] - 幕信息数组(从audio_info.json加载时长)",
]
},
# 步骤7: 生成代码
{
"step": 7,
"name": "implement",
"input": "script.py脚手架 + 分镜.md + audio_info.json",
"output": "完整的script.py",
"rules": [
"根据分镜实现每幕动画",
"读白提到什么就高亮什么",
"画面时长 >= 音频时长",
"calculate_geometry()必须完整实现"
]
},
# 步骤8: 检查与渲染
{
"step": 8,
"name": "check_and_render",
"input": "script.py",
"output": "视频文件 + 关键帧截图",
"command": "python scripts/render.py",
"check": ["代码结构检查(check.py)", "几何正确性", "高亮同步", "字幕清晰", "动画流畅"],
"on_fail": "回到步骤7修改代码"
}
]
Workflow state transitions step1 → step2 → step3 → step4 → step5 → step6 → step7 → step8 ↑____________↓ (loop on failure)
---
## 步骤1:分析题目,推导数学事实
**目标**:建立正确的数学计算方法和流程,形成画出符合数学的图形的方法。
### 输出格式
```markdown
## 数学事实分析
### 已知条件
- 条件1:...
- 条件2:...
### 推导的事实
1. **事实名称**: 描述
- 计算过程: ...
- 数学表达: ...
### 图形构建方法
- 点的坐标: ...
- 边的关系: ...
- 圆/弧的定义: ...
### 需要证明的结论
- 结论1: ...
Note: The conclusions that all proof problems need to prove can all be used in the storyboard as establishedfacts.
Step 2: HTML + SVG visualization
Goal: Use HTML + SVG to draw the figure, show the drawing process and solution flow, and plan for the storyboard. Supplement: If the user has already provided an accurate HTML file describing the drawing process, you can copy one according to the filename requirement and skip generating the file.
Output requirements
- File naming:
数学_{日期}_{题目简述}.html - Includes: problem statement, SVG figure, step-by-step solution, key element annotations
- SVG needs to showthe drawing process(e.g.: first draw the triangle → then draw the circle → annotate points)
Step 3: Generate the storyboard script
Goal: Define the video structure, with no limit on the number of scenes, and reserve the audio filename at the end (duration left empty).
File naming
{日期}_{题目}_分镜.md
Storyboard script structure
# 分镜脚本 - {题目名称}
## 分镜设计
### 第1幕:{幕名}
**画面**: ...
**字幕**: ...(简洁,≤20字)
**读白**: ...(详细,口语化)
**动画**: ...
**目的**: ...
---
## 音频生成清单(步骤4填写)
| 幕号 | 文件名 | 读白文本 | 时长 | 说话人 | 情感 |
|------|--------|----------|------|--------|------|
| 1 | audio_001_{幕名}.wav | "读白文本" | | xiaoxiao | 热情 |
| 2 | audio_002_{幕名}.wav | "读白文本" | | xiaoxiao | 平和 |
Key specifications
- Scene numbers are numbered consecutively starting from 1
- Filename format:
audio_{三位幕号}_{幕名}.wav - The duration column is empty, filled in by Step 5
- No limit on the number of scenes; decide according to content needs
Subtitle exit convention (important)
To avoid forgetting to exit text in animations, in theanimationpart of the storyboard use the following convention to specify exit timing:
**动画**:
- 0.0s: 字幕"勾股定理"淡入
- 2.0s: → 字幕退场(或:字幕淡出)
- 3.0s: 图形高亮
Exit marking methods(choose one of three):
→The arrow symbol indicates exit (recommended, most concise)退场:or淡出:explicit marking持续X秒specify display duration
Example:
**动画**:
- 字幕"正方形性质"淡入,持续3秒 → 自动退场
- 0.0s: 标题淡入 | 3.0s: →
- 公式分步显示,第5秒全部退场
Reference example
See references/storyboard_sample.md - Problem 99: Prove four points are concyclic
Step 4: TTS generates voice files
Use script:scripts/generate_tts.py(Edge TTS recommended)
Execution command
python scripts/generate_tts.py 分镜.md ./audio --voice xiaoxiao
Output file
audio/
├── audio_001_开场.wav
├── audio_002_展示图形.wav
├── audio_003_定理证明.wav
├── ...
└── audio_info.json
audio_info.json format
{
"files": [
{"scene": 1, "file": "audio_001_开场.wav", "duration": 8.5},
{"scene": 2, "file": "audio_002_展示图形.wav", "duration": 15.2}
]
}
Step 5: Verify and update storyboard durations
Use script:scripts/validate_audio.py
Function
- Read
audio/audio_info.jsonto get audio duration - Verify that all audio files exist and have normal durations (>0 seconds)
- Update the duration column in the "Audio Generation List" of the storyboard script
- If audio is missing or the duration is abnormal, report an error reminder
Execution command
python scripts/validate_audio.py 分镜.md ./audio
Verification failure cases
- Missing audio file → Error: "Missing audio file for scene X"
- Duration is 0 or abnormal → Error: "Scene X audio duration is abnormal, please check the format"
- Storyboard and audio counts do not match → Error: "Storyboard has X scenes, audio has Y files, counts do not match"
Step 6: Generate the script.py scaffold
Use the template:templates/script_scaffold.py
Output:script.py(a pseudocode framework that definitely won't build)
Parts the scaffold must include
from manim import *
import json
import os
class MathScene(Scene):
"""
数学教学视频场景 - 脚手架
根据分镜脚本和音频信息生成完整动画
"""
# ========== 1. 颜色定义 ==========
COLORS = {
'background': '#1a1a2e', # 深蓝背景
'primary': '#4ecca3', # 主色(青色)
'secondary': '#e94560', # 辅助色(红色)
'highlight': '#ffc107', # 高亮色(黄色)
'text': '#ffffff', # 文字白色
'grid': '#2a2a4e', # 网格线
}
# ========== 2. 幕信息数组(供AI参考) ==========
# 格式: (幕号, 幕名, 音频文件名, 时长秒数)
# 注意:时长从 audio_info.json 读取,确保画面等待音频原则
SCENES = [
(1, "开场", "audio_001_开场.wav", None),
(2, "展示图形", "audio_002_展示图形.wav", None),
# ... 根据分镜动态生成
]
def __init__(self, **kwargs):
super().__init__(**kwargs)
# 加载音频时长信息
self.audio_timings = self._load_audio_timings()
def _load_audio_timings(self):
"""从 audio_info.json 加载音频时长"""
# TODO: AI实现 - 读取JSON并填充 SCENES 的时长
pass
# ========== 3. 几何计算函数 ==========
def calculate_geometry(self):
"""
计算所有几何元素的位置和属性
从步骤2的HTML画图中提取信息,建立数学模型:
- 所有点的坐标 (x, y) - ⚠️ 始终使用2D,z坐标始终为0
- 边的长度和方程
- 圆/弧的圆心和半径
- 交点计算
- 切线、法线等辅助线
返回: dict 包含所有几何对象的数据
"""
# TODO: AI根据题目几何关系实现
geometry = {
'points': {}, # {'A': (x, y), 'B': (x, y), ...}
'lines': {}, # {'AB': {'start': A, 'end': B, 'length': L}, ...}
'circles': {}, # {'circle1': {'center': O, 'radius': r}, ...}
'arcs': {}, # 圆弧定义
}
return geometry
# ========== 4. 几何验证函数 ==========
def assert_geometry(self, geometry):
"""
验证几何计算的正确性(最小验证原则)
验证内容:
1. 题目给定的事实(如:两条边相等,谁是谁的一半)
2. 精度问题:使用相对误差比较,而非绝对相等
3. 画布范围检查:确保图形在可视区域内
4. 错误提示:所有assert必须用中文描述问题,包含具体数值和修复建议
=== 几何条件验证 ===
基于题目给定的条件,编写最小但关键的验证:
- 如果题目说 AB = BC,验证 |AB - BC| < epsilon
- 如果题目说 E 是中点,验证 AE = EB(考虑浮点误差)
- 如果题目说某角是直角,验证向量点积接近0
- 如果题目给具体数值(如边长为5),验证计算结果匹配
精度建议:
- 相对误差:1e-6 或绝对误差 1e-4
- 示例:assert abs(len_AB - len_BC) < 1e-4, "中文错误提示:AB长度不等于BC长度"
=== 画布范围验证算法 ===
目标:确保整个图形在画布内,且位于视觉中心区域
算法步骤(AI需要根据几何数据实现):
1. 计算所有几何元素的外接矩形 bounding_box
- 遍历所有点、线、圆的边界点
- 取最小x, 最大x, 最小y, 最大y
2. 验证矩形在画布范围内(考虑边距)
- 画布默认范围:FRAME_WIDTH=14.2, FRAME_HEIGHT=8
- 建议边距:四周各留 0.5-1.0 单位
- 验证:min_x > -7+margin 且 max_x < 7-margin
min_y > -4+margin 且 max_y < 4-margin
3. 验证矩形中心在视觉中心区域
- 画布中心:(0, 0)
- 视觉中心区域:中心 ±20%(即 x∈[-1.4, 1.4], y∈[-0.8, 0.8])
- 计算矩形中心:(center_x, center_y) = ((min_x+max_x)/2, (min_y+max_y)/2)
- 验证:center_x ∈ [-1.4, 1.4] 且 center_y ∈ [-0.8, 0.8]
4. 如果图形超出范围,AI需要调整几何计算的缩放或平移
"""
# === TODO: AI实现 - 基于题目条件的验证(失败时用中文报错)===
# 示例代码(根据实际题目修改):
# epsilon = 1e-4
# len_ab = geometry['lines']['AB']['length']
# len_bc = geometry['lines']['BC']['length']
# assert abs(len_ab - len_bc) < epsilon, f"几何验证失败:AB长度({len_ab:.4f})不等于BC长度({len_bc:.4f}),题目要求两边相等"
#
# len_ae = geometry['lines']['AE']['length']
# len_ab = geometry['lines']['AB']['length']
# assert abs(len_ae * 2 - len_ab) < epsilon, f"几何验证失败:E不是AB中点,AE({len_ae:.4f})*2={len_ae*2:.4f} != AB({len_ab:.4f})"
# === TODO: AI实现 - 画布范围检查(失败时用中文报错)===
# 1. 计算bounding_box
# all_points = list(geometry['points'].values())
# min_x = min(p[0] for p in all_points)
# max_x = max(p[0] for p in all_points)
# min_y = min(p[1] for p in all_points)
# max_y = max(p[1] for p in all_points)
#
# 2. 验证画布边界(失败时报具体数值)
# margin = 0.5
# assert min_x > -7 + margin, f"画布验证失败:图形左边界(min_x={min_x:.2f})超出安全区域(>{-7+margin}),需要向右平移或缩小"
# assert max_x < 7 - margin, f"画布验证失败:图形右边界(max_x={max_x:.2f})超出安全区域(<{7-margin}),需要向左平移或缩小"
# assert min_y > -4 + margin, f"画布验证失败:图形下边界(min_y={min_y:.2f})超出安全区域(>{-4+margin}),需要向上平移或缩小"
# assert max_y < 4 - margin, f"画布验证失败:图形上边界(max_y={max_y:.2f})超出安全区域(<{4-margin}),需要向下平移或缩小"
#
# 3. 验证视觉中心(失败时报具体偏移)
# center_x = (min_x + max_x) / 2
# center_y = (min_y + max_y) / 2
# assert -1.4 <= center_x <= 1.4, f"视觉中心验证失败:图形中心X={center_x:.2f}超出视觉中心区域[-1.4, 1.4],建议调整几何计算使图形居中"
# assert -0.8 <= center_y <= 0.8, f"视觉中心验证失败:图形中心Y={center_y:.2f}超出视觉中心区域[-0.8, 0.8],建议调整几何计算使图形居中"
pass
# ========== 5. 图形元素定义 ==========
def define_elements(self, geometry):
"""
定义 Manim 图形对象(但不创建动画)
为每一幕需要的图形元素预先定义,便于结构化调用:
- 点、线、圆的基础对象
- 高亮效果对象
- 标注文字对象
"""
# TODO: AI根据分镜需求定义
elements = {
'points': {}, # Mobject 点对象
'lines': {}, # Mobject 线对象
'circles': {}, # Mobject 圆对象
'labels': {}, # 标签文字
}
return elements
# ========== 6. 构造主流程 ==========
def construct(self):
"""主构造流程"""
# 计算几何
geometry = self.calculate_geometry()
self.assert_geometry(geometry)
elements = self.define_elements(geometry)
# 设置背景
self.camera.background_color = self.COLORS['background']
# 按幕执行
for scene_num, scene_name, audio_file, duration in self.SCENES:
self.play_scene(scene_num, scene_name, audio_file, duration, elements, geometry)
def play_scene(self, scene_num, scene_name, audio_file, duration, elements, geometry):
"""
播放单幕动画 - 必须添加音频
========== 音频集成(强制要求) ==========
每幕必须添加对应的音频文件,否则视频将没有声音!
方式:在方法第一行就添加音频
self.add_sound(f"audio/{audio_file}")
注意:
- audio_file 参数已经包含文件名(如 "audio_001_开场.wav")
- 音频文件位于 audio/ 目录下
- 添加音频后,后续动画时长应 >= duration(音频时长)
========== 画面等待音频原则 ==========
- 动画总时长 >= 音频时长(duration 参数)
- 可以画面多等待(多出静音部分,后期可剪辑)
- 不能画面比音频短(会导致音频被截断)
========== 高亮规范(必须在读白提到时同步) ==========
- 提到某个边等于某个边 → 用动画高亮两条边
- 提到某个点/圆 → 闪烁或放大高亮
- 提到证明结论 → 用框或颜色强调
========== 绘制策略 ==========
- 简单图形:直接显示,然后高亮关键部分
- 复杂图形:逐步绘制,边画边讲解
"""
# TODO: AI实现 - 第一行必须添加音频!
# self.add_sound(f"audio/{audio_file}")
pass
# ========== 7. 音频集成 ==========
def add_audio_to_scene(self, audio_file, animation_duration):
"""
添加音频到场景
方式1(推荐):在script.py中直接添加音频文件
self.add_sound(audio_file)
方式2:如果需要在特定时间点触发,使用延时
确保:animation_duration >= 音频实际时长
"""
# TODO: AI实现音频集成
pass
Step 7: AI generates the final code
Input:
- Storyboard script (duration already updated)
audio/audio_info.jsonscript.pyscaffold
Output: complete script.py
AI generation guidance:
- Implement the specific animation code based on the "animation" description in the storyboard
- Determine the highlight timing based on the "narration" content
- Implement the specific calculation of
calculate_geometry()specific calculation - Implement the validation logic of
assert_geometry()verification logic - Implement the
play_scene_X()method for each scene - Audio integration (required):
- The first line of each scene must be
self.add_sound(f"audio/{audio_file}") - Ensure the total animation duration >= audio duration (use the
durationparameter) - If the animation is too short, add
self.wait()to extend it
- The first line of each scene must be
- Verify the audio file path is correct:
audio/audio_001_XXX.wav - Subtitle display and exit (critical):
- Use the
show_subtitle_timed(text, duration)method provided by the scaffold to display timed subtitles - Or use
show_subtitle_with_audio(text, audio_duration)to make the subtitle last until the audio ends - The
→or退场:marker in the storyboard indicates that the text needs to fade out at that time point - You must ensure all text elements have an exit animation, to avoid lingering into the next scene
- Use the
Step 8: Code check and rendering
8.1 Code structure check (required)
Use the script:scripts/check.py
Before rendering, you must first check whether the code contains the necessary functions and structures:
# 检查 script.py(默认)
python scripts/check.py
# 检查指定文件
python scripts/check.py my_script.py
Check contents:
- ✅
calculate_geometry()- Whether the geometry calculation function exists - ✅
assert_geometry()- Whether the geometry validation function exists - ✅
define_elements()- Whether the graphic element definition function exists - ✅ Subtitle class
Subtitle/TitleSubtitlewhether it exists - ✅ Whether there is a
add_sound()call (audio integration) - ✅ Whether there is a class inheriting
Sceneclass
Check results:
- ❌ Error: must be fixed, otherwise rendering cannot proceed
- ⚠️ Warning: recommended to fix, but will not block rendering
8.2 Render the video
Method 1: Use the rendering script (recommended, includes checks)
# 完整流程:检查 -> 渲染 -> 拷贝到根目录
python scripts/render.py
# 指定文件和场景
python scripts/render.py -f script.py -s MathScene
# 指定渲染质量
python scripts/render.py -q h # 1080p60 (默认)
python scripts/render.py -q k # 4K
python scripts/render.py -q m # 720p30
# 跳过检查(不推荐,仅用于快速测试)
python scripts/render.py --no-check
Method 2: Use manim directly (skip checks, not recommended)
manim -pqh script.py MathScene
Extract keyframes for verification
python scripts/extract_frames.py media/videos/script/1080p60/MathScene.mp4 --interval 5
Verification contents
- Whether the geometric figures are correct (positions of points, lines, circles)
- Whether the highlights are synchronized with the explanation
- Whether the subtitles are clear and readable
- Whether the animation is smooth
- Whether the audio is added correctly:
- Every scene has sound (no silent scenes)
- The audio matches the narration content in the storyboard
- Animation duration >= audio duration (no audio truncation)
Must be executed after rendering
# 在construct()最后添加:拷贝视频到根目录
import shutil
import os
from pathlib import Path
# 渲染完成后拷贝到根目录
video_src = Path("media/videos/script/1920p60/SquareTriangleProblem.mp4")
video_dst = Path("最终视频.mp4")
if video_src.exists():
shutil.copy2(video_src, video_dst)
print(f"✓ 视频已拷贝到: {video_dst}")
Rule: After rendering is complete, the video must be copied to the project root directory, do not leave it deep in media/.
If problems are found
- Go back to Step 7 and modify the code
- Re-render verification
Mathematical problem-solving principles (absolutely important)
❌ Coordinate systems are prohibited
Never use coordinate systems to solve, should use various definitions and reasoning.
Incorrect example(coordinate method):
设B=(0,0), C=(5,0), A=(1.8, 2.4)...
面积 = ½ × 底 × 高
Correct example(geometric reasoning):
1. 由勾股定理:BC = √(AB² + AC²) = 5
2. 正方形性质:BE = BC = 5
3. 等积变换:S△ABE = S△ABC × (EB/BC) × sin(∠ABE)/sin(∠ABC)
4. 或用向量叉积、海伦公式等纯几何方法
Allowed visualization aids:
- For animation display, coordinates may be shown, but coordinate calculations cannot be used in the problem-solving process
- You can use the approach of geometric sketchpad: transformations such as rotation, translation, symmetry, etc.
Recommended geometric reasoning methods
- Equal-area transformation - same base and equal height, equal base and same height
- Similar triangles - proportional relationships
- Pythagorean theorem - side length relationships
- Vectors/cross product - operations with purely geometric meaning
- Rotation/symmetry - geometric transformations
File structure
tutor/
├── SKILL.md # 本文件 - 工作流程定义
├── requirements.txt # Python 依赖
├── references/
│ └── storyboard_sample.md # 分镜脚本示例(参考)
├── templates/
│ └── script_scaffold.py # Manim 脚手架模板(含字幕类)
├── scripts/
│ ├── generate_tts.py # TTS 生成脚本
│ ├── validate_audio.py # 音频验证脚本
│ ├── check.py # 代码结构检查脚本(渲染前必执行)
│ └── render.py # 渲染流水线脚本(检查+渲染+拷贝)
└── sample/ # 示例项目(保留参考)
└── geometry_proof/
Dependency management
Use uv to manage dependencies:
# 创建虚拟环境
uv venv .venv
# 安装依赖
uv pip install -r requirements.txt
# 激活环境
source .venv/bin/activate
Bypassing LaTeX dependencies (important)
Manim requires LaTeX by default to render mathematical formulas. If you do not want to install LaTeX,Use Text instead of MathTex entirely:
# ❌ 需要LaTeX
MathTex(r"BC^2 = AB^2 + AC^2")
MathTex(r"\frac{1}{2} \times 5")
MathTex("A") # 甚至连字母都需要LaTeX
# ✅ 不需要LaTeX
Text("BC² = AB² + AC²", font_size=36)
Text("½ × 5", font_size=30)
Text("A", font_size=32) # 用Text直接显示字母
Common replacement reference table:
| Mathematical symbol | LaTeX notation | No-LaTeX notation |
|---|---|---|
| Superscript | x^2 |
x² (directly enter Unicode) |
| Fraction | \frac{1}{2} |
½ or 1/2 |
| Degrees | 90^\circ |
90° |
| Square centimeters | \text{cm}^2 |
cm² |
| Square root | \sqrt{25} |
√25 |
| Angle | \angle A |
∠A |
The scaffold does not use MathTex by default, if mathematical formulas are needed, please directly enter Unicode characters.
Summary of key principles
| Principle | Description |
|---|---|
| Mathematics first | First establish the correct mathematical model, then draw the diagram |
| Audio required | Each scene must call self.add_sound(), otherwise the video will have no sound |
| Audio-visual synchronization | The visuals wait for the audio (animation duration >= audio duration), ensuring narration and animation are synchronized |
| Highlight correspondence | Whatever the voiceover mentions, highlight that in the visuals |
| Minimal verification | assert_geometry Verify the problem conditions (with precision) + canvas range (boundary + center) |
| Gradual abstraction | From HTML visualization → storyboard script → Manim code |