我是如何通过 TRAE Work 做到从 PNG 到 SVG,实现主题感知人物渲染动态视频制作的

一篇关于在 TRAE Work 中与 AI 协作,将视频项目中的静态角色图片替换为完全程序化生成的 SVG 人物,并根据文章内容主题动态切换服装、配饰、道具与配色方案的完整记录。

起因:两个绕不开的问题

retro-anime-video 是我维护的一个 Skill,用来生成 90 年代日漫风格的知识讲解视频。它的原始设计很简单——通过 Python 脚本预生成角色 PNG 图片(蜡笔小新等知名 IP 形象),再丢进 Remotion 渲染成视频。这套方案跑了大半年,一直相安无事,直到两个问题越来越刺眼。

第一个是版权。蜡笔小新是臼井仪人工作室的角色,直接拿来用在视频里,严格来说是侵权。做个人项目无所谓,但一旦想对外发布或商业化,这就是一颗定时炸弹。

第二个更隐蔽,也更致命——主题脱节。无论视频讲的是大模型、金融理财还是健康养生,画面里站着的永远是同一个红衣黄裤的小男孩。观众看第一集觉得有趣,看第五集就开始出戏,因为角色和内容之间没有任何关联。

上图是优化前的标题场景,角色是预渲染的蜡笔小新 PNG 图片,无论视频内容是什么,角色的外观都完全相同。

对比场景也是一样,角色无法根据"传统 vs TRAE"的对比内容做任何外观适配。

我需要一套完全不同的方案:不引用任何外部图片,用代码画出人物,并且让人物的外观跟着视频内容变。

对话开始:向 AI 提出需求

我在 TRAE Work 里打开了 retro-anime-video 项目,给 AI 发了第一条指令:

:深度优化 Skills:关于人物的渲染,不再使用 PNG,而是改为 SVG 程序化生成,根据文章主题内容来生成人物;

这条指令包含三个关键约束:弃用 PNG、改用 SVG 程序化生成、根据文章主题动态匹配人物外观。AI 收到后,没有急着写代码,而是先分析了整个项目的架构,定位到三个需要修改的核心文件:

  • CustomCharacter.tsx — SVG 渲染引擎,负责用路径画出人物
  • ContentAwareDecor.tsx — 内容分析引擎,负责从文本中提取主题
  • CharacterHost.tsx — 桥接层,把角色组件接入 Remotion 场景

接下来,AI 开始逐一实现这些模块。

系统架构设计

AI 设计的数据流很清晰:场景文本先经过关键词匹配识别主题,主题映射到人物配置(服装、配饰、道具、发型、配色),配置再驱动 SVG 渲染引擎画出完整人物。

三个模块各自独立,职责分明。ContentAwareDecor 只管分析文本输出主题,CustomCharacter 只管根据配置画 SVG,CharacterHost 把两者粘合到 Remotion 的场景系统里。这种解耦意味着以后扩展新主题,只需要加一行配置和一组关键词,不用动渲染引擎。

内容分析引擎:让 AI 听懂文章在说什么

AI 首先实现的是内容分析引擎。思路很直接——关键词匹配。每个主题维护一组中英文关键词,文本里命中任意一个,就算这个主题命中。

const THEME_KEYWORDS: Record<string, string[]> = {
  ai_model: ['模型', '开源', '参数', '训练', '推理', '大模型', 'LLM',
    'Kimi', 'K3', 'GPT', 'Claude', 'GLM', 'Moonshot', '月之暗面',
    'AGI', 'Agent', '智能体', '多模态', '视觉理解'],
  tech: ['技术', '代码', '程序', '数据', '算法', 'Python',
    'transformers', '核心', '功能', '实战', '优化', '延迟', '吞吐'],
  knowledge: ['知识', '学习', '什么是', '总结', '概念', '原理', '理解'],
  finance: ['金融', '经济', '市场', '股票', '基金', '理财', '利率'],
  // ... 共 19 个主题
};

匹配函数本身只有十几行:

export const analyzeContentThemes = (text: string): string[] => {
  const matchedThemes: string[] = [];
  const lowerText = text.toLowerCase();
  for (const [theme, keywords] of Object.entries(THEME_KEYWORDS)) {
    for (const kw of keywords) {
      if (lowerText.includes(kw.toLowerCase())) {
        if (!matchedThemes.includes(theme)) {
          matchedThemes.push(theme);
        }
        break;
      }
    }
  }
  return matchedThemes;
};

一段文本可能同时命中多个主题。比如"Kimi K3 是月之暗面发布的开源大模型"这句话,同时命中 ai_model(因为"Kimi""K3"“月之暗面”“开源”“大模型”)和 tech(因为"模型"也在 tech 的关键词列表里)。AI 设计了一个优先级数组来处理这种情况:

const THEME_PRIORITY = [
  'ai_model', 'tech', 'hardware',
  'knowledge',
  'finance', 'benchmark', 'value',
  'space', 'nature', 'health',
  'music', 'energy', 'success',
  'emotion', 'communication',
  'time', 'journey', 'idea',
  'structure', 'growth',
];

AI 模型类主题排在最前面,因为这类内容最需要精准的角色匹配——讲大模型的视频如果角色穿的是运动服,违和感太强了。

主题到人物的映射:19 套外观配置

分析出主题后,需要把它转化成具体的人物外观。AI 设计了一个 CharacterConfig 数据结构,定义了五个维度的外观属性:

type ClothingType = 'hoodie' | 'sweater_vest' | 'suit' | 'tshirt'
  | 'lab_coat' | 'jacket' | 'sporty' | 'cardigan' | 'default';
type AccessoryType = 'glasses' | 'reading_glasses' | 'goggles' | 'headphones'
  | 'headband' | 'medal' | 'watch' | 'bracelet' | 'none';
type PropType = 'tablet' | 'book' | 'chart' | 'dumbbell' | 'telescope'
  | 'microphone' | 'trophy' | 'compass' | 'speech_bubble' | 'none';

interface CharacterConfig {
  clothing: ClothingType;
  accessory: AccessoryType;
  prop: PropType;
  hair: HairStyle;
  pattern: PatternType;
  clothColor: string;
  accentColor: string;
  hairColor: string;
}

然后是一张 19 行的映射表,每行对应一个主题:

主题 服装 配饰 道具 适用场景
ai_model 连帽衫 眼镜 平板 大模型介绍、AI 产品发布
tech 连帽衫 眼镜 平板 技术教程、编程实战
knowledge 毛衣背心 阅读眼镜 书本 知识科普、概念解释
finance 西装 手表 图表 金融、经济、市场
space 实验服 护目镜 望远镜 太空、宇宙、星辰
nature 运动T恤 发带 哑铃 自然、生态、环境
music 夹克 耳机 麦克风 音乐、旋律、节奏
success 运动外套 奖牌 奖杯 超越、登顶、突破

映射表里的每个条目就是一行配置。科技主题对应的配置如下:

tech: {
  clothing: 'hoodie', accessory: 'glasses', prop: 'tablet',
  hair: 'messy', pattern: 'circuit',
  clothColor: INK.indigo, accentColor: INK.blue, hairColor: INK.dark,
},

getCharacterConfig 函数把分析和映射串联起来,返回最终的人物配置:

export const getCharacterConfig = (content: string): CharacterConfig => {
  const themes = analyzeContentThemes(content);
  if (themes.length === 0) return DEFAULT_CONFIG;
  for (const theme of THEME_PRIORITY) {
    if (themes.includes(theme)) {
      return THEME_CHARACTER_MAP[theme] || DEFAULT_CONFIG;
    }
  }
  return DEFAULT_CONFIG;
};

SVG 渲染引擎:用代码画出手绘风格的人物

这是整个系统最硬核的部分,AI 在 CustomCharacter.tsx 里写了约 1300 行代码。

手绘质感的秘密:种子随机抖动

SVG 路径画出来的线条天然过于"完美"——数学上精确的圆和直线,看起来像 CAD 制图,不像手绘。AI 的解决方案是引入种子随机数,给每个路径点加一点抖动:

const seededRandom = (seed: number): number => {
  const x = Math.sin(seed * 12.9898 + 78.233) * 43758.5453;
  return x - Math.floor(x);
};

const jitter = (seed: number, amount: number): number => {
  return (seededRandom(seed) - 0.5) * amount;
};

基于这两个函数,AI 实现了 wobblyCirclewobblyLinewobblyEllipse 三个基础路径生成器。以 wobblyCircle 为例,它把一个圆拆成 12 段,每段的半径加一点随机偏移,再用二次贝塞尔曲线连接,画出来的圆就带着手绘特有的不规则感。同一种子永远产生相同的抖动,所以每一帧渲染出来的人物是一致的,不会闪烁。

五个模块化渲染函数

人物被拆成五个独立的部分,每个部分由一个渲染函数负责:

  • renderClothing() — 9 种服装(连帽衫、毛衣背心、西装、T恤、实验服、夹克、运动外套、开衫、默认)
  • renderHair() — 8 种发型(凌乱、整齐、尖刺、马尾、蓬松、时尚、波浪、默认)
  • renderAccessory() — 9 种配饰(眼镜、阅读眼镜、护目镜、耳机、发带、奖牌、手表、手链、无)
  • renderProp() — 10 种道具(平板、书本、图表、哑铃、望远镜、麦克风、奖杯、指南针、对话气泡、无)
  • renderPattern() — 8 种图案(电路、条纹、圆点、叶子、音符、火焰、罗盘、无)

每种服装共用一个基础身体路径,然后在上面叠加特征元素。以连帽衫为例,AI 在基础身体上画了帽兜填充、帽兜内阴影、帽绳、帽绳末端的圆球、口袋轮廓和拉链线条——六层细节叠在一起,让连帽衫在低透明度下也能辨识。

主组件装配

CustomCharacter 组件把五个部分组装起来,还加了三种动画效果:

export const CustomCharacter: React.FC<CustomCharacterProps> = ({
  pose, frame, scale = 1, accentColor, content = '',
}) => {
  const config = React.useMemo(() => getCharacterConfig(content), [content]);

  const breath = 1 + Math.sin(frame * 0.05) * 0.015;  // 呼吸
  const sway = Math.sin(frame * 0.04) * 3;              // 摇摆
  const entranceDur = 18;
  const entranceP = Math.min(frame / entranceDur, 1);   // 入场弹跳
  const bounce = entranceP < 1
    ? 0.3 + 0.8 * entranceP + 0.1 * Math.sin(entranceP * Math.PI)
    : 1;

  return (
    <svg width="320" height="420" viewBox="0 0 320 420">
      {renderClothing(config, cx, cy, seed, sw, color)}
      {renderPattern(config, cx, cy, seed, sw)}
      {renderHair(config, cx, cy, seed, sw, color)}
      {renderAccessory(config, cx, cy, seed, sw, color)}
      {renderProp(config, cx, cy, seed, sw, color, handX, handY, pose)}
    </svg>
  );
};

组件支持 12 种姿势(pose),每种姿势定义不同的手臂角度和表情组合:wave(挥手)、explain(讲解)、think(思考)、write(书写)、jump(跳跃)等。

场景集成:让 9 个场景都传 content

9 个场景模板需要把各自的内容文本传给角色组件。AI 修改了每个场景文件中 CharacterHost 的调用,添加 content 属性:

// TitleCard.tsx
<CharacterHost
  style={style}
  pose={pose}
  frame={frame}
  position="center"
  scale={1}
  content={`${title} ${subtitle}`}
/>

每个场景传入的 content 内容不同:

场景 传入的 content 角色位置
TitleCard 标题 + 副标题 center
CodeBlockScene 标题 + 代码 bottom_left
QuoteScene 引言 + 说话人 bottom_right
ComparisonScene 标题 + 左右标签 + 内容 bottom_center

以 Kimi K3 视频为例,标题是"Kimi K3 发布",副标题是"月之暗面旗舰开源大模型"。关键词"Kimi""K3"“月之暗面”“开源”"大模型"全部命中 ai_model 主题,角色自动穿戴连帽衫和眼镜、手持平板。

第一次验收:AI 给出了错误的结果

代码写完后,我让 AI 渲染视频看看效果:

:提供一下最新 Skills 生成的视频内容

AI 渲染了视频并截取了帧。但我看了一眼就发现问题——画面里的角色虽然换了新衣服,但怎么看都还是之前那个蜡笔小新的 PNG 图片,不是 SVG 程序化生成的。我立刻指出了这个问题:

:不对,这个版本不正确,已经没有 PNG,全是 SVG 动态生成的内容了

AI 重新检查了渲染管线,发现 CharacterHost.tsx 的 re-export 指向了旧的 PNG 组件,而不是新的 CustomCharacter。修正桥接层后重新渲染,这次出来的画面才是真正的 SVG 人物。

深度优化:从"几乎不可见"到"清晰可辨"

第一版 SVG 人物渲染出来后,问题比想象的严重——角色几乎看不见。服装填充透明度太低(只有 14.5%),道具尺寸太小(眼镜半径只有 12px),关键词覆盖不足导致很多场景退化为基础休闲装。AI 花了四轮优化才把效果拉上来。

第一轮:提升填充透明度。 服装填充从 ${clothColor}25(约 14.5%)提升到 ${clothColor}55(约 33%),关键元素填充提升到 ${clothColor}70(约 44%)。

第二轮:增大配饰和道具尺寸。 眼镜镜框半径从 12px 增加到 16px,平板宽度从 35px 增加到 48px,书本尺寸从 30x35px 增加到 40x48px。

第三轮:丰富服装细节。 连帽衫增加了帽兜内阴影、口袋轮廓和拉链线条;西装增加了口袋方巾;实验服增加了纽扣和侧口袋。

第四轮:扩展关键词覆盖。 ai_model 主题的关键词从 7 个扩展到 30 个,加入了"Kimi""K3"“GPT”“Claude”“GLM”“Moonshot”"月之暗面"等品牌名和产品名,确保主流 AI 内容都能命中。

最终效果

四轮优化后,我再次让 AI 渲染视频。这次的效果终于达到了预期——角色清晰可见,主题特征明确。

标题场景中,内容包含"Kimi K3 发布"和"月之暗面旗舰开源大模型",命中 ai_model 主题。角色穿戴蓝色连帽衫、圆形眼镜,手持平板电脑。人物由纯 SVG 路径绘制,没有任何外部图片。

数据展示场景中,角色佩戴眼镜、手持指示棒和平板,呈现科技讲解员形象。注意角色的服装颜色和配饰都与 AI 主题匹配。

知识科普场景中,内容包含"什么是"“概念”"原理"等关键词,角色穿戴蓝色连帽衫、佩戴眼镜,手持平板。

数据详情场景中,角色手持指示棒指向数据卡片,服装和配饰的细节都清晰可辨。

和优化前的 PNG 版本对比,变化是根本性的:

  • 版权安全 — 所有人物元素由 SVG 代码生成,不引用任何第三方角色形象
  • 主题感知 — 19 个主题覆盖 AI、技术、金融、知识、太空、音乐等领域,根据场景内容自动切换外观
  • 视觉质量 — 手绘抖动路径、多层级细节、呼吸和入场动画,人物从"几乎不可见"提升到"主题特征清晰可辨"

打包与分发

效果确认后,我让 AI 把项目打包成可分发的 zip:

:提供 Skills 下载的 zip 包

AI 排除了 node_modulesbuild 缓存和大体积视频文件,打包后的 zip 包含 Skill 定义文件(SKILL.md)、Python 管线脚本、Remotion 渲染引擎和所有 SVG 组件源码。

渲染配置也踩了几个坑。Remotion 报 “concurrency is set higher than the amount of CPU cores available”,需要把并发数从 3 降到 2。Chrome 路径不存在的问题通过安装 google-chrome-stable 的 deb 包解决。最终的渲染命令是:

npx remotion render AnimeVideo /workspace/kimi-k3-latest.mp4 --concurrency=2

回顾:和 AI 协作的体会

这次优化从提出需求到最终交付,整个过程都在 TRAE Work 里完成。我发了五条指令,AI 每条都理解到位并执行:

第一条"深度优化 Skills"——AI 分析了项目架构,设计了三模块解耦方案,实现了 1300 行 SVG 渲染引擎。

第二条"提供视频内容"——AI 渲染了视频,但结果有问题。

第三条"不对,这个版本不正确"——我纠正了方向,AI 修正了桥接层并重新渲染。

第四条"提供 zip 包"——AI 打包了项目。

第五条"整理成教程文章"——就是这篇文章本身。

整个过程里最有价值的不是 AI 一次写对了多少代码,而是它对需求的拆解能力。我说"根据文章主题内容来生成人物",AI 自己推导出了"需要内容分析引擎 + 主题映射表 + 模块化渲染函数"三层架构,这个设计比我自己想的都清楚。

当然也有不足。第一版渲染出来角色几乎不可见,说明 AI 对视觉效果的实际呈现缺乏预判——它知道代码逻辑是对的,但不知道 14.5% 的透明度在浅色背景上会是什么效果。这种"代码正确但效果不对"的情况,需要人类来验收和反馈。我在第三条指令里指出的问题,本质上就是在做这一层质量控制。

如果要做类似的项目,我的建议是:把需求说清楚(弃用 PNG、SVG 生成、主题感知),让 AI 设计架构和写代码,但每一版都要自己看实际渲染效果,用具体的反馈推动优化。AI 写代码快,但"看起来对不对"这件事,目前还是人说了算。