OpenMontage Avatar Video 技能实战:HeyGen v2 视频模板与批量个性化视频生成完全指南
本文基于 OpenMontage 仓库中 avatar-video 技能的模板参考文档 templates.md,系统讲解 HeyGen v2 模板 API 的完整用法:模板列表与详情查询、变量替换生成、三类变量(文本/图像/音频)、批量个性化生成与请求前校验,并结合仓库源码说明该能力在 OpenMontage 智能体生产体系中的定位与配套机制。读完本文,你可以直接照抄命令与代码,用 HeyGen 模板 API 构建可复制、可批量、可校验的个性化视频生产线。
1. 技能定位:模板在 OpenMontage avatar-video 技能中的角色
OpenMontage 把 AI 视频生产能力组织为一组 agent 技能文件。其中 avatar-video 技能 基于 HeyGen v2 API,提供对虚拟人、语音、脚本、场景与背景的精确控制,其默认工作流是:
- 列出虚拟人 —
GET /v2/avatars,选定avatar_id与default_voice_id(详见 avatars.md); - 列出语音 —
GET /v2/voices(详见 voices.md); - 编写脚本 — 每个场景一个概念(详见 scripts.md);
- 生成视频 —
POST /v2/video/generate(详见 video-generation.md); - 轮询完成状态 —
GET /v2/videos/{video_id}(详见 video-status.md)。
在上述"逐帧精确控制"的路线之外,HeyGen 提供了另一条模板化路线:预先设计好带占位变量的视频结构,之后只需替换变量即可大规模生成个性化视频。技能文件的 Quick Reference 表中明确将"Use templates"映射到本文所讲解的 templates.md。两条路线的取舍在技能文件中有清晰说明——"描述一个视频想法让 AI 全权处理"用 create-video 技能;"指定虚拟人精确说出指定台词、批量按精确规格生成"则属于本技能范畴。
1.1 认证与环境变量
所有请求都需要 X-Api-Key 请求头,统一通过环境变量 HEYGEN_API_KEY 提供。技能元数据声明了 HEYGEN_API_KEY 为必需环境变量(见 SKILL.md 的 metadata.openclaw.requires.env)。在 OpenMontage 的工具层,heygen_video.py 同样遵循该约定:get_status() 检查 HEYGEN_API_KEY 是否存在,缺失时返回 ToolStatus.UNAVAILABLE,并提示到 https://app.heygen.com/settings/api 获取密钥;同时定义了 RetryPolicy(max_retries=2, backoff_seconds=10.0, retryable_errors=["rate_limit", "timeout", "server_error"]) 的容错策略,这正是第 8 节批量生成中"请求间加延迟"实践的仓库级印证。
1.2 MCP 工具优先原则
技能文件强调:若 HeyGen MCP 工具可用(mcp__heygen__*),优先使用而非直接 HTTP 调用——它们自动处理认证与请求格式。不过模板相关端点(/v2/templates)与视频生成(POST /v2/video/generate)走的是直接 API 调用,这也是本文所有示例的形式。
2. 模板数据模型
HeyGen 模板允许创建带变量占位符的可复用视频结构,从而支持大规模个性化视频生成。参考文档定义了三层 TypeScript 类型,完整刻画了模板的数据结构:
interface Template {
template_id: string;
name: string;
thumbnail_url: string;
variables: TemplateVariable[];
}
interface TemplateVariable {
name: string;
type: "text" | "image" | "audio";
properties?: {
max_length?: number;
default_value?: string;
};
}
interface TemplatesResponse {
error: null | string;
data: {
templates: Template[];
};
}
三个关键设计点值得注意:
- 变量三态:每个
TemplateVariable的type限定为"text" | "image" | "audio",与第 5 节三类变量示例一一对应; - 约束内嵌于模板:
properties.max_length与properties.default_value由模板定义方声明,调用方应在生成前据此校验(见第 7 节); - 统一错误信封:所有响应都用
{ error: null | string, data: ... }包裹,error非空即代表请求级失败。
3. 列出模板(Listing Templates)
3.1 curl
curl -X GET "https://api.heygen.com/v2/templates" \
-H "X-Api-Key: $HEYGEN_API_KEY"
3.2 TypeScript
listTemplates() 封装了列表请求、错误检查与结果提取:
async function listTemplates(): Promise<Template[]> {
const response = await fetch("https://api.heygen.com/v2/templates", {
headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! },
});
const json: TemplatesResponse = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data.templates;
}
3.3 Python
import requests
import os
def list_templates() -> list:
response = requests.get(
"https://api.heygen.com/v2/templates",
headers={"X-Api-Key": os.environ["HEYGEN_API_KEY"]}
)
data = response.json()
if data.get("error"):
raise Exception(data["error"])
return data["data"]["templates"]
3.4 响应格式
列表接口的完整响应结构(含一个带 3 个变量的"Product Announcement"模板示例):
{
"error": null,
"data": {
"templates": [
{
"template_id": "template_abc123",
"name": "Product Announcement",
"thumbnail_url": "https://files.heygen.ai/...",
"variables": [
{
"name": "product_name",
"type": "text",
"properties": {
"max_length": 50
}
},
{
"name": "presenter_script",
"type": "text",
"properties": {
"max_length": 500
}
},
{
"name": "product_image",
"type": "image"
}
]
}
]
}
}
从示例可以看到:presenter_script 的 max_length 为 500,说明脚本类变量通常被模板设计方赋予较宽松的字数上限,而 product_name 只有 50——这个约束就是第 7 节校验逻辑的输入依据。
4. 获取模板详情(Getting Template Details)
生成前必须知道模板定义了哪些变量。技能文件对此有明确提醒:"variables 对象的键必须与模板定义的变量名匹配,请查看模板详情确认定义了哪些变量"。
curl
curl -X GET "https://api.heygen.com/v2/template/{template_id}" \
-H "X-Api-Key: $HEYGEN_API_KEY"
TypeScript
async function getTemplate(templateId: string): Promise<Template> {
const response = await fetch(
`https://api.heygen.com/v2/template/${templateId}`,
{ headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
);
const json = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data;
}
注意详情接口返回的 json.data 直接就是 Template 对象(不像列表那样多套一层 templates 数组),这是两个端点在响应结构上的差异,实现时不要混淆。
5. 从模板生成视频(Generating Video from Template)
5.1 请求字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
variables |
object | ✓ | 与模板变量一一对应的键值对 |
test |
boolean | 测试模式(带水印,不消耗额度) | |
title |
string | 用于组织归档的视频名称 | |
callback_id |
string | 自定义 ID,用于 webhook 追踪 | |
callback_url |
string | 完成通知的回调 URL |
其中 test: true 的"带水印、不扣额度"语义与技能文件最佳实践第 4 条一致——开发阶段一律用测试模式验证,避免浪费额度;额度的完整规则参见技能配套的 quota.md。callback_url / callback_id 则对接技能的 webhook 机制(见 webhooks.md),适合不想维持轮询连接的生产系统。
5.2 curl
curl -X POST "https://api.heygen.com/v2/template/{template_id}/generate" \
-H "X-Api-Key: $HEYGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"test": false,
"variables": {
"product_name": "SuperWidget Pro",
"presenter_script": "Introducing our latest innovation!",
"product_image": "https://example.com/product.jpg"
}
}'
5.3 TypeScript
interface TemplateGenerateRequest {
variables: Record<string, string>; // Required
test?: boolean;
title?: string;
callback_id?: string;
callback_url?: string;
}
interface TemplateGenerateResponse {
error: null | string;
data: {
video_id: string;
};
}
async function generateFromTemplate(
templateId: string,
variables: Record<string, string>,
test: boolean = false
): Promise<string> {
const response = await fetch(
`https://api.heygen.com/v2/template/${templateId}/generate`,
{
method: "POST",
headers: {
"X-Api-Key": process.env.HEYGEN_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({ test, variables }),
}
);
const json: TemplateGenerateResponse = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data.video_id;
}
5.4 Python
def generate_from_template(template_id: str, variables: dict, test: bool = False) -> str:
response = requests.post(
f"https://api.heygen.com/v2/template/{template_id}/generate",
headers={
"X-Api-Key": os.environ["HEYGEN_API_KEY"],
"Content-Type": "application/json"
},
json={
"test": test,
"variables": variables
}
)
data = response.json()
if data.get("error"):
raise Exception(data["error"])
return data["data"]["video_id"]
生成接口是异步的:立即返回 video_id,视频在后台渲染。后续状态查询、下载与轮询模式完整继承技能的 video-status.md,该文档给出的关键事实是:视频生成通常需要 5–15 分钟,高峰期或长脚本可能超过 20 分钟;建议轮询超时设置为 15–20 分钟(900,000–1,200,000 ms),并以指数退避重试方式下载最终视频。状态机为 pending → processing → completed | failed,失败时响应携带 failure_code 与 failure_message(例如 script_too_long)。
6. 三类变量的取值形态
6.1 文本变量(Text Variables)
用于动态文本内容:
const variables = {
customer_name: "John Smith",
product_name: "SuperWidget Pro",
price: "$99.99",
cta_text: "Order Now!",
};
6.2 图像变量(Image Variables)
用于动态图像(背景、产品图)——值为可公网访问的 URL:
const variables = {
product_image: "https://example.com/product.jpg",
logo: "https://example.com/logo.png",
background: "https://example.com/bg.jpg",
};
6.3 音频变量(Audio Variables)
用于自定义音频内容:
const variables = {
background_music: "https://example.com/music.mp3",
custom_voiceover: "https://example.com/voiceover.mp3",
};
三类变量的共同规律是:非文本变量一律使用 URL 而非本地文件。如果手头是本地素材,需要先走技能的资产上传流程(assets.md)获得可访问地址。
7. 生成前校验:把错误挡在 API 之外
参考文档给出的 validateTemplateVariables 实现了三层防御:缺变量检测、文本长度上限检测(依据模板声明的 max_length)、图像 URL 合法性检测:
function validateTemplateVariables(
template: Template,
variables: Record<string, string>
): { valid: boolean; errors: string[] } {
const errors: string[] = [];
for (const templateVar of template.variables) {
const value = variables[templateVar.name];
// Check if required variable is provided
if (!value) {
errors.push(`Missing required variable: ${templateVar.name}`);
continue;
}
// Check text length limits
if (templateVar.type === "text" && templateVar.properties?.max_length) {
if (value.length > templateVar.properties.max_length) {
errors.push(
`Variable "${templateVar.name}" exceeds max length of ${templateVar.properties.max_length}`
);
}
}
// Validate image URLs
if (templateVar.type === "image") {
try {
new URL(value);
} catch {
errors.push(`Variable "${templateVar.name}" is not a valid URL`);
}
}
}
return {
valid: errors.length === 0,
errors,
};
}
该校验的价值在于:一次生成可能要等待 5–15 分钟且消耗额度,而一次变量缺失或超长只会在渲染中途以 failed 状态暴露(如 script_too_long),前期校验能把这类浪费降到零。它同时呼应了技能文件最佳实践第 6 条——"生成前校验 avatar 与 voice ID 是否真实存在",校验思想贯穿整个 avatar-video 技能。
8. 批量个性化生成(Batch Generation)
模板的真正威力在于"一份模板 × N 份数据"。参考文档的批量生成实现包含两个生产级细节:逐条提交后收集 videoId 与 请求间 1 秒限流延迟:
interface PersonalizationData {
name: string;
email: string;
company: string;
customMessage: string;
}
async function batchGenerateVideos(
templateId: string,
recipients: PersonalizationData[]
): Promise<string[]> {
const videoIds: string[] = [];
for (const recipient of recipients) {
const variables = {
recipient_name: recipient.name,
company_name: recipient.company,
personalized_message: recipient.customMessage,
};
const videoId = await generateFromTemplate(templateId, variables);
videoIds.push(videoId);
// Rate limiting: add delay between requests
await new Promise((r) => setTimeout(r, 1000));
}
return videoIds;
}
// Usage
const recipients = [
{
name: "John Smith",
email: "john@example.com",
company: "Acme Inc",
customMessage: "Thanks for your interest in our product!",
},
{
name: "Jane Doe",
email: "jane@example.com",
company: "Tech Corp",
customMessage: "We'd love to show you a demo!",
},
];
const videoIds = await batchGenerateVideos("template_abc123", recipients);
这里的限流策略与 OpenMontage 工具层的实现相互印证:heygen_video.py 将 rate_limit 列为可重试错误、退避间隔 10 秒,说明 HeyGen API 对高频请求存在真实的限流约束,批量场景必须主动控速。批量任务还应配合第 5.1 节的 callback_id:为每位收件人分配不同 ID,webhook 回调即可精确定位是哪条视频完成,避免轮询 N 个 video_id。
9. 完整模板工作流(Complete Template Workflow)
参考文档将上述所有环节组装成一条端到端管线:拉详情 → 校验 → 生成 → 等待完成:
async function createPersonalizedVideo(
templateId: string,
personalization: Record<string, string>
): Promise<string> {
// 1. Get template details
const template = await getTemplate(templateId);
console.log(`Using template: ${template.name}`);
// 2. Validate variables
const validation = validateTemplateVariables(template, personalization);
if (!validation.valid) {
throw new Error(`Validation errors: ${validation.errors.join(", ")}`);
}
// 3. Generate video
console.log("Generating video...");
const videoId = await generateFromTemplate(templateId, personalization);
console.log(`Video ID: ${videoId}`);
// 4. Wait for completion
const videoUrl = await waitForVideo(videoId);
console.log(`Video ready: ${videoUrl}`);
return videoUrl;
}
// Usage
const videoUrl = await createPersonalizedVideo("template_abc123", {
customer_name: "John Smith",
product_name: "SuperWidget Pro",
offer_details: "Get 20% off your first order!",
});
其中第 4 步的 waitForVideo(videoId) 即技能 video-status.md 提供的轮询实现(默认 10 分钟超时、5 秒轮询间隔,超时抛 "Video generation timed out")。生产环境建议按该文推荐把超时放宽到 15–20 分钟,并对 completed 后的下载加指数退避重试——因为视频 URL 在状态变为 completed 后可能不会立即可用。长任务还可采用文档中的"断点续查"模式:生成后立即保存 video_id 到本地状态文件,进程退出,后续再检查状态,避免长时间挂起一个等待进程。
10. 最佳实践与典型用例
参考文档总结的 7 条最佳实践:
- 为灵活性设计 — 用通用占位符创建模板;
- 设置合理上限 — 为文本变量定义
max_length; - 校验输入 — 生成前检查变量值(第 7 节);
- 使用测试模式 — 先用
test: true验证再进入生产(水印、不扣额度); - 实现限流 — 批量生成时请求间加延迟(第 8 节);
- 缓存模板数据 — 减少重复的模板详情 API 调用;
- 处理错误 — 优雅地处理生成失败。
典型用例覆盖:
- 销售触达 — 面向潜在客户的个性化视频;
- 客户入职 — 带客户姓名的欢迎视频;
- 产品更新 — 含动态内容的发布公告;
- 培训 — 定制化培训模块;
- 营销campaign — 定向促销视频。
11. 小结:模板路线与精确控制路线如何互补
在 OpenMontage 的 avatar-video 技能体系中,templates.md 所代表的模板路线与 POST /v2/video/generate 的逐场景精确控制路线互补:前者以"变量替换"换取规模化与一致性,适合销售触达、客户入职等数据驱动场景;后者以显式的 avatar/voice/script/background 配置换取逐场景艺术控制。两者共享同一套工程底座——HEYGEN_API_KEY 认证(技能元数据强制声明)、video_id 异步状态机(pending → processing → completed | failed)、5–15 分钟量级的生成时长预期,以及技能配套的 quota.md(额度规则)与 webhooks.md(完成回调)。仓库工具层 heygen_video.py 中"密钥缺失即 UNAVAILABLE、rate_limit 可重试、退避 10 秒"的声明,为本文所有限流与容错建议提供了来自实际运行代码的佐证。按本文顺序实现——列表 → 详情 → 校验 → 生成 → 轮询下载——即可搭起一条可复制、可批量、可观测的 HeyGen 模板化视频生产线。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00