OpenMontage 人脸替换(Face Swap)实战指南:基于 HeyGen FaceswapNode 工作流的换脸视频生成
换脸(Face Swap)是 AI 视频生产管线中最常用的"二次创作"能力之一:把一张来源照片中的人脸,无缝替换到目标视频中的原有人脸上。本指南以 OpenMontage 仓库内 .agents/skills/faceswap/SKILL.md 技能文档为核心,系统讲解通过 HeyGen /v1/workflows/executions 接口完成人脸替换的完整链路——从 HEYGEN_API_KEY 鉴权、任务提交、状态轮询到结果取回。读者学完后,既能用 curl / TypeScript / Python 独立调用换脸 API,也能把换脸任务与数字人(Avatar)视频生成串成"先生成、再换脸"的个性化管线。
技能定位:OpenMontage 里的"换脸"角色
在 OpenMontage 的 Agent 技能体系中,人脸替换隶属于 HeyGen 云端能力簇。仓库的 AGENT_GUIDE.md 在「Avatar / lip-sync」分组中列出了 avatar-video、heygen、create-video、faceswap 等技能,而 skills/INDEX.md 进一步把 faceswap 与 Lip Sync、HeyGen AI Video 场景归为一类。也就是说:这个技能解决的问题是——拿一张来源人脸图片,替换到一段目标视频上,得到一段全新的人物出镜视频。
技能清单(frontmatter)给出了 Agent 触发它时应该满足的条件:
| 触发条件 | 说明 |
|---|---|
| 用 AI 在视频中替换人脸 | 用另一张脸替换视频中的人脸 |
| 从来源图片向目标视频换脸 | source_image_url → target_video_url 的单向注入 |
| 制作个性化视频 | 把某人的脸换进通用出镜视频,实现千人千面 |
处理 HeyGen /v1/workflows/executions |
统一走该执行端点的 FaceswapNode 工作流 |
该技能声明 allowed-tools: mcp__heygen__*,且其运行前提是通过环境变量注入 HEYGEN_API_KEY。
鉴权准备:统一使用 HEYGEN_API_KEY
所有换脸请求都需要在 HTTP Header 中携带 X-Api-Key,密钥统一来自环境变量 HEYGEN_API_KEY:
export HEYGEN_API_KEY=your_key_here
这一约定与 OpenMontage 全仓库的 HeyGen 接入方式保持一致:docs/PROVIDERS.md 在环境变量清单中登记了 HEYGEN_API_KEY= # HeyGen avatar video gateway;而仓库自带的 HeyGen 视频工具在 tools/video/_shared.py 中同样以 os.environ.get("HEYGEN_API_KEY") 读取密钥(缺失时直接判定工具不可用)。因此,无论你是通过 MCP 技能调用,还是像下面这样直接用 HTTP 调用,密钥的注入方式都是同一个环境变量。
验证鉴权链路最简单的方式是一次直接的 POST:
curl -X POST "https://api.heygen.com/v1/workflows/executions" \
-H "X-Api-Key: $HEYGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{"workflow_type": "FaceswapNode", "input": {"source_image_url": "https://example.com/face.jpg", "target_video_url": "https://example.com/video.mp4"}}'
默认工作流:四步完成一次换脸
换脸任务是典型的异步云端作业,完整生命周期只有四步:
- 调用
POST /v1/workflows/executions,提交workflow_type: "FaceswapNode"、一张来源人脸图、一段目标视频; - 从响应中拿到
execution_id; - 每 10 秒轮询一次
GET /v1/workflows/executions/{id},直到状态变为completed; - 从输出中取出结果视频地址
video_url。
之所以要轮询而不是同步等待,是因为人脸替换属于 GPU 密集型计算。根据技能文档的说明,单次换脸处理通常需要 1~3 分钟,官方推荐以 10 秒为轮询间隔。下面的各个小节将逐一拆解每一步的请求构造与响应解析。
发起换脸任务
端点与请求字段
- Endpoint:
POST https://api.heygen.com/v1/workflows/executions
请求体结构如下:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
workflow_type |
string | Y | 必须为 "FaceswapNode",用于选择换脸执行节点 |
input.source_image_url |
string | Y | 被换入的人脸图片 URL(来源脸) |
input.target_video_url |
string | Y | 应用换脸的目标视频 URL(被替换的视频) |
注意 workflow_type 是区分"这次执行到底跑哪个节点"的开关:换脸用 FaceswapNode,而后续示例里生成数字人则用 AvatarInferenceNode。
curl 完整示例
curl -X POST "https://api.heygen.com/v1/workflows/executions" \
-H "X-Api-Key: $HEYGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflow_type": "FaceswapNode",
"input": {
"source_image_url": "https://example.com/face-photo.jpg",
"target_video_url": "https://example.com/original-video.mp4"
}
}'
TypeScript 实现
把请求封装成函数,返回 execution_id 供后续轮询使用:
interface FaceswapInput {
source_image_url: string;
target_video_url: string;
}
interface ExecuteResponse {
data: {
execution_id: string;
status: "submitted";
};
}
async function faceswap(input: FaceswapInput): Promise<string> {
const response = await fetch("https://api.heygen.com/v1/workflows/executions", {
method: "POST",
headers: {
"X-Api-Key": process.env.HEYGEN_API_KEY!,
"Content-Type": "application/json",
},
body: JSON.stringify({
workflow_type: "FaceswapNode",
input,
}),
});
const json: ExecuteResponse = await response.json();
return json.data.execution_id;
}
Python 实现
import requests
import os
def faceswap(source_image_url: str, target_video_url: str) -> str:
payload = {
"workflow_type": "FaceswapNode",
"input": {
"source_image_url": source_image_url,
"target_video_url": target_video_url,
},
}
response = requests.post(
"https://api.heygen.com/v1/workflows/executions",
headers={
"X-Api-Key": os.environ["HEYGEN_API_KEY"],
"Content-Type": "application/json",
},
json=payload,
)
data = response.json()
return data["data"]["execution_id"]
响应格式
提交成功后,服务端立即返回一个已受理的任务(同步返回、任务后台异步执行):
{
"data": {
"execution_id": "node-gw-f1s2w3p4",
"status": "submitted"
}
}
execution_id 形如 node-gw-...,是后续所有状态查询的凭证,请务必保存。
查询任务状态
端点
- Endpoint:
GET https://api.heygen.com/v1/workflows/executions/{execution_id}
curl -X GET "https://api.heygen.com/v1/workflows/executions/node-gw-f1s2w3p4" \
-H "X-Api-Key: $HEYGEN_API_KEY"
完成时的响应格式
{
"data": {
"execution_id": "node-gw-f1s2w3p4",
"status": "completed",
"output": {
"video_url": "https://resource.heygen.ai/faceswap/output.mp4"
}
}
}
当 status 为 completed 时,换脸结果视频地址位于 data.output.video_url,可直接下载或交给下游(如配音、字幕、切片)继续处理。
轮询等待完成的完整实现
把"提交 + 轮询 + 取结果"收拢为一个函数,并对 completed / failed / not_found 三种终态以及超时分别处理:
async function faceswapAndWait(
input: FaceswapInput,
maxWaitMs = 600000,
pollIntervalMs = 10000
): Promise<string> {
const executionId = await faceswap(input);
console.log(`Submitted face swap: ${executionId}`);
const startTime = Date.now();
while (Date.now() - startTime < maxWaitMs) {
const response = await fetch(
`https://api.heygen.com/v1/workflows/executions/${executionId}`,
{ headers: { "X-Api-Key": process.env.HEYGEN_API_KEY! } }
);
const { data } = await response.json();
switch (data.status) {
case "completed":
return data.output.video_url;
case "failed":
throw new Error(data.error?.message || "Face swap failed");
case "not_found":
throw new Error("Workflow not found");
default:
await new Promise((r) => setTimeout(r, pollIntervalMs));
}
}
throw new Error("Face swap timed out");
}
这段轮询有几个工程要点值得关注:
- 默认超时 600000ms(10 分钟):与文档估计的 1~3 分钟 GPU 处理时间相比留足余量,避免在视频较长、排队拥堵时误判失败;
- 默认间隔 10000ms(10 秒):官方推荐的轮询节奏,既不会过度占用 API 配额,也不会让用户等太久;
- 状态机完备:把
failed、not_found也纳入显式分支并抛出带原因的错误,而不是静默无限循环。
使用示例
基本换脸:把个人大头照换进演讲视频
curl -X POST "https://api.heygen.com/v1/workflows/executions" \
-H "X-Api-Key: $HEYGEN_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"workflow_type": "FaceswapNode",
"input": {
"source_image_url": "https://example.com/headshot.jpg",
"target_video_url": "https://example.com/presentation.mp4"
}
}'
这个场景对应技能触发条件里的"Replacing a face in a video with another face"——例如把通用演讲模板视频中的人物脸替换成自己的照片,得到一段"本人出镜"的定制内容。
链式用法:先生成 Avatar 视频,再换入自定义人脸
这是换脸最有价值的组合玩法:先让 HeyGen 生成一段数字人口播视频,再用自定义人脸去替换,实现"人人是自己形象的数字人"。
import time
# Step 1: Generate avatar video
avatar_execution_id = requests.post(
"https://api.heygen.com/v1/workflows/executions",
headers={"X-Api-Key": os.environ["HEYGEN_API_KEY"], "Content-Type": "application/json"},
json={
"workflow_type": "AvatarInferenceNode",
"input": {
"avatar": {"avatar_id": "Angela-inblackskirt-20220820"},
"audio_list": [{"audio_url": "https://example.com/speech.mp3"}],
},
},
).json()["data"]["execution_id"]
# Step 2: Wait for avatar video to complete
while True:
status = requests.get(
f"https://api.heygen.com/v1/workflows/executions/{avatar_execution_id}",
headers={"X-Api-Key": os.environ["HEYGEN_API_KEY"]},
).json()["data"]
if status["status"] == "completed":
avatar_video_url = status["output"]["video"]["video_url"]
break
time.sleep(10)
# Step 3: Swap in a custom face
faceswap_execution_id = faceswap(
source_image_url="https://example.com/custom-face.jpg",
target_video_url=avatar_video_url,
)
三段式结构非常清晰:
- 生成数字人:
AvatarInferenceNode工作流,指定avatar_id(示例为Angela-inblackskirt-20220820)与配音音频audio_list[].audio_url; - 等待完成:同样是 10 秒间隔轮询,完成后从
data.output.video.video_url取视频(注意此处数字人任务的输出结构是output.video.video_url,与 FaceswapNode 直接返回output.video_url不同); - 换入自定义脸:把第 2 步得到的数字人视频当作
target_video_url,换脸任务只需追加一次faceswap()调用即可。
这套链式用法在 OpenMontage 里可以继续下沉到生产工具链中:仓库已把 HeyGen 作为云端视频网关接入(参见 docs/PROVIDERS.md 中 "HeyGen — Avatar Video Gateway" 的说明,以及 tools/video/heygen_video.py、tools/video/_shared.py 中基于 HEYGEN_API_KEY 的请求封装、预设上传与结果轮询实现),Agent 可以在统一的环境变量鉴权下复用同一套任务编排思路。
最佳实践清单
技能文档给出了 6 条实战准则,逐条展开如下:
- 使用清晰、正对镜头的人脸照片——来源图应只含一张脸、光线良好、角度正面,过大的侧脸或遮挡会显著降低换脸质量;
- 认清换脸是 GPU 密集型计算——单次处理预计耗时 1~3 分钟,请以 10 秒为间隔轮询,并合理设置客户端超时(前文示例中的 10 分钟上限即为此设计);
- 来源图分辨率越高越好——高分辨率人脸照片能保留更多五官细节,换脸结果更自然;
- 来源图只允许出现一张脸——多张脸会让算法无法判断"该换谁",必须在送入任务前裁剪或挑选;
- 目标视频几乎没有限制——目标视频可以是数字人视频、真人录屏、或者任何"画面里存在可见人脸"的视频,算法负责跟踪并替换对应人脸;
- 积极与其它工作流链式组合——先跑
AvatarInferenceNode生成数字人、再做FaceswapNode换脸,是实现"自定义形象 + 任意文案出镜"类个性化视频的标准路径。
小结
以 .agents/skills/faceswap/SKILL.md 为参照,Face Swap 的调用范式可以概括为一句话:提交一个 workflow_type: "FaceswapNode" 的执行任务,用 10 秒间隔轮询 execution_id,在 completed 后消费 output.video_url。配合 OpenMontage 中 HeyGen 云端网关的既有接入方式(同一 HEYGEN_API_KEY 环境变量),开发者或 Agent 既可以独立执行换脸,也能无缝把它串进"数字人生成 → 换脸个性化 → 下游剪辑"的完整视频生产管线中。
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