在 LiveKit Agents 中接入 Protoface 虚拟人形象:livekit-plugins-protoface 插件安装、配置与源码解析
【免费下载链接】agentsA framework for building realtime voice AI agents 🤖🎙️📹项目地址: https://gitcode.com/GitHub_Trending/agen/agents
导读
本文围绕 livekit-plugins-protoface 插件,系统讲解如何在 LiveKit Agents 实时语音 AI 应用中将对话语音输出路由到 Protoface 云端托管的虚拟人(virtual avatar),实现"语音 + 虚拟形象"的实时交互体验。读完本文,你将掌握该插件的安装方式、环境变量与参数配置、会话生命周期管理,并通过源码理解其与 LiveKit 房间、音频数据流、Worker Token 鉴权之间的底层协作机制。
插件定位:为 LiveKit Agents 增加虚拟人形象能力
Protoface 是一个提供云端托管虚拟人形象的服务商。livekit-plugins-protoface是 LiveKit Agents 生态中的官方插件,它把 Protoface 的会话 API 封装成一个与 LiveKit 原生AvatarSession抽象兼容的会话对象,让开发者无需关心托管端的接入细节,即可把 Agent 的 TTS 音频输出转交给 Protoface 渲染的虚拟人,并通过 LiveKit 房间发布为视频与音频轨。
从 pyproject.toml 可以看到,该插件的关键元数据如下:
- 包名:
livekit-plugins-protoface - 当前版本:
1.8.0(定义于 version.py) - 依赖:
livekit-agents>=1.8.0 - 运行环境:Python
>=3.10 - 许可证:Apache-2.0
插件在导入时即完成注册:__init__.py中定义了继承自livekit.agents.Plugin的ProtofacePlugin类,并调用Plugin.register_plugin(ProtofacePlugin())挂载到 LiveKit Agents 的插件体系中,同时对外导出DEFAULT_STOCK_AVATAR_ID、AvatarSession、ProtofaceException和__version__。
安装
使用 pip 直接安装即可:
pip install livekit-plugins-protoface由于插件依赖livekit-agents>=1.8.0,安装器会自动拉取满足版本要求的 livekit-agents 框架。建议在虚拟环境(如 uv、venv、poetry)中进行安装,避免与系统级 Python 环境互相污染。
前置条件:API Key 与必要环境变量
使用该插件前,你需要先从 Protoface 申请 API Key。插件支持两种提供方式:
- 通过环境变量
PROTOFACE_API_KEY设置(推荐); - 在创建
AvatarSession或ProtofaceAPI时通过api_key参数显式传入。
从 api.py 的源码可以看到,ProtofaceAPI的构造函数会优先使用显式传入的api_key,否则回退读取PROTOFACE_API_KEY环境变量;两者都缺失时直接抛出ProtofaceException:
self._api_key = _resolve_optional_string(api_key, "PROTOFACE_API_KEY") if not self._api_key: raise ProtofaceException( "api_key must be set by passing it to ProtofaceAPI or " "setting the PROTOFACE_API_KEY environment variable" )除 API Key 外,AvatarSession.start()在接入 LiveKit 房间时还需要 LiveKit 服务器的连接凭据,缺失同样会抛出ProtofaceException(见 avatar.py)。插件涉及的环境变量汇总如下:
| 环境变量 | 用途 | 默认值 |
|---|---|---|
PROTOFACE_API_KEY | Protoface API Key,创建会话时的鉴权凭据 | 无(必填) |
PROTOFACE_API_URL | Protoface API 基础地址,便于自建代理或测试环境 | https://api.protoface.com(定义于 api.py) |
LIVEKIT_URL | LiveKit 服务器 WebSocket 地址,Protoface 托管端据此加入房间 | 无(start()时必填) |
LIVEKIT_API_KEY | LiveKit API Key,用于签发 Worker Token | 无(start()时必填) |
LIVEKIT_API_SECRET | LiveKit API Secret,用于签发 Worker Token | 无(start()时必填) |
核心用法:创建并启动 AvatarSession
插件对外最核心的类是AvatarSession,它继承自 LiveKit Agents 语音模块中的抽象基类AvatarSession(定义于 livekit-agents/livekit/agents/voice/avatar/_types.py),因此可以与其他虚拟人插件以相同的方式集成进 Agent 会话。
构造函数参数
从 avatar.py 可以看到构造函数支持的参数:
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
avatar_id | str | av_stock_001 | 要渲染的 Protoface 虚拟人 ID,常量DEFAULT_STOCK_AVATAR_ID即"av_stock_001" |
api_key | str \| None | NOT_GIVEN | Protoface API Key,未传则读取PROTOFACE_API_KEY |
api_url | str \| None | NOT_GIVEN | API 基础地址,未传则读取PROTOFACE_API_URL,再回退到官方默认地址 |
max_duration_seconds | int \| None | NOT_GIVEN | 会话最大时长(秒),Protoface 会取该值与账户套餐上限中较小者 |
avatar_participant_identity | str \| None | NOT_GIVEN | 虚拟人在 LiveKit 房间中的参与者身份,默认protoface-avatar-agent |
avatar_participant_name | str \| None | NOT_GIVEN | 虚拟人参与者的显示名称,默认protoface-avatar-agent |
conn_options | APIConnectOptions | DEFAULT_API_CONNECT_OPTIONS | Protoface API 请求的超时与重试配置 |
一个典型的创建方式:
from livekit.plugins.protoface import AvatarSession avatar = AvatarSession( avatar_id="av_stock_001", max_duration_seconds=600, )启动会话
在 Agent 的 Job 入口中,将已连接的房间和AgentSession传给start():
await avatar.start(agent_session, room)start()内部完成四件事(见 avatar.py):
- 校验单次启动:同一个
AvatarSession实例只能启动一次,重复调用会抛出RuntimeError("AvatarSession.start() called twice; create a new AvatarSession."); - 解析 LiveKit 凭据:依次使用参数值或
LIVEKIT_URL/LIVEKIT_API_KEY/LIVEKIT_API_SECRET环境变量,任一缺失即抛ProtofaceException; - 创建托管会话:调用 Protoface 的
POST /v1/sessions接口,携带avatar_id与transport配置(详见下文),并记录返回的session_id; - 接管音频输出:通过
agent_session.output.replace_audio_tail(...)把 Agent 的音频输出替换为发往虚拟人的DataStreamAudioOutput。
Transport 配置:托管端如何加入 LiveKit 房间
start()构造的 transport 对象完整展示了 Protoface 托管端与 LiveKit 房间的连接方式:
transport = { "type": "livekit", "url": livekit_url_value, "room_name": room.name, "worker_token": worker_token, "worker_identity": self._avatar_participant_identity, "audio_source": "data_stream", }type固定为"livekit",表示托管端以 LiveKit 参与者身份接入;room_name即当前 Agent Job 所在的房间名;worker_token是插件为本会话临时签发的 JWT(见下文);audio_source固定为"data_stream",与 LiveKit Agents 的DataStreamAudioOutput一一对应。
底层 API 客户端:ProtofaceAPI
插件在 api.py 中实现了一个基于aiohttp的异步客户端ProtofaceAPI,AvatarSession的所有 HTTP 交互都经由它完成:
start_session(avatar_id, transport, max_duration_seconds):发起POST /v1/sessions,请求体为{"avatar_id": ..., "transport": ..., "max_duration_seconds": ...}(仅当显式传入时长时才携带该字段);end_session(session_id):发起POST /v1/sessions/{session_id}/end,用于优雅结束托管会话。
所有请求都会携带以下请求头:
{ "Authorization": f"Bearer {self._api_key}", "User-Agent": "livekit-plugins-protoface/{__version__}", "Accept": "application/json", }超时与重试机制
客户端内置了与 LiveKit Agents 一致的错误分级与重试策略(api.py):
- 超时:
asyncio.TimeoutError映射为APITimeoutError; - 网络错误:
aiohttp.ClientError映射为APIConnectionError; - 服务端错误:非 2xx 响应映射为
APIStatusError,其中retryable=False的错误(如非对象 JSON 响应)立即抛出、不重试; - 重试:总尝试次数为
conn_options.max_retry + 1,重试间隔通过conn_options._interval_for_retry(attempt)计算(指数退避),全部重试失败后统一抛出APIConnectionError。
start_session()返回的响应中必须包含字符串类型的id字段,否则AvatarSession.start()会抛出ProtofaceException("Protoface API response missing session id")。
音频路由:把 TTS 输出喂给虚拟人
插件接入虚拟人的关键一步是把 Agent 的 TTS 音频输出替换为发给 Protoface 参与者的数据流。相关实现位于 avatar.py:
agent_session.output.replace_audio_tail( DataStreamAudioOutput( room=room, destination_identity=self._avatar_participant_identity, sample_rate=SAMPLE_RATE, wait_remote_track=rtc.TrackKind.KIND_VIDEO, ), )其中DataStreamAudioOutput是 LiveKit Agents 语音模块提供的音频输出实现(定义于 livekit-agents/livekit/agents/voice/avatar/_datastream_io.py),它会把 Agent 生成的音频以数据流形式发送给房间内指定身份的参与者。需要注意两个细节:
- 采样率固定为 16 kHz(
SAMPLE_RATE = 16000,见 avatar.py); wait_remote_track=rtc.TrackKind.KIND_VIDEO表示等待虚拟人的视频轨就绪,确保虚拟人真正出现在房间后才开始推流音频,避免"只闻其声、不见其人"。
Worker Token 签发与安全边界
为了让 Protoface 托管端能够以受控身份加入房间,插件在本地用 LiveKit 凭据签发一个短期 JWT(avatar.py)。签发逻辑要点:
- 参与者身份来源:优先取
get_job_context()中 Agent 的本地参与者身份,其次取已连接房间的room.local_participant.identity,两者都不可用时抛ProtofaceException; - Token 类型:
with_kind("agent"),即按 Agent 类型参与者签发; - 权限范围:
VideoGrants(room_join=True, room=房间名, can_publish=True, can_subscribe=True, can_publish_data=True),最小化地覆盖发布音视频轨与订阅房间数据所需的能力; - 身份与属性:携带虚拟人参与者的
identity、name,并通过with_attributes({ATTRIBUTE_PUBLISH_ON_BEHALF: 本地参与者身份})标注该参与者是"代表"本地 Agent 发布轨道的,便于在房间中建立归属关系。
会话结束与资源释放
AvatarSession.aclose()(avatar.py)负责优雅关闭:
- 记录当前
session_id并将其置空,防止重复关闭; - 调用
ProtofaceAPI.end_session(session_id)请求 Protoface 端优雅结束托管会话;若调用失败仅记录告警日志,不阻断本地清理; - 委托基类
super().aclose()释放 LiveKit 侧的资源(数据流、房间监听等)。
从基类实现(livekit-agents/livekit/agents/voice/avatar/_types.py)可以看到,AvatarSession.start()在 Job 上下文内会自动注册aclose作为关闭回调;若在 Job 上下文之外使用,则需要手动调用aclose()释放资源。
此外,基类还定义了插件需要实现的抽象契约:avatar_identity(虚拟人参与者标识)与provider(供应商名称,本插件返回"protoface"),并通过rtc.EventEmitter支持metrics_collected等事件订阅。
异常体系速查
插件定义了独立的异常类型 errors.py:
ProtofaceException:配置或协议层面的错误,例如缺少 API Key、缺少 LiveKit 凭据、Protoface 响应缺少session id、无法获取本地参与者身份、重复调用start()(后者以RuntimeError抛出);- 网络/服务端错误复用 LiveKit Agents 的
APIConnectionError、APITimeoutError、APIStatusError,并遵循统一的重试语义。
调试时可通过日志定位问题,插件日志以livekit.plugins.protoface为 logger 名输出(见 log.py),会话创建成功与结束失败等关键事件均记录有session_id与avatar_id。
常见问题排查清单
- 启动即报
ProtofaceException: api_key must be set...:确认已设置PROTOFACE_API_KEY环境变量,或在构造AvatarSession(api_key=...)时显式传入; start()报缺少 LiveKit 凭据:确认已设置LIVEKIT_URL、LIVEKIT_API_KEY、LIVEKIT_API_SECRET三者,且 Agent 端与托管端能访问同一 LiveKit 服务器;RuntimeError: AvatarSession.start() called twice:一个实例只能启动一次,会话结束后如需重启请新建AvatarSession;- 房间中看不到虚拟人:检查
avatar_id是否有效,以及 Protoface 账户套餐是否允许当前会话时长(max_duration_seconds取配置值与套餐上限的较小者); - 需要自定义 API 地址(如私有部署或代理):设置
PROTOFACE_API_URL环境变量即可覆盖默认的https://api.protoface.com。
小结
livekit-plugins-protoface是一个轻量而完整的 LiveKit Agents 虚拟人插件:安装一条命令、配置一个环境变量即可接入,AvatarSession在启动时自动完成托管会话创建、Worker Token 签发与音频流接管。透过源码可以看到,它与 LiveKit Agents 的AvatarSession抽象、DataStreamAudioOutput音频通道以及统一的 API 错误重试体系深度集成,为实时语音 Agent 增加可见的虚拟形象提供了可靠的落地路径。
【免费下载链接】agentsA framework for building realtime voice AI agents 🤖🎙️📹项目地址: https://gitcode.com/GitHub_Trending/agen/agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考