Tobatsu · Shared Skill · 设计稿 v0

subtitle-effect-renderer

给视频烧录短视频特效字幕。SRT → ASS → ffmpeg。 Agent 决定风格,模板提供骨架,自然语言做微调。 一个 skill 服务所有需要加字幕的 workflow。

5 套模板
clean → glow
画幅自适应
9:16 / 1:1 / 16:9
语种
5+ 含 RTL
入口
挂在现有 workflow
Input Model

三层参数:从默认到精确控制

用户在 task form 填 3 个字段。Skill 按优先级合并:模板默认值最弱,自然语言中等, 显式 overrides 最强。最终用了哪个值、值来自哪一层,都写进 result.json,可追溯。

template_defaults priority · low
骨架。每个模板内嵌一份完整默认参数(颜色、字号、动画时长、过冲幅度、淡入淡出时长)。用户什么都不填也能跑。
skill 内置
instruction priority · mid
自然语言微调。"字大一号" → fontsize_scale 1.15;"改蓝色高亮" → primary_color #00BFFF;"弹得更强" → pop_peak 145。Agent 看 SKILL.md 的 NL 映射表自己解读。
payload.instruction
style_overrides priority · high
专家通道。直接给 JSON,精确锁定某个参数。覆盖 instruction 的解读结果。适合"我已经知道想要什么"。
payload.style_overrides
# 三种典型 payload

# A. 只选模板,什么都不调 — 直接用默认
{ "subtitle_template": "pop_kara" }

# B. 自然语言微调
{ "subtitle_template": "pop_kara",
  "instruction": "改蓝色高亮,弹得更强,字大一号" }

# C. instruction 不够精确 — 加 overrides 锁死关键值
{ "subtitle_template": "glow",
  "instruction": "低调点,别太炫",
  "style_overrides": { "base_outline_color": "#4A0099" } }
Templates

5 套模板:从干净到爆款

每个模板是一段确定性的 ASS 标签生成规则 + 一份参数表。Agent 选哪个,看用户填的 subtitle_template;若选了 "auto",Agent 自己读 instruction + 视频内容决定。

clean
干净专业
整句切分,简单淡入,白字黑描。句级 SRT 也能用。
"专业 / 知识科普 / 不抢戏"
karaoke
卡拉OK 高亮
静态显示,词级 \kf 平滑填充由白变黄。播客/采访常用。
"逐词高亮 / 跟读感"
pop
Q 弹弹出
词级,0% → 130% 过冲 → 100% 回弹。弹簧动画。
"一个一个蹦出来 / 弹性"
pop_kara
Q 弹 + 颜色
弹入瞬间黄色,落地变白。抖音/Reels 爆款风。
"抖音 / 短视频爆款 / 节奏感"
glow
霓虹脉冲
紫红厚描边,当前词描边色脉冲到青绿。游戏/赛博。
"游戏 / 赛博 / 霓虹"
模板核心 ASS 标签关键可调参数SRT 要求
clean\fad(120,150) + 整句换行fontsize · primary_color · outline_color句级或词级
karaoke\kf<cs> 逐词primary_color · secondary_color仅词级
pop\alpha\fscx\fscy + \t() 三段pop_peak · pop_up_ms · pop_settle_ms仅词级
pop_karapop + \1c 色切primary_color · settle_color · pop_peak · fontsize_scale仅词级
glow\3c\bord + \t() 脉冲base_outline · flash_outline · flash_bord_boost仅词级
Adaptive Rules

自动适配:画幅 · 分辨率 · 语种

用户不需要选这些。Skill 探测视频和 SRT,自动决定字号、留白、行宽、字体。 所有数值假设短边 1080 基线;不同分辨率按比例缩放。

画幅 (Aspect)

portrait 9:16marginv 260
landscape 16:9marginv 70
square 1:1marginv 140
fontsize 默认64 / 48 / 56
每行字符上限22 / 46 / 30

分辨率 (Resolution)

4K 短边 2160scale 2.0×
1080 短边scale 1.0× · 基线
720 短边scale 0.67×
540 短边scale 0.50×
缩放对象fontsize · outline · shadow · margin

语种 + 字体

cjk 中/日/韩Noto Sans CJK SC
cyrillic 俄/乌Montserrat-Bold
latin 英/西/法Montserrat-Bold
arabic 阿Noto Sans Arabic · RTL
thai 泰Noto Sans Thai
踩过的坑(必写进 SKILL.md): pysubs2 默认不写 PlayResX/PlayResY,libass 会按 384×288 缩放整个坐标系, 导致字幕被推出画面顶部。脚本必须显式把 PlayRes 设成视频实际分辨率。
Fontsize Control

字号:三层都可改

"字大一点"也能改,"我就要 80px"也能改。最终值 = template_default × fontsize_scale × resolution_scale,除非用 absolute 锁死。

用户说什么fontsize_scaleportrait 上实际像素
(什么都没说)1.0064
"字大一点" / "字大一号"1.1574
"字大很多" / "再大点"1.3083
"字超大" / "字最大"1.4593
"字小一点"0.8554
"更紧凑" / "字小点"0.7548
(精确值) style_overrides.fontsize: 80(绕过)80
Dependencies

依赖:Host 预装,Task 时只检查

v106 模式 — 不在任务运行时 pip install 或 curl 字体。所有 binary/包都在 worker host 准备好,workflow 的 hooks.after_create 跑 fail-fast 检查,缺则不让任务启动。

依赖类型位置暴露after_create 检查
系统二进制 /usr/bin/ffmpeg · ffprobe PATH command -v ffmpeg
Python + pysubs2 /opt/tobatsu/venvs/subtitle-effect-renderer/bin/python $SUBTITLE_RENDERER_PYTHON "$PY" -c "import pysubs2"
字体集 /opt/tobatsu/assets/subtitle-fonts $SUBTITLE_FONT_DIR fc-match "Noto Sans Arabic"
失败模式: 任何一项缺,workflow 报 dependency_unavailable, Agent 不启动。错误清晰可追,不会让 Agent 跑到一半因为缺字体崩。

* 字体不入 skill_revisions 表(管理 skill 仅 UTF-8 文本),companion 文件只放 .md/.py。

Execution

Agent 在 task 里干的事

Agent 收到 payload,读 .tobatsu/skills/subtitle-effect-renderer/SKILL.md, 跑 companion 脚本,上传产物。整条流水线 7 步。

01
Probe
ffprobe 视频 → w/h/duration
aspect
02
Detect
SRT 语种 + 词级/句级
font · cpl
03
Resolve
合并 3 层参数
params_used
04
Group
词 → 短语 (≤2 行)
N chunks
05
ASS
套模板生成 .ass
PlayResX/Y
06
Burn-in
ffmpeg + libass
out.mp4
07
Upload
output upload → CDN
result.json
# Agent 实际跑的命令
$SUBTITLE_RENDERER_PYTHON \
  .tobatsu/skills/subtitle-effect-renderer/scripts/render_subtitles.py \
  --video=$workspace/inputs/source.mp4 \
  --srt=$workspace/inputs/source.srt \
  --template=pop_kara \
  --params='{"primary_color":"#00BFFF","fontsize_scale":1.15}' \
  --fontsdir=$SUBTITLE_FONT_DIR \
  --out=$workspace/outputs/video/sample_sub.mp4 \
  --result=$workspace/outputs/result.json

tobatsu-agent output upload $workspace/outputs/video/sample_sub.mp4 \
  --label "subtitled-video" --content-type video/mp4
tobatsu-agent complete --summary "pop_kara · 33 chunks · 59s"
Output Contract

result.json:每个值都可追溯

每个最终参数都标注来自哪一层。出问题倒查 5 秒能定位是 instruction 解读偏了、 overrides 写错了,还是 template 默认值不合理。

{
  "template_resolved": "pop_kara",
  "template_source": "explicit",      // "explicit" | "auto-from-instruction"

  "params_used": {
    "primary_color":   "#00BFFF",
    "settle_color":    "#FFFFFF",
    "pop_peak":        145,
    "fontsize":        74,
    "fontsize_scale":  1.15
  },
  "params_source": {
    "primary_color":   "instruction",   // "蓝色高亮"
    "settle_color":    "default",
    "pop_peak":        "instruction",   // "弹得更强"
    "fontsize_scale":  "instruction",   // "字大一号"
    "fontsize":        "computed"      // = default × scale × res
  },

  "video": {
    "url":        "https://cdn-asia.../sample_sub.mp4",
    "width":      1080,
    "height":     1920,
    "duration_ms": 59200,
    "size_bytes":  19450427
  },
  "ass_file":    "outputs/subtitles/final.ass",
  "chunks":      33,
  "script_detected": "latin",
  "aspect":        "portrait"
}
Acceptance

第一版要跑完的 6 个用例

覆盖语种 × 画幅 × 分辨率 × 模板的关键组合。每个用例都要看到字幕位置、字号、语种渲染都对。

* 阿拉伯是硬要求,要测 RTL 方向 + 字形重塑 + 卡拉OK 方向 + 组合符号上下行间距,缺一项不算 done。