OpenMontage Avatar Video 技能深度解析:HeyGen 语音(Voices)配置、语速音调调节与 SSML 停顿控制实战
OpenMontage 的 avatar-video 技能通过 HeyGen v2 API 实现"数字人+配音"的精准视频生产,其中 references/voices.md 是该技能中负责语音链路的核心参考文档。本文完整覆盖该文档的全部内容——语音列表接口、多语言支持、speed/pitch 参数调节、SSML 风格 <break> 停顿标签的语法规则与最佳实践、自定义音频替代 TTS、语音筛选与数字人配对策略,并结合 OpenMontage 仓库中的工具实现(如 heygen_video 工具)补充说明这套语音配置在真实生产流水线中的落地位置。读完本文,你可以独立完成 HeyGen 语音的选择、参数化调优与脚本级节奏控制。
一、文档定位:avatar-video 技能中的语音链路
voices.md 位于 avatar-video 技能的参考资料目录下,与 avatars.md、scripts.md、video-generation.md 等参考文件共同构成该技能的完整知识体系。技能主文件 SKILL.md 定义了默认工作流:
- 列出数字人(
GET /v2/avatars)——选定数字人并记录avatar_id与default_voice_id; - 列出语音(
GET /v2/voices,即本文档主题)——为数字人挑选性别/语言匹配的语音; - 撰写脚本——按场景组织内容;
- 生成视频(
POST /v2/video/generate)——为每个场景传入数字人、语音、脚本与背景; - 轮询完成状态——直到状态变为
completed。
语音配置就发生在第 2 步与第 4 步之间:先通过列表接口获取可用语音,再把 voice_id(可附加 speed、pitch 参数)填入生成请求的 video_inputs[].voice 中。所有请求都需要 X-Api-Key 请求头,密钥来自环境变量 HEYGEN_API_KEY——这一点与仓库中 heygen_video 工具的鉴权方式一致,见 heygen_video.py 中 get_status() 对 HEYGEN_API_KEY 环境变量的检查,以及 PROVIDERS.md 中对该密钥的配置说明。
二、列出可用语音(GET /v2/voices)
curl 调用
curl -X GET "https://api.heygen.com/v2/voices" \
-H "X-Api-Key: $HEYGEN_API_KEY"
TypeScript 实现
文档给出了完整的类型定义与调用封装:
interface Voice {
voice_id: string;
name: string;
language: string;
gender: "male" | "female";
preview_audio: string;
support_pause: boolean;
emotion_support: boolean;
}
interface VoicesResponse {
error: null | string;
data: {
voices: Voice[];
};
}
async function listVoices(): Promise<Voice[]> {
const response = await fetch("https://api.heygen.com/v2/voices", {
headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! },
});
const json: VoicesResponse = await response.json();
if (json.error) {
throw new Error(json.error);
}
return json.data.voices;
}
每个 Voice 对象的核心字段含义:
| 字段 | 说明 |
|---|---|
voice_id |
语音唯一标识,生成视频时填入 voice.voice_id |
name |
语音名称(如 Sara、Paul) |
language |
语言/地域(如 "English"),可用于筛选 |
gender |
性别,male / female,应与数字人性别匹配 |
preview_audio |
试听音频 URL,选语音前建议先试听 |
support_pause |
是否支持 <break> 停顿标签 |
emotion_support |
是否支持情感表达 |
Python 实现
import requests
import os
def list_voices() -> list:
response = requests.get(
"https://api.heygen.com/v2/voices",
headers={"X-Api-Key": os.environ["HEYGEN_API_KEY"]}
)
data = response.json()
if data.get("error"):
raise Exception(data["error"])
return data["data"]["voices"]
响应格式示例
{
"error": null,
"data": {
"voices": [
{
"voice_id": "1bd001e7e50f421d891986aad5158bc8",
"name": "Sara",
"language": "English",
"gender": "female",
"preview_audio": "https://files.heygen.ai/...",
"support_pause": true,
"emotion_support": true
},
{
"voice_id": "de8b5d78f2e0485f88d1e9f5c8e7f9a6",
"name": "Paul",
"language": "English",
"gender": "male",
"preview_audio": "https://files.heygen.ai/...",
"support_pause": true,
"emotion_support": false
}
]
}
}
三、支持的语言与地域
HeyGen 提供多语言语音覆盖。文档列出的支持语言清单(实际可用集合以接口返回为准):
| 语言 | 代码 | 说明 |
|---|---|---|
| 英语(美国) | en-US | 多语音可选 |
| 英语(英国) | en-GB | 英式口音 |
| 西班牙语 | es-ES | 西班牙本土 |
| 西班牙语(拉丁) | es-MX | 墨西哥西语 |
| 法语 | fr-FR | 法国法语 |
| 德语 | de-DE | 标准德语 |
| 葡萄牙语 | pt-BR | 巴西葡语 |
| 中文(普通话) | zh-CN | 简体中文 |
| 日语 | ja-JP | 标准日语 |
| 韩语 | ko-KR | 标准韩语 |
| 意大利语 | it-IT | 标准意大利语 |
| 荷兰语 | nl-NL | 标准荷兰语 |
| 波兰语 | pl-PL | 标准波兰语 |
| 阿拉伯语 | ar-SA | 沙特阿拉伯语 |
选型要点:地域要与目标受众匹配(例如面向巴西市场应选 pt-BR 而非欧洲葡语)。这一建议也体现在文档最佳实践第 4 条"Consider locale"中。
四、在视频生成中使用语音
语音配置位于 POST /v2/video/generate 请求体的 video_inputs[].voice 中,与 character(数字人)、background(背景)平级。
基础用法(text 类型)
const videoConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "Hello! Welcome to our presentation.",
voice_id: "1bd001e7e50f421d891986aad5158bc8",
},
},
],
};
语速调节(speed)
const videoConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "This is spoken at a faster pace.",
voice_id: "1bd001e7e50f421d891986aad5158bc8",
speed: 1.2, // 1.0 is normal, range: 0.5 - 2.0
},
},
],
};
- 取值范围:
0.5–2.0,默认1.0; - 推荐区间:文档最佳实践建议日常使用
0.9–1.1x保持自然节奏。配套的 scripts.md 进一步细分:0.8–0.9适合复杂主题与年长观众,1.1–1.2适合节奏明快的内容,1.3+需谨慎使用以免影响清晰度。
音调调节(pitch)
const videoConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "This has a higher pitch.",
voice_id: "1bd001e7e50f421d891986aad5158bc8",
pitch: 10, // Range: -20 to 20
},
},
],
};
- 取值范围:
-20到20,默认0(默认值与取值范围在 video-generation.md 的字段表中有交叉印证:speed0.5-2.0 默认 1.0,pitch-20 到 20 默认 0)。
speed 与 pitch 是两个相互独立的微调旋钮:语速影响整体时长(可按 150 词/分钟 × speed 估算),音调影响音色明暗,二者组合使用可以在不换 voice_id 的前提下适配不同内容风格。
五、用 SSML 风格 break 标签添加停顿
HeyGen 支持在脚本文本中嵌入 SSML 风格的 <break> 标签,实现精确的停顿控制。
标签格式
<break time="Xs"/>
其中 X 为秒数(如 1s、1.5s、0.5s)。
书写规则
| 规则 | 示例 |
|---|---|
| 秒数必须带 "s" 后缀 | <break time="1.5s"/> ✓ |
| 标签前必须有空格 | word <break time="1s"/> ✓ |
| 标签后必须有空格 | <break time="1s"/> word ✓ |
| 必须是自闭合标签 | <break time="1s"/> ✓ |
错误写法:word<break time="1s"/>word(标签紧贴文字,无空格)
正确写法:word <break time="1s"/> word
停顿示例
// Single pause
const script1 = "Hello and welcome. <break time=\"1s\"/> Let me introduce our product.";
// Multiple pauses
const script2 = "First point. <break time=\"1.5s\"/> Second point. <break time=\"1s\"/> Third point.";
// Pause at start (dramatic opening)
const script3 = "<break time=\"0.5s\"/> Welcome to our presentation.";
// Longer pause for emphasis
const script4 = "And the winner is... <break time=\"2s\"/> You!";
完整示例:带停顿的产品演示脚本
const scriptWithPauses = `
Welcome to our product demo. <break time="1s"/>
Today I'll show you three key features. <break time="0.5s"/>
First, let's look at the dashboard. <break time="1.5s"/>
As you can see, it's incredibly intuitive.
`;
const videoConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: scriptWithPauses,
voice_id: "1bd001e7e50f421d891986aad5158bc8",
},
},
],
};
连续 break 的合并规则
多个连续 break 标签会被自动合并为一个停顿:
// These two breaks:
"Hello <break time=\"1s\"/> <break time=\"0.5s\"/> world"
// Are treated as a single 1.5s pause
停顿最佳实践
- 用于强调——在重要观点前加停顿;
- 保持时长合理——
0.5s到2s是常见区间,更长会显得不自然; - 贴合人类语流——在人类会呼吸或停顿的位置添加;
- 试听验证——生成后收听音频,确认节奏是否合适。
此外,只有 support_pause: true 的语音才支持该特性,筛选语音时可以把这一字段作为过滤条件(见下一节的 filterByFeatures)。
六、用自定义音频替代 TTS
除了文本转语音,voice.type 可以直接传 audio 并附带音频 URL,由数字人对口型播放你自己的音频:
const videoConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "audio",
audio_url: "https://example.com/my-audio.mp3",
},
},
],
};
从源码结构看,voice.type 完整支持三种取值:text(TTS)、audio(自定义音频)、silence(静音,需传 duration 秒数)——完整的字段约束见 video-generation.md 中的 video_inputs[].voice Fields 表格。audio 类型适用于真人录音、既有播客源或需要完全不受 TTS 音色限制的场景;此时 speed/pitch 参数不适用,音频本身的节奏就是最终节奏。
七、语音筛选(Filtering)
拿到 listVoices() 返回的完整列表后,通常按语言、性别、能力三个维度做客户端筛选。
按语言筛选
function filterByLanguage(voices: Voice[], language: string): Voice[] {
return voices.filter((v) =>
v.language.toLowerCase().includes(language.toLowerCase())
);
}
const englishVoices = filterByLanguage(voices, "english");
const spanishVoices = filterByLanguage(voices, "spanish");
按性别筛选
function filterByGender(voices: Voice[], gender: "male" | "female"): Voice[] {
return voices.filter((v) => v.gender === gender);
}
const femaleVoices = filterByGender(voices, "female");
按能力特征筛选
function filterByFeatures(
voices: Voice[],
options: { supportPause?: boolean; emotionSupport?: boolean }
): Voice[] {
return voices.filter((v) => {
if (options.supportPause !== undefined && v.support_pause !== options.supportPause) {
return false;
}
if (options.emotionSupport !== undefined && v.emotion_support !== options.emotionSupport) {
return false;
}
return true;
});
}
const expressiveVoices = filterByFeatures(voices, { emotionSupport: true });
八、语音选择助手(组合条件一次到位)
三个维度筛选的组合版本,返回第一个满足全部条件的语音:
interface VoiceSelectionCriteria {
language?: string;
gender?: "male" | "female";
supportPause?: boolean;
emotionSupport?: boolean;
}
async function findVoice(criteria: VoiceSelectionCriteria): Promise<Voice | null> {
const voices = await listVoices();
const filtered = voices.filter((v) => {
if (criteria.language && !v.language.toLowerCase().includes(criteria.language.toLowerCase())) {
return false;
}
if (criteria.gender && v.gender !== criteria.gender) {
return false;
}
if (criteria.supportPause !== undefined && v.support_pause !== criteria.supportPause) {
return false;
}
if (criteria.emotionSupport !== undefined && v.emotion_support !== criteria.emotionSupport) {
return false;
}
return true;
});
return filtered[0] || null;
}
// Usage
const voice = await findVoice({
language: "english",
gender: "female",
emotionSupport: true,
});
findVoice 的返回值为 null 表示条件过严(例如指定了不存在的语言+性别组合),调用方应做好回退处理,例如放宽 emotionSupport 或更换语言再查一次。
九、多语言视频:每个场景使用不同语言的语音
video_inputs 是一个数组(1–50 个场景),每个场景拥有独立的 character 与 voice,因此同一条视频里可以混合使用不同语言、不同 voice_id 的场景:
const multiLanguageConfig = {
video_inputs: [
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "Hello! Welcome to our global product launch.",
voice_id: "english_voice_id",
},
},
{
character: {
type: "avatar",
avatar_id: "josh_lite3_20230714",
avatar_style: "normal",
},
voice: {
type: "text",
input_text: "Hola! Bienvenidos al lanzamiento global de nuestro producto.",
voice_id: "spanish_voice_id",
},
},
],
};
这个模式同样适用于"同一数字人、不同语言、逐场景切换背景"的多场景制作,背景配置规则参考 backgrounds.md。
十、语音与数字人的配对策略
首选:使用数字人的默认语音(default_voice_id)
很多数字人带有预匹配的 default_voice_id。这是文档明确推荐的首选方案,因为配对经过官方验证,性别必然一致、口型同步最自然、代码也最简单。
// Using v2 API to get avatar with default voice
const response = await fetch(
"https://api.heygen.com/v2/avatar_group.list?include_public=true",
{ headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
);
const { data } = await response.json();
// Find avatar with a default voice
const avatar = data.avatar_group_list.find((a: any) => a.default_voice_id);
if (avatar) {
const videoConfig = {
video_inputs: [{
character: { type: "avatar", avatar_id: avatar.id },
voice: {
type: "text",
input_text: script,
voice_id: avatar.default_voice_id, // Pre-matched voice
},
}],
};
}
更细致的推荐流程记录在 avatars.md:先 GET /v2/avatars 获取 avatar_id 列表,再 GET /v2/avatar/{id}/details 拿到该数字人的 default_voice_id,最后 POST /v2/video/generate。完整示例可参考 avatars.md 中的 generateWithAvatarDefaultVoice 函数。
回退方案:手动按性别匹配
当数字人没有默认语音时,手动选择同性别(且语言匹配)的语音:
interface AvatarVoicePair {
avatarId: string;
voiceId: string;
gender: "male" | "female";
}
async function findMatchingAvatarAndVoice(
preferredGender?: "male" | "female"
): Promise<AvatarVoicePair> {
const [avatars, voices] = await Promise.all([
listAvatars(),
listVoices(),
]);
// Default to male if no preference
const gender = preferredGender || "male";
// Find avatar with matching gender
const avatar = avatars.find((a) => a.gender === gender);
if (!avatar) {
throw new Error(`No ${gender} avatar available`);
}
// Find voice with matching gender AND language
const voice = voices.find(
(v) => v.gender === gender && v.language.toLowerCase().includes("english")
);
if (!voice) {
throw new Error(`No ${gender} English voice available`);
}
return {
avatarId: avatar.avatar_id,
voiceId: voice.voice_id,
gender,
};
}
注意示例中 listAvatars() 的接口封装在 avatars.md 中定义(GET /v2/avatars,返回体含 avatars 与 talking_photos 两个数组)。
十一、最佳实践汇总
文档给出的 7 条生产建议,可直接作为上线前 checklist:
- 语音性别与数字人匹配——男声配男数字人、女声配女数字人,首选
default_voice_id; - 语音与内容匹配——商务内容选专业音色;
- 先试听再定稿——收听
preview_audio后再确认voice_id; - 匹配受众地域口音——如面向拉美用户选
es-MX; - 使用自然语速——
speed通常控制在 0.9–1.1x; - 添加停顿——用 SSML
<break>标签获得更自然的语流(前提是support_pause: true); - 验证可用性——生成前务必确认
voice_id存在于列表接口返回中。
十二、在 OpenMontage 工具链中的落点
voices.md 是写给 Agent 的技能参考资料,而 OpenMontage 仓库中的可执行工具则覆盖 HeyGen 的另一条链路。从源码结构看:
- heygen_video.py 定义了
HeyGenVideo工具(provider = "heygen"),其input_schema只暴露prompt、operation、provider_variant、aspect_ratio等视频生成参数——它走的是POST /v1/workflows/executions的 VEO/Sora/Kling 等通用视频生成工作流(见 shared helpers 中的generate_heygen_video与poll_heygen),并不直接承载 avatar + voice 配置; - 因此,"指定数字人+指定语音+精确脚本"的 avatar 视频生产在 OpenMontage 中由 avatar-video 技能(
allowed-tools: mcp__heygen__*)按本文档描述的 v2 API 直接编排完成,SKILL.md 也明确了分工:视频状态查询优先使用 HeyGen MCP 工具,而 avatar/voice 列表与POST /v2/video/generate走直接 API 调用。
理解这一分工有助于正确选择实现路径:需要"文本提示词生成通用视频"时可用 heygen_video 工具;需要"数字人口播+精确语音控制"时应遵循本技能文档的 v2 接口链路。
十三、关键要点速查
| 主题 | 端点/字段 | 要点 |
|---|---|---|
| 列出语音 | GET /v2/voices |
返回 voice_id、language、gender、support_pause、emotion_support |
| 语言覆盖 | language 字段 |
14 种语言/地域,选与受众匹配的地域口音 |
| 语速 | voice.speed |
0.5–2.0,默认 1.0,推荐 0.9–1.1 |
| 音调 | voice.pitch |
-20 到 20,默认 0 |
| 停顿 | 脚本内 <break time="Xs"/> |
前后必须有空格、秒数带 s、自闭合;连续 break 自动合并 |
| 自定义音频 | voice.type: "audio" + audio_url |
跳过 TTS,直接对口型既有音频 |
| 配对策略 | default_voice_id 优先 |
无默认语音时按性别+语言手动匹配 |
| 鉴权 | X-Api-Key 头 |
来自环境变量 HEYGEN_API_KEY |
以上所有接口与参数均以当前仓库 .agents/skills/avatar-video/references/voices.md 为准;端点字段约束可交叉核对 video-generation.md,脚本写作与语速/时长估算可继续参考 scripts.md。
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