
Tutor General
- 9 installs
- 8 repo stars
- Updated August 4, 2026
- wangyendt/wayne-skills
Helps with ai & agent building tasks.
About
tutor-general is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted development.
- tutor-general
- AI & Agent Building
- AI-coding skill
Tutor General by the numbers
- 9 all-time installs (skills.sh)
- Ranked #12,133 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/wangyendt/wayne-skills --skill tutor-generalAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 9 |
|---|---|
| repo stars | ★ 8 |
| Last updated | August 4, 2026 |
| Repository | wangyendt/wayne-skills ↗ |
What it does
Helps with ai & agent building tasks.
Files
Tutor General - 通用知识教学视频
⚠️ 重要声明:本技能受 tutor-math-geometry 启发,用 Motion Canvas 替代 Manim,适配更广泛的知识点讲解。
与 tutor-math-geometry 的区别
| 维度 | tutor-math-geometry | tutor-general |
|---|---|---|
| 主题 | 数学几何 | 任何知识点 |
| 渲染引擎 | Manim | Motion Canvas (TypeScript) |
| 代码风格 | Python | TypeScript |
| 动画类型 | 数学公式/图形 | 流程图/示意图/动画 |
核心工作流
WORKFLOW = [
// 步骤1: 内容分析
{
"step": 1,
"name": "analyze_content",
"input": "主题/文本/URL",
"output": "content_analysis.md",
"tasks": ["提炼核心概念", "确定讲解结构", "规划可视化方式"]
},
// 步骤2: 分镜脚本
{
"step": 2,
"name": "storyboard",
"input": "content_analysis.md",
"output": "{日期}_{主题}_分镜.md",
"tasks": ["定义幕结构", "设计画面/字幕/读白", "规划动画类型"]
},
// 步骤3: TTS生成
{
"step": 3,
"name": "generate_tts",
"input": "分镜脚本",
"output": "audio/audio_{三位幕号}_{幕名}.wav + audio_info.json",
"command": "python scripts/generate_tts.py audio_list.csv ./audio --voice xiaoxiao"
},
// 步骤4: 验证更新
{
"step": 4,
"name": "validate_audio",
"input": "分镜.md + audio/",
"output": "更新后的分镜.md(填充时长) + audio_info.json",
"command": "python scripts/validate_audio.py 分镜.md ./audio",
"check": ["音频存在性", "时长>0", "数量匹配"]
},
// 步骤5: Motion Canvas 脚手架
{
"step": 5,
"name": "scaffold",
"input": "分镜.md + audio_info.json",
"output": "src/scene.ts (伪代码框架)",
"template": "templates/scene_scaffold.ts",
"must_include": [
"createScene() - 场景创建",
"COLORS - 颜色定义",
"SCENES[] - 幕信息数组(从audio_info.json加载时长)",
"Motion Canvas API 调用"
]
},
// 步骤6: 生成代码
{
"step": 6,
"name": "implement",
"input": "脚手架 + 分镜.md + audio_info.json",
"output": "完整的 Motion Canvas 代码",
"rules": [
"根据分镜实现每幕动画",
"使用 @motion-canvas/2d 库",
"画面时长 >= 音频时长",
"TypeScript 类型正确"
]
},
// 步骤7: 检查与渲染
{
"step": 7,
"name": "check_and_render",
"input": "Motion Canvas 项目",
"output": "视频文件",
"command": "npm run export",
"check": ["TypeScript 编译", "动画同步", "导出成功"]
}
]---
步骤1:内容分析
目标:理解主题,提炼核心概念,规划可视化方案。
输出格式
## 内容分析
### 主题
{主题名称}
### 核心概念(3-5个)
1. 概念1:...
2. 概念2:...
3. 概念3:...
### 讲解结构
- 引入:...
- 展开:...
- 总结:...
### 可视化方案
- 概念1 → 流程图
- 概念2 → 示意图
- 概念3 → 动画演示---
步骤2:分镜脚本
目标:定义视频结构,结尾预留音频文件名(时长为空)。
文件命名
{日期}_{主题}_分镜.md
分镜脚本结构
# 分镜脚本 - {主题名称}
## 分镜设计
### 第1幕:{幕名}
**画面**: ...
**字幕**: ...(简洁,≤20字)
**读白**: ...(详细,口语化)
**动画类型**: 淡入/移动/缩放/高亮
**时长**: {音频时长}
---
### 第2幕:{幕名}
...
## 音频生成清单
| 幕号 | 文件名 | 读白文本 | 时长 | 说话人 | 情感 |
|------|--------|----------|------|--------|------|
| 1 | audio_001_{幕名}.wav | "读白文本" | | xiaoxiao | 热情 |
| 2 | audio_002_{幕名}.wav | "读白文本" | | xiaoxiao | 平和 |Motion Canvas 动画类型
fade-in- 淡入fade-out- 淡出move- 移动scale- 缩放rotate- 旋转highlight- 高亮draw- 绘制(用于流程图)
---
步骤3:TTS生成
命令:
python scripts/generate_tts.py audio_list.csv ./audio --voice xiaoxiao输出:
audio/audio_001_{幕名}.wavaudio_info.json
---
步骤4:验证更新
命令:
python scripts/validate_audio.py 分镜.md ./audio检查项:
- [ ] 音频文件存在
- [ ] 时长 > 0
- [ ] 数量匹配
---
步骤5:Motion Canvas 脚手架
模板:templates/scene_scaffold.ts
必须包含:
// 颜色定义
const COLORS = {
background: '#0f172a',
primary: '#4ecca3',
secondary: '#e94560',
highlight: '#ffc107',
text: '#ffffff',
};
// 幕信息(从 audio_info.json 加载)
const SCENES = [
{ id: 1, name: '开场', duration: 5.0 },
{ id: 2, name: '概念', duration: 10.0 },
// ...
];
// 场景创建
export function createScene() {
// ...
}---
步骤6:Motion Canvas 代码实现
核心 API
import {
Scene,
Circle,
Rect,
Line,
Text,
VGroup,
layout,
fadeIn,
any,
all,
waitFor
} from '@motion-canvas/2d';
// 创建圆形
const circle = new Circle({ radius: 50, fill: '#4ecca3' });
// 创建文字
const text = new Text({
text: 'Hello',
fontSize: 32,
fill: 'white'
});
// 动画序列
circle.fill('red', 1);
// 等待音频
yield* waitFor(audioDuration);常用动画模式
// 淡入
new Rect().opacity(0).fadeIn(1);
// 移动
new Circle().position([100, 0]).move([200, 0], 1);
// 高亮
new Circle().stroke('#ffff00', 0.5).back(0.5);
// 流程图
new Layout({ direction: 'row', spacing: 50 })
.add(new Rect())
.add(new Arrow())
.add(new Rect());---
步骤7:渲染
开发模式(实时预览)
npm run serve导出视频
npm run exportMotion Canvas 优势
- ✅ 实时预览,调试快
- ✅ TypeScript 类型安全
- ✅ 可交互预览
- ✅ 适合流程图/示意图
对比 Manim
| 维度 | Manim | Motion Canvas |
|---|---|---|
| 语言 | Python | TypeScript |
| 实时预览 | 需渲染 | 即时预览 |
| 适合 | 数学图形 | 流程/演示 |
| 输出 | 视频 | 视频/可交互 |
---
依赖
- Node.js 16+
- npm
- @motion-canvas/core
- @motion-canvas/2d
- @motion-canvas/ui
- edge-tts (用于TTS)
- ffmpeg (用于视频导出)
安装
npm install使用示例
# 1. 复制模板
cp -r templates/* ./my-project/
# 2. 生成分镜脚本
# 编辑 {主题}_分镜.md
# 3. 生成 TTS
python scripts/generate_tts.py audio_list.csv ./audio
# 4. 验证音频
python scripts/validate_audio.py 分镜.md ./audio
# 5. 实现动画
# 编辑 src/scene.ts
# 6. 开发预览
npm run serve
# 7. 导出
npm run export---
目录结构
tutor-general/
├── SKILL.md # 本文件
├── README.md # 说明文档
├── scripts/
│ ├── generate_tts.py # TTS生成脚本
│ ├── validate_audio.py # 音频验证脚本
│ └── audio_list.example.csv # 示例
├── templates/
│ ├── scene_scaffold.ts # 脚手架模板
│ └── package.json # 项目模板
├── references/
│ └── storyboard_sample.md # 分镜示例
└── assets/
└── (静态资源)---
注意事项
1. 动画同步:画面时长必须 >= 对应音频时长 2. TypeScript:确保类型正确,Motion Canvas 需要编译 3. 实时预览:开发时用 npm run serve 实时查看效果 4. 导出时间:视频导出需要较长时间,请耐心等待
Tutor General - 通用知识教学视频制作
⚠️ 重要声明:本技能受 tutor-math-geometry 启发,用 Motion Canvas 替代 Manim,适配更广泛的知识点讲解。
通用知识教学视频制作技能,用于生成带配音的 Motion Canvas 动画视频。支持任何主题的知识讲解(科普、技术原理、流程演示等),不限于数学几何。
与 tutor-math-geometry 的区别
| 维度 | tutor-math-geometry | tutor-general |
|---|---|---|
| 主题 | 数学几何 | 任何知识点 |
| 渲染引擎 | Manim (Python) | Motion Canvas (TypeScript) |
| 代码风格 | Python | TypeScript |
| 动画类型 | 数学公式/图形 | 流程图/示意图/动画 |
核心工作流
1. 内容分析 → 提炼核心概念,规划可视化 2. 分镜脚本 → 定义幕结构,设计画面/字幕/读白 3. TTS 音频 → 生成配音文件 4. 验证更新 → 填充音频时长 5. 脚手架 → 生成 Motion Canvas 代码框架 6. 实现代码 → 根据分镜实现动画 7. 渲染验证 → 生成最终视频
使用场景示例
- 科普讲解(什么是 DIC?什么是量子计算?)
- 技术原理演示(神经网络如何工作?)
- 流程介绍(Kubernetes 架构)
- 概念解析(什么是向量数据库?)
目录结构
tutor-general/
├── SKILL.md # 技能定义
├── README.md # 本文件
├── scripts/
│ ├── generate_tts.py # TTS 生成
│ └── validate_audio.py # 音频验证
├── templates/
│ ├── scene_scaffold.ts # 代码脚手架
│ └── package.json # 项目模板
└── references/
└── storyboard_sample.md # 分镜示例快速开始
# 1. 分析内容(手动或 AI 辅助)
# 创建 content_analysis.md
# 2. 编写分镜脚本
# 创建 {主题}_分镜.md
# 3. 生成 TTS
python scripts/generate_tts.py audio_list.csv ./audio
# 4. 验证音频
python scripts/validate_audio.py 分镜.md ./audio
# 5. 实现动画
# 参考 templates/scene_scaffold.ts 编写 TypeScript 代码
# 6. 开发预览
npm run serve
# 7. 导出视频
npm run export依赖
- 运行时:Node.js 16+
- 动画引擎:@motion-canvas/core, @motion-canvas/2d, @motion-canvas/ui
- 语音合成:edge-tts
- 视频处理:ffmpeg
安装
# 复制模板
cp -r templates/* my-project/
cd my-project
# 安装依赖
npm install
# 开发预览
npm run serve
# 导出视频
npm run exportMotion Canvas 特点
- ✅ 实时预览(边改边看)
- ✅ TypeScript 类型安全
- ✅ 适合流程图/示意图/动画
- ✅ 可交互预览
scene,name,text,voice,emotion
1,开场,"大家好,今天我们来学习...",xiaoxiao,热情
2,概念一,"首先,什么是核心概念...",xiaoxiao,平和
3,概念二,"接下来看第二个要点...",xiaoxiao,平和
4,总结,"通过今天的学习,我们了解了...",xiaoxiao,满意
#!/usr/bin/env python3
"""
TTS 生成脚本
用法: python generate_tts.py audio_list.csv ./audio --voice xiaoxiao
"""
import argparse
import asyncio
import edge_tts
import os
import csv
import json
async def generate_tts(csv_file: str, output_dir: str, voice: str = "zh-CN-XiaoxiaoNeural"):
"""生成 TTS 音频"""
os.makedirs(output_dir, exist_ok=True)
# 读取 CSV
with open(csv_file, 'r', encoding='utf-8') as f:
reader = csv.DictReader(f)
rows = list(reader)
results = []
for row in rows:
scene = row['scene']
name = row['name']
text = row['text']
filename = f"audio_{int(scene):03d}_{name}.wav"
filepath = os.path.join(output_dir, filename)
print(f"生成: {filename}")
# 生成 MP3
communicate = edge_tts.Communicate(text, voice)
mp3_path = filepath.replace('.wav', '.mp3')
await communicate.save(mp3_path)
# 转换为 WAV
os.system(f"ffmpeg -y -i {mp3_path} -ar 16000 -ac 1 {filepath} >/dev/null 2>&1")
os.remove(mp3_path)
results.append({
'scene': int(scene),
'file': filename,
'duration': 0 # 稍后验证
})
# 保存 audio_info.json
info_path = os.path.join(output_dir, 'audio_info.json')
with open(info_path, 'w', encoding='utf-8') as f:
json.dump({'files': results}, f, ensure_ascii=False, indent=2)
print(f"\n生成了 {len(results)} 个音频文件")
def main():
parser = argparse.ArgumentParser(description='生成 TTS 音频')
parser.add_argument('csv', help='CSV 文件路径')
parser.add_argument('output', help='输出目录')
parser.add_argument('--voice', default='zh-CN-XiaoxiaoNeural', help='语音')
args = parser.parse_args()
asyncio.run(generate_tts(args.csv, args.output, args.voice))
if __name__ == '__main__':
main()
#!/usr/bin/env python3
"""
音频验证脚本
用法: python validate_audio.py 分镜.md ./audio
"""
import os
import re
import json
import argparse
from pathlib import Path
from mutagen.wave import WAVE
def get_duration(wav_path: str) -> float:
"""获取 WAV 文件时长"""
try:
audio = WAVE(wav_path)
return audio.info.length
except:
return 0
def validate_audio(storyboard_md: str, audio_dir: str) -> dict:
"""验证音频文件并更新分镜"""
# 读取分镜文件
with open(storyboard_md, 'r', encoding='utf-8') as f:
content = f.read()
# 查找音频文件名
audio_files = re.findall(r'audio_(\d+)_\w+\.wav', content)
audio_dir = Path(audio_dir)
results = []
# 验证每个音频
for scene in audio_files:
filename = f"audio_{int(scene):03d}.wav"
filepath = audio_dir / filename
if filepath.exists():
duration = get_duration(str(filepath))
results.append({
'scene': int(scene),
'file': filename,
'duration': round(duration, 2)
})
print(f"✓ {filename}: {duration:.2f}s")
else:
print(f"✗ {filename}: 不存在")
results.append({
'scene': int(scene),
'file': filename,
'duration': 0
})
# 保存 audio_info.json
info_path = audio_dir / 'audio_info.json'
with open(info_path, 'w', encoding='utf-8') as f:
json.dump({'files': results}, f, ensure_ascii=False, indent=2)
print(f"\n验证完成,生成了 {info_path}")
return results
def main():
parser = argparse.ArgumentParser(description='验证音频文件')
parser.add_argument('storyboard', help='分镜文件路径')
parser.add_argument('audio_dir', help='音频目录')
args = parser.parse_args()
validate_audio(args.storyboard, args.audio_dir)
if __name__ == '__main__':
main()
{
"name": "tutor-general-project",
"version": "1.0.0",
"description": "Generated by tutor-general skill",
"scripts": {
"serve": "motion-canvas serve",
"build": "motion-canvas build",
"export": "motion-canvas export"
},
"dependencies": {
"@motion-canvas/2d": "^3.0.0",
"@motion-canvas/core": "^3.0.0",
"@motion-canvas/ui": "^3.0.0"
},
"devDependencies": {
"motion-canvas": "^3.0.0"
}
}
import { Scene2D, Circle, Rect, Text, VGroup, Layout, all, waitFor } from '@motion-canvas/2d';
// ============ 颜色定义 ============
const COLORS = {
background: '#0f172a',
primary: '#4ecca3',
secondary: '#e94560',
highlight: '#ffc107',
text: '#ffffff',
};
// ============ 幕信息(从 audio_info.json 加载) ============
// TODO: 替换为实际的音频时长
const SCENES = [
{ id: 1, name: '开场', duration: 5.0 },
{ id: 2, name: '概念', duration: 10.0 },
{ id: 3, name: '总结', duration: 5.0 },
];
// ============ 场景 ============
export class MyScene extends Scene2D {
constructor() {
super({ background: COLORS.background });
}
// 动画序列
*flow() {
// 第1幕:开场
yield* this.intro();
// 第2幕:概念
yield* this.concepts();
// 第3幕:总结
yield* this.summary();
}
*intro() {
const title = new Text({
text: '主题名称',
fontSize: 48,
fill: COLORS.primary,
});
this.add(title);
title.opacity(0);
// 淡入
yield* title.opacity(1, 1);
// 等待音频
yield* waitFor(SCENES[0].duration - 2);
// 淡出
yield* title.opacity(0, 0.5);
}
*concepts() {
const concepts = new VGroup({
layout: { direction: 'column', spacing: 30 },
y: -50,
});
const items = ['概念1', '概念2', '概念3'];
for (const item of items) {
concepts.add(
new Text({ text: item, fontSize: 32, fill: COLORS.text })
);
}
this.add(concepts);
concepts.opacity(0);
// 依次淡入
for (const child of concepts.children()) {
yield* child.opacity(1, 0.5);
yield* waitFor(0.5);
}
yield* waitFor(SCENES[1].duration - 4);
yield* concepts.opacity(0, 0.5);
}
*summary() {
const summary = new Text({
text: '总结内容',
fontSize: 36,
fill: COLORS.highlight,
});
this.add(summary);
summary.opacity(0);
yield* summary.opacity(1, 1);
yield* waitFor(SCENES[2].duration - 2);
}
}