OpenMontage avatar-video 技能实战:HeyGen 数字人形象选择、预览与 default_voice_id 策略全解
本篇指南基于 OpenMontage 仓库中 avatar-video 技能包的核心参考文档 avatars.md,系统讲解 HeyGen v2 API 中数字人(Avatar)的完整操作链路:如何列出与预览形象、通过 avatar_id 前缀区分公共/自定义形象、为不同视频格式选择 avatar_style、以及为什么应优先使用形象的 default_voice_id 而不是手动配声音。读完本文,你可以在 OpenMontage 的 Agent 工作流(或自己的脚本)中独立完成“选形象 → 预览确认 → 取默认音色 → 生成视频”的可靠流程。
avatars.md 在 OpenMontage 中的位置
.agents/skills/avatar-video/ 是 OpenMontage 中专门处理“精确控制型”数字人视频的 Agent 技能包,其入口 SKILL.md 定义了五步默认工作流:列出形象 → 列出声音 → 撰写脚本 → 生成视频 → 轮询状态,而 avatars.md 正是第 1 步的参考实现:
- List avatars —
GET /v2/avatars→ pick an avatar, preview it, noteavatar_idanddefault_voice_id。见 avatars.md- List voices —
GET /v2/voices。见 voices.md ……- Generate the video —
POST /v2/video/generate。见 video-generation.md
所有请求都需要 X-Api-Key 请求头,因此运行前必须设置环境变量:
export HEYGEN_API_KEY=your_key_here
SKILL.md 还规定:如果环境中存在 HeyGen MCP 工具(mcp__heygen__*),应优先使用它们(例如 mcp__heygen__get_video 查询状态),而“列出形象/声音”和“生成视频”则通过直接 API 调用完成——本文覆盖的就是这两类直接调用。
预览先行:生成视频前先看形象
数字人视频的核心风险是“生成后才发现问题形象不符合预期”,因此文档给出的第一条纪律是:生成前先预览。每个形象都带 preview_image_url(静态图)和 preview_video_url(动画短视频),两者都是公开可访问的 URL,无需下载、无需鉴权,可直接在浏览器打开:
| 字段 | 说明 |
|---|---|
preview_image_url |
形象的静态图片(JPG),公开 URL |
preview_video_url |
展示形象动画的短视频片段 |
文档提供的 TypeScript 预览函数,一次列出前 5 个形象并输出预览地址:
async function listAndPreviewAvatars(openInBrowser = true): Promise<void> {
const response = await fetch("https://api.heygen.com/v2/avatars", {
headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! },
});
const { data } = await response.json();
for (const avatar of data.avatars.slice(0, 5)) {
console.log(`\n${avatar.avatar_name} (${avatar.gender})`);
console.log(` ID: ${avatar.avatar_id}`);
console.log(` Preview: ${avatar.preview_image_url}`);
}
// Preview URLs can be opened directly in any browser
for (const avatar of data.avatars.slice(0, 3)) {
console.log(`Open in browser: ${avatar.preview_image_url}`);
}
}
完整的“先预览后生成”工作流
- 列出可用形象 —— 获取名称、性别与预览 URL;
- 向用户展示预览 URL —— 分享
preview_image_url做视觉确认; - 用户按名称或 ID 选定形象;
- 获取形象详情,拿到
default_voice_id; - 用选定的形象生成视频。
这一步也是 SKILL.md “Best Practices” 的第一条:在生成前先让用户看到形象,避免浪费积分。
列出全部可用形象
curl
curl -X GET "https://api.heygen.com/v2/avatars" \
-H "X-Api-Key: $HEYGEN_API_KEY"
TypeScript(含完整类型定义)
interface Avatar {
avatar_id: string;
avatar_name: string;
gender: "male" | "female";
preview_image_url: string;
preview_video_url: string;
}
interface AvatarsResponse {
error: null | string;
data: {
avatars: Avatar[];
talking_photos: TalkingPhoto[];
};
}
async function listAvatars(): Promise<Avatar[]> {
const response = await fetch("https://api.heygen.com/v2/avatars", {
headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! },
});
const json: AvatarsResponse = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data.avatars;
}
注意响应体中除了 avatars 数组,还包含 talking_photos(照片说话人)数组——character.type 取 "talking_photo" 时使用后者,参见 video-generation.md 中 talking_photo_id 字段的说明。
Python
import requests
import os
def list_avatars() -> list:
response = requests.get(
"https://api.heygen.com/v2/avatars",
headers={"X-Api-Key": os.environ["HEYGEN_API_KEY"]}
)
data = response.json()
if data.get("error"):
raise Exception(data["error"])
return data["data"]["avatars"]
响应格式
{
"error": null,
"data": {
"avatars": [
{
"avatar_id": "josh_lite3_20230714",
"avatar_name": "Josh",
"gender": "male",
"preview_image_url": "https://files.heygen.ai/...",
"preview_video_url": "https://files.heygen.ai/..."
},
{
"avatar_id": "angela_expressive_20231010",
"avatar_name": "Angela",
"gender": "female",
"preview_image_url": "https://files.heygen.ai/...",
"preview_video_url": "https://files.heygen.ai/..."
}
],
"talking_photos": []
}
}
error 字段非空时表示请求失败,应直接把 error 作为错误抛出——文档中的 TS/Python 实现均遵循这一约定。
形象类型:公共形象 vs 自定义形象
HeyGen 的形象库包含任意账号都可使用的公共形象,以及由自己的训练素材生成的自定义形象。区分方式很简单:avatar_id 是否以 custom_ 开头。
// List only public avatars
const avatars = await listAvatars();
const publicAvatars = avatars.filter((a) => !a.avatar_id.startsWith("custom_"));
const customAvatars = avatars.filter((a) => a.avatar_id.startsWith("custom_"));
从文档结构看,自定义形象通常还会进入“形象分组”体系(见下文 Avatar Groups),custom_ 前缀 + 分组接口两者结合,可以在批量生成场景中精确锁定“我的品牌代言人”这一类形象,而不受公共库更新影响。
Avatar Styles:四种渲染风格与适用场景
同一个形象支持不同的 avatar_style 渲染风格,直接决定它在成片中的呈现形态:
| 风格 | 说明 |
|---|---|
normal |
全身/标准取景,常规镜头 |
closeUp |
面部特写,表情表现力更强 |
circle |
圆形取景框(talking head) |
voice_only |
仅音频,不渲染视频 |
风格与使用场景的对应关系:
| 使用场景 | 推荐风格 |
|---|---|
| 全屏出镜视频 | normal |
| 私人化/亲近感内容 | closeUp |
| 画中画(PiP)叠加 | circle |
| 角落小窗组件 | circle |
| 播客/纯音频内容 | voice_only |
| 带动画图形叠加的形象视频 | normal 或 closeUp + 透明背景 |
在视频配置中使用风格
avatar_style 是 video_inputs[].character 下的可选字段,在 POST /v2/video/generate 请求体中这样写:
const videoConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal", // "normal" | "closeUp" | "circle" | "voice_only"
},
voice: {
type: "text",
input_text: "Hello, world!",
voice_id: "1bd001e7e50f421d891986aad5158bc8",
},
},
],
};
Circle 风格 + 绿幕:画中画合成的推荐组合
circle 风格最适合叠加合成场景。文档给出的做法是:形象设为 circle,背景设为绿色 #00FF00 以便色度键抠像(或使用 WebM 透明端点):
// Circle avatar for picture-in-picture
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "circle",
},
voice: { ... },
background: {
type: "color",
value: "#00FF00", // Green for chroma key, or use webm endpoint
},
}
OpenMontage 仓库中恰好有对应的下游处理工具可衔接这个绿幕方案:green_screen_composite.py 与 green_screen_processor.py 负责把绿幕素材抠像后合成到目标画面,skills/core/ffmpeg.md 则提供了 ffmpeg 层的色度键参数参考。
搜索与过滤形象
列表接口返回全量形象,实际选型时通常先按性别或名字做本地过滤。文档给出两个纯函数式工具:
// 按性别过滤
function filterByGender(avatars: Avatar[], gender: "male" | "female"): Avatar[] {
return avatars.filter((a) => a.gender === gender);
}
const maleAvatars = filterByGender(avatars, "male");
const femaleAvatars = filterByGender(avatars, "female");
// 按名称模糊搜索
function searchByName(avatars: Avatar[], query: string): Avatar[] {
const lowerQuery = query.toLowerCase();
return avatars.filter((a) =>
a.avatar_name.toLowerCase().includes(lowerQuery)
);
}
const results = searchByName(avatars, "josh");
这类“列表全量拉取 + 本地过滤”的模式,与 SKILL.md 中“Validate inputs — Check avatar and voice IDs exist before generating”的最佳实践一致:先确认 ID 真实存在,再提交生成请求。
Avatar Groups:形象分组管理
形象按组(group)组织,便于在账号内管理批量形象。
列出形象分组
curl -X GET "https://api.heygen.com/v2/avatar_group.list?include_public=true" \
-H "X-Api-Key: $HEYGEN_API_KEY"
查询参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
include_public |
bool | false | 是否在结果中包含公共形象 |
interface AvatarGroupItem {
id: string;
name: string;
created_at: number;
num_looks: number;
preview_image: string;
group_type: string;
train_status: string;
default_voice_id: string | null;
}
interface AvatarGroupListResponse {
error: null | string;
data: {
avatar_group_list: AvatarGroupItem[];
};
}
async function listAvatarGroups(
includePublic = true
): Promise<AvatarGroupListResponse["data"]> {
const params = new URLSearchParams({
include_public: includePublic.toString(),
});
const response = await fetch(
`https://api.heygen.com/v2/avatar_group.list?${params}`,
{ headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
);
const json: AvatarGroupListResponse = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data;
}
分组条目中的 num_looks 表示组内形象数量,train_status 反映训练状态,default_voice_id 则给出组级默认音色——这些字段在管理自训形象时尤其有用。
获取组内形象
curl -X GET "https://api.heygen.com/v2/avatar_group/{group_id}/avatars" \
-H "X-Api-Key: $HEYGEN_API_KEY"
在视频生成中使用形象
形象配置最终汇入 POST /v2/video/generate 的 video_inputs 数组(单请求 1–50 个输入对象)。最小可用的单场景配置:
const videoConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "Welcome to our product demo!",
voice_id: "1bd001e7e50f421d891986aad5158bc8",
},
},
],
dimension: { width: 1920, height: 1080 },
};
多场景视频则在同一 video_inputs 数组中放入多个对象,每个场景可使用不同形象、不同声音:
const multiSceneConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "Hi, I'm Josh. Let me introduce my colleague.",
voice_id: "1bd001e7e50f421d891986aad5158bc8",
},
},
{
character: {
type: "avatar",
avatar_id: "angela_expressive_20231010",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "Hello! I'm Angela. Nice to meet you!",
voice_id: "2d5b0e6a8c3f47d9a1b2c3d4e5f60718",
},
},
],
};
完整的请求字段(dimension、test、caption、background 等)与轮询逻辑,分别在 video-generation.md 和 video-status.md 中有定义。
使用形象的 default_voice_id(推荐做法)
文档的核心主张是:大多数形象自带一个经过配对优化的 default_voice_id,应优先使用它,而不是手动挑选声音。理由有四:
- 性别保证匹配 —— 形象与声音是预先配对好的;
- 口型同步更自然 —— 默认音色针对该形象做过优化;
- 代码更简单 —— 不需要单独拉取声音列表再做匹配;
- 质量更有保障 —— 这是 HeyGen 官方测试过的组合。
推荐三步流程
1. GET /v2/avatars → 获取 avatar_id 列表
2. GET /v2/avatar/{id}/details → 获取所选形象的 default_voice_id
3. POST /v2/video/generate → 使用 avatar_id + default_voice_id 生成
获取形象详情
curl -X GET "https://api.heygen.com/v2/avatar/{avatar_id}/details" \
-H "X-Api-Key: $HEYGEN_API_KEY"
响应示例:
{
"error": null,
"data": {
"type": "avatar",
"id": "josh_lite3_20230714",
"name": "Josh",
"gender": "male",
"preview_image_url": "https://files.heygen.ai/...",
"preview_video_url": "https://files.heygen.ai/...",
"premium": false,
"is_public": true,
"default_voice_id": "1bd001e7e50f421d891986aad5158bc8",
"tags": ["AVATAR_IV"]
}
}
其中 premium、is_public、tags 可用于做资格判断(例如筛选免费公共形象),default_voice_id 可能为 null,代码必须处理这种情况。
TypeScript 实现
interface AvatarDetails {
type: "avatar";
id: string;
name: string;
gender: "male" | "female";
preview_image_url: string;
preview_video_url: string;
premium: boolean;
is_public: boolean;
default_voice_id: string | null;
tags: string[];
}
async function getAvatarDetails(avatarId: string): Promise<AvatarDetails> {
const response = await fetch(
`https://api.heygen.com/v2/avatar/${avatarId}/details`,
{ 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;
}
// Usage: Get default voice for a known avatar
const details = await getAvatarDetails("josh_lite3_20230714");
if (details.default_voice_id) {
console.log(`Using ${details.name} with default voice: ${details.default_voice_id}`);
} else {
console.log(`${details.name} has no default voice, select manually`);
}
完整示例:用任意形象的默认声音生成视频
async function generateWithAvatarDefaultVoice(
avatarId: string,
script: string
): Promise<string> {
// 1. Get avatar details to find default voice
const avatar = await getAvatarDetails(avatarId);
if (!avatar.default_voice_id) {
throw new Error(`Avatar ${avatar.name} has no default voice`);
}
// 2. Generate video with the avatar's default voice
const videoId = await generateVideo({
video_inputs: [{
character: {
type: "avatar",
avatar_id: avatar.id,
avatar_style: "normal",
},
voice: {
type: "text",
input_text: script,
voice_id: avatar.default_voice_id,
},
}],
dimension: { width: 1920, height: 1080 },
});
return videoId;
}
如果 default_voice_id 为空,回退策略是手动匹配性别:声音列表见 voices.md,确保 gender 与形象一致。
如何选对形象:分类、准则与检查清单
形象分类
| 类别 | 示例 | 最适合 |
|---|---|---|
| 商务/职业 | Josh、Angela、Wayne | 企业视频、产品演示、培训 |
| 休闲/亲和 | Lily 及各类生活化形象 | 社交媒体、非正式内容 |
| 主题/节日 | 节日主题、cosplay 形象 | 特定营销活动、季节性内容 |
| 表现力强 | 名字中带 "expressive" 的形象 | 强代入感叙事、动态内容 |
选择准则
商务/职业内容:
- 选择中性着装(商务休闲或正装)的形象;
- 避开主题化或节日形象(节日装扮、便服);
- 通过预览确认职业感;
- 结合目标受众选择性别与外貌特征。
休闲/社交内容:
- 选择空间更大,主题形象可用于特定营销;
- 形象气质要与内容基调匹配。
常见错误
- 用节日形象做商务内容 —— 在产品介绍中穿节日装扮会显得不专业;
- 不预览就生成 —— 务必打开预览 URL 确认外貌;
- 忽视 avatar_style ——
circle风格不适合全屏出镜; - 声音性别错配 —— 始终使用
default_voice_id,或手动保证性别一致。
生成前检查清单
- [ ] 已在浏览器中预览形象图片/视频;
- [ ] 形象外貌匹配内容基调(专业 vs 休闲);
- [ ]
avatar_style(normal/closeUp/circle)匹配视频格式; - [ ] 声音性别与形象性别一致;
- [ ] 在有
default_voice_id时优先使用它。
实用辅助函数
文档还沉淀了三个可直接复用的辅助函数,覆盖“按 ID 查找、校验、随机选择”三种常见需求:
// 按 ID 查找
async function getAvatarById(avatarId: string): Promise<Avatar | null> {
const avatars = await listAvatars();
return avatars.find((a) => a.avatar_id === avatarId) || null;
}
// 校验 ID 是否有效
async function isValidAvatarId(avatarId: string): Promise<boolean> {
const avatar = await getAvatarById(avatarId);
return avatar !== null;
}
// 随机取一个形象(可选指定性别)
async function getRandomAvatar(gender?: "male" | "female"): Promise<Avatar> {
let avatars = await listAvatars();
if (gender) {
avatars = avatars.filter((a) => a.gender === gender);
}
const randomIndex = Math.floor(Math.random() * avatars.length);
return avatars[randomIndex];
}
在 Agent 自动化流程里,isValidAvatarId 是生成前校验的合适入口;getRandomAvatar 则适合批量 A/B 测试多个形象时的快速取材。
常用公共形象 ID 速查
文档附带的常用公共形象 ID(可用性可能随时间变化):
| Avatar ID | 名称 | 性别 |
|---|---|---|
josh_lite3_20230714 |
Josh | Male |
angela_expressive_20231010 |
Angela | Female |
wayne_20240422 |
Wayne | Male |
lily_20230614 |
Lily | Female |
原文特别提醒:使用前务必先调用 GET /v2/avatars 验证形象是否仍然可用,不要把上表当作长期契约——这也与 SKILL.md “Validate inputs” 的最佳实践呼应。
小结
avatars.md 提供的不只是一份 API 清单,而是一套可落地的选型决策链:
- 列表与预览:
GET /v2/avatars拿到avatar_id、gender和两个公开预览 URL,先看后定; - 类型区分:
custom_前缀识别自训形象,avatar_group.list(include_public=true)管理分组; - 风格匹配:
normal/closeUp/circle/voice_only按全屏、特写、画中画、纯音频四类场景对号入座,circle+ 绿幕或 WebM 是合成叠加的标准组合; - 声音策略:优先
GET /v2/avatar/{id}/details取default_voice_id,为空时再回退到性别手动匹配; - 纪律性校验:生成前完成预览确认与 ID 有效性校验,避免积分浪费在错误的形象上。
沿着 SKILL.md 的五步工作流,把本文的形象选择链路接到 video-generation.md 的 POST /v2/video/generate 和 video-status.md 的轮询逻辑上,即构成 OpenMontage 中一条完整、可复现的 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 StartedRust0623
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