ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

LiveKit Agents LemonSlice 插件实战:为实时语音 AI Agent 接入虚拟形象与外部视频会议

LiveKit Agents LemonSlice 插件实战:为实时语音 AI Agent 接入虚拟形象与外部视频会议 LiveKit Agents LemonSlice 插件实战为实时语音 AI Agent 接入虚拟形象与外部视频会议【免费下载链接】agentsA framework for building realtime voice AI agents ️项目地址: https://gitcode.com/GitHub_Trending/agen/agents导读本文围绕开源仓库中livekit-plugins/livekit-plugins-lemonslice插件的 README.md 展开完整讲解如何在 LiveKit Agents 框架中为实时语音 Agent 接入 LemonSlice 虚拟形象Virtual Avatar并通过源码级分析说明其会话启动、API 鉴权、数据流音频传输与 Zoom / Google Meet / Microsoft Teams / Webex 外部会议接入的底层原理。读完本文你将掌握该插件的安装、前置条件、核心参数语义、与AgentSession的接线方式以及外部会议集成模式下的音频与聊天中继机制。一、插件概览LemonSlice 是什么LemonSlice 是一个提供虚拟形象Virtual Avatar能力的在线服务能够让 AI Agent 以人脸形象出现在实时音视频房间中。livekit-plugins-lemonslice是 LiveKit Agents 生态中用于对接该服务的官方插件其职责可以概括为通过 LemonSlice 云端 API 创建Agent 会话把 LiveKit 房间信息URL、Token、Session ID交给 LemonSlice由其托管虚拟形象的渲染与推流在 LiveKit 房间中以一个独立的 Participant 身份加入默认 identity 为lemonslice-avatar-agent发布视频轨道将 Agent 的 TTS 音频通过 LiveKit DataStream 以代发布publish on behalf的方式路由给该虚拟形象实现虚拟形象开口说话可选地支持把虚拟形象派入外部视频会议Zoom、Google Meet、Microsoft Teams、Webex并中继会议音频与聊天消息回 Agent。从仓库结构看插件源码位于 livekit/plugins/lemonslice包含文件职责avatar.pyAvatarSession会话类负责启动会话、加入/离开会议、生成房间选项api.pyLemonSlice REST API 客户端含重试逻辑与图片上传meeting/audio.py会议音频 WebSocket 中继、PCM 反序列化、降混与重采样meeting/chat.py会议聊天消息中继到AgentSessionmeeting/codec.py会议聊天消息的 JSON 线格式解析meeting/room.pyJoinMeetingResult结果类型init.py插件入口导出AvatarSession、LemonSliceException并注册插件二、安装与前置条件2.1 安装插件根据 README插件通过 pip 安装pip install livekit-plugins-lemonslice从 pyproject.toml 可以看到该插件声明的运行时依赖为livekit-agents1.8.0与pillow10.3.0并要求 Python3.10.0。pillow依赖用于支持直接以 PIL 图片作为形象上传的能力而livekit-agents提供了AgentSession、Plugin基类、APIConnectOptions等核心运行设施。2.2 前置条件API Key你需要向 LemonSlice 申请一个 API Key。插件支持两种提供方式环境变量推荐设置LEMONSLICE_API_KEY代码参数在AvatarSession/LemonSliceAPI构造时通过api_key显式传入在 api.py 中客户端会优先使用显式传入的api_key否则回退读取LEMONSLICE_API_KEY环境变量两者都缺失时直接抛出LemonSliceException(LEMONSLICE_API_KEY must be set)。此外插件内置了默认的 API 端点https://lemonslice.com/api/liveai/sessions可通过api_url参数覆盖。除了 LemonSlice 自身的 API Key使用该插件还需要LiveKit 服务器凭据。在AvatarSession.start()中插件要求提供livekit_url、livekit_api_key、livekit_api_secret三者均可通过参数传入或从环境变量LIVEKIT_URL、LIVEKIT_API_KEY、LIVEKIT_API_SECRET读取缺失时同样抛出LemonSliceException见 avatar.py。小结运行时需要的环境变量至少包括LEMONSLICE_API_KEY以及 LiveKit 相关的LIVEKIT_URL、LIVEKIT_API_KEY、LIVEKIT_API_SECRET。三、核心类AvatarSession参数与语义插件对外最重要的类是AvatarSession它继承自 LiveKit Agents 的 AvatarSession 抽象基类livekit.agents.voice.avatar.AvatarSession。基类定义了avatar_identity抽象属性、start()、wait_for_join()、aclose()等生命周期方法AvatarSession继承后实现了provider lemonslice。3.1 构造参数一览参数类型说明agent_idstrLemonSlice 平台上的 Agent形象ID用于在会话中启用某个已配置好的形象agent_image_urlstr直接使用一个图片 URL 作为形象agent_imagePIL.Image.Image直接传入 PIL 图片对象插件会将其编码为 PNG 并以 multipart 上传agent_promptstr提示词用于潜移默化地影响形象在回复时的动作与表情agent_idle_promptstr提示词用于影响形象在空闲时的动作与表情idle_timeoutint空闲超时秒api_urlstr覆盖默认的 LemonSlice API 地址api_keystrLemonSlice API Key缺省读LEMONSLICE_API_KEYavatar_participant_identitystr形象以什么 Participant Identity 加入 LiveKit 房间默认lemonslice-avatar-agentavatar_participant_namestr形象在房间中的显示名默认lemonslice-avatar-agentconn_optionsAPIConnectOptions对 LemonSlice API 的连接选项超时、最大重试次数等默认DEFAULT_API_CONNECT_OPTIONS额外**kwargsdict其余关键字会作为extra_payload原样合并进 API 请求体3.2 形象来源的三选一约束agent_id、agent_image_url、agent_image三者是互斥的。在 api.py 中start_agent_session会统计三个参数中被显式提供的数量为 0抛出LemonSliceException(Missing one of agent_id, agent_image_url or agent_image)大于 1抛出LemonSliceException(Only one of agent_id, agent_image_url or agent_image can be provided)。因此构造AvatarSession时形象来源必须且只能指定一种。3.3 底层 API 请求体结构当不携带图片时start_agent_session会向 LemonSlice API 发送如下 JSON见 api.py{ transport_type: livekit, properties: { livekit_url: LiveKit 服务器 URL, livekit_token: 为形象签发的 LiveKit JWT, livekit_session_id: 房间 SID }, agent_id: 可选, agent_image_url: 可选, agent_prompt: 可选, agent_idle_prompt: 可选, idle_timeout: 60 }若提供了agent_imagePIL 图片则改用aiohttp.FormData进行 multipart 上传payload字段携带上面的 JSONimage字段携带 PNG 编码后的图片字节文件名为image.png、content_type为image/png见 api.py 与_encode_image函数。请求携带X-API-Key请求头进行鉴权。底层_post方法内置了基于APIConnectOptions的指数退避重试总超时 60 秒、建连超时取conn_options.timeout对可重试的错误按_interval_for_retry(i)间隔重试最多重试conn_options.max_retry次对非可重试的APIStatusError直接抛出api.py。四、将 AvatarSession 接入 AgentSession4.1 标准接入流程综合源码与 LiveKit Agents 的 Avatar 使用模式推荐接入流程如下import asyncio from livekit.agents import AgentSession, Agent, RoomInputOptions from livekit.agents.llm import LLM from livekit.plugins.lemonslice import AvatarSession from livekit.plugins.openai import LLM as OpenAILLM async def main(): # 1. 创建 LemonSlice 虚拟形象会话 avatar AvatarSession( agent_id你的 LemonSlice agent_id, agent_prompt表现得热情、专业回复时配合自然的点头动作, agent_idle_prompt空闲时保持自然呼吸感, idle_timeout120, ) # 2. 创建 Agent 会话并把 avatar 挂载进去 session AgentSession( llmOpenAILLM(modelgpt-4o-mini), avataravatar, ) # 3. 获取 avatar 推荐的房间 I/O 选项并启动 room_options avatar.room_options() await session.start( roomroom, room_input_optionsRoomInputOptions(), room_output_optionsroom_options, ) # 4. 等待形象真正加入房间并发布视频轨道 await avatar.wait_for_join() asyncio.run(main())说明以上示例中的 LLM、房间创建等环节与 LiveKit Agents 常规用法一致AgentSession的构造支持avatar参数传入实现了AvatarSession抽象基类的实例。wait_for_join()来自基类实现默认最多等待 30 秒形象未按时加入会抛asyncio.TimeoutError见 livekit-agents 的 _types.py。4.2start()内部到底做了什么调用AvatarSession.start()时插件按顺序完成以下关键动作avatar.py读取 LiveKit 凭据参数优先其次环境变量签发形象 Token用api.AccessToken以 identity/name 为lemonslice-avatar-agent可自定义、kindagent、授予room_join权限签发 JWT并通过with_attributes({ATTRIBUTE_PUBLISH_ON_BEHALF: 本地 Agent identity})声明该形象可代理本地 Agent 发布媒体热切换音频输出调用agent_session.output.replace_audio_tail(...)把音频输出替换为DataStreamAudioOutput目标为形象 identity采样率 16000Hz等待视频轨道出现后再缓冲、等待播放开始调用 LemonSlice API 创建会话把形象来源、提示词、LiveKit 连接信息与 Token 一并提交拿到session_id并返回。其中第 2 步的ATTRIBUTE_PUBLISH_ON_BEHALF是 LiveKit Agents 房间 I/O 层的既有机制——在 room_io/_output.py 与 room_io.py 中_output会读取参与者的该属性来决定媒体发布的实际归属从而让形象发布的音视频被路由到正确的本地 Agent 会话。这也是虚拟形象替你开口说话的实现基石。第 3 步的DataStreamAudioOutput定义于 livekit-agents 的 _datastream_io.py其核心机制是通过 LiveKit 房间的 DataStream 字节流把 TTS 音频推送给远端形象工作进程并注册 RPC 回调来感知播放完成/播放开始实现播放进度的闭环同步。replace_audio_tail保证热替换后TranscriptSynchronizer与RecorderAudioOutput链路不中断中间间隙由wait_remote_track的缓冲兜底。五、外部视频会议集成join_meeting该插件一个突出的能力是把虚拟形象派入 Zoom、Google Meet、Microsoft Teams、Webex 等外部视频会议。调用顺序要求严格先start()创建 LemonSlice 会话再join_meeting()之后才启动AgentSession。5.1 join_meeting 参数与返回值result await avatar.join_meeting( https://meet.google.com/xxx-yyy-zzz, # 外部会议 URL bot_nameLemonSlice Avatar, # 可选机器人在会议中的显示名 listen_to_meeting_chatTrue, # 可选是否把会议聊天中继给 Agent ) print(result.websocket_url) # 会议音频/聊天中继的 WebSocket 地址 print(result.meeting_bot_id) # 外部会议中的机器人 IDJoinMeetingResult定义在 meeting/room.py包含websocket_url混合会议音频与聊天的 WebSocket 地址与meeting_bot_id机器人实例标识两个字段。5.2 调用前置校验与广播 Tokenjoin_meeting()有两个前置约束avatar.py必须先调用过start()否则抛LemonSliceException(call start() before join_meeting())不能重复加入已存在meeting_bot_id时抛already joined a meeting; call leave_meeting() first。此外插件会使用缓存的 LiveKit 凭据签发一个广播 Token_mint_broadcast_token该 Token 以 identity 为lemonslice-meeting-broadcast、TTL 4 小时授权room_join与can_subscribe但禁止发布can_publishFalse、can_publish_dataFalse即只允许订阅形象在会议中的媒体、不允许向 LiveKit 房间发布数据avatar.py。该 Token 随join_meeting请求提交给 LemonSlice用于后续订阅形象在会议场景下的音视频。5.3 会议音频中继加入会议后插件会创建一个MeetingAudioInput继承livekit.agents.voice.io.AudioInput并把它挂到agent_session.input.audio上——这样会议混音音频会直接进入 Agent 的 STT而不再从 LiveKit 房间取音频输入启动后台任务stream_meeting_relay连接 LemonSlice 返回的 WebSocket 中继。MeetingAudioInput的音频流水线meeting/audio.py依次执行反序列化每条二进制帧以I采样率4 字节小端无符号B声道数1 字节的头部 PCM16 数据组成_deserialize_frame降混多声道通过 numpy 取均值降为单声道与 RoomIO 的 STT 输入保持行为一致_downmix_to_mono重采样与目标采样率不一致时通过rtc.AudioResampler实时重采样到 16000Hz_resample入队帧进入有界队列默认容量 100满时丢弃最旧帧STT 侧通过__anext__消费。中继 WebSocket 连接自带20 秒心跳与指数退避重连初始 1 秒、上限 30 秒每次失败翻倍直到收到stop事件meeting/audio.py。5.4 会议聊天中继当listen_to_meeting_chatTrue时MeetingChatRelay会把会议聊天翻译成 Agent 的用户输入WebSocket 的 TEXT 帧经 codec.py 的deserialize_chat解析线格式为{type: chat, sender: ..., text: ..., to: ...}过滤掉机器人自己发送的消息按bot_name或默认LemonSlice Avatar忽略格式化为[sender]: text文本format_chat_user_input放入容量 100 的队列后台 drain 任务依次等待会话离开initializing状态 →await session.interrupt()打断当前语音 →session.generate_reply(user_input...)触发回复meeting/chat.py。5.5 room_options会议模式的 I/O 切换AvatarSession.room_options()会根据当前是否处于会议模式返回不同的RoomOptionsavatar.py会议模式已join_meetingRoomOptions(audio_inputFalse, audio_outputFalse)即关闭 LiveKit 房间侧的音频输入输出会议音频走MeetingAudioInput、聊天走MeetingChatRelay普通模式原样透传调用方传入的RoomOptions。5.6 离开会议与资源释放leave_meeting()会调用 LemonSlice API 的/leave-meeting端点移除机器人 → 置位stop事件并取消中继任务 → 关闭聊天中继。aclose()则先leave_meeting()再调用基类清理avatar.py。基类的aclose()还会通过 LiveKit API 把形象参与者的身份从房间中移除见 livekit-agents 的 _types.py。六、错误处理与可观测性6.1 异常体系插件统一以LemonSliceException继承自Exception定义于 api.py表达配置/前置条件类错误例如缺少LEMONSLICE_API_KEY缺少livekit_url/livekit_api_key/livekit_api_secret形象来源参数缺失或重复未先start()就调用join_meeting()重复加入会议。对 API 调用失败_post会抛出 LiveKit Agents 标准异常APIStatusError服务端返回非 2xx或APIConnectionError重试耗尽。6.2 日志插件使用独立的 loggerlivekit.plugins.lemonslice见 log.py可通过标准 logging 配置开启 DEBUG 级别查看会话 ID、会议中继连接状态、首帧/首条聊天到达等调试信息。七、常见问题与最佳实践形象不出现/没有视频确认wait_for_join()被调用且未超时检查agent_id或图片参数是否合法以及 LiveKit 凭据是否能正常签发 Token。Token 必须带ATTRIBUTE_PUBLISH_ON_BEHALF属性否则音频输出无法正确路由到形象。会议模式下没有声音进 STT确认在AgentSession.start()时使用了avatar.room_options()的返回值会议音频是直接喂给 STT 的若仍从 LiveKit 房间取音频会重复/丢失。重连日志刷屏中继 WebSocket 断线时会按 1s→30s 指数退避自动重连属正常行为若长期连不上检查websocket_url与网络环境。资源泄漏会话结束务必调用await avatar.aclose()它会依次离开会议、取消中继任务、移除房间参与者。形象来源三选一agent_id、agent_image_url、agent_image只能传一个传多会抛异常。八、小结livekit-plugins-lemonslice是 LiveKit Agents 中把 LemonSlice 虚拟形象能力接入实时语音 Agent 的完整实现通过一个AvatarSession类即可完成创建形象会话 → 热切换 DataStream 音频输出 → 形象以独立参与者身份发布音视频并在此基础上延伸出虚拟形象参加 Zoom/Meet/Teams/Webex 会议的高级能力会议侧的音频经 WebSocket 中继重采样后直达 STT、聊天经中继格式化为 Agent 用户输入形成闭环。如需深入阅读实现细节推荐从以下文件开始插件 READMElivekit-plugins/livekit-plugins-lemonslice/README.md会话实现livekit/plugins/lemonslice/avatar.pyAPI 客户端livekit/plugins/lemonslice/api.py会议音频中继livekit/plugins/lemonslice/meeting/audio.py会议聊天中继livekit/plugins/lemonslice/meeting/chat.pyAvatar 抽象基类livekit-agents/livekit/agents/voice/avatar/_types.pyDataStream 音频输出livekit-agents/livekit/agents/voice/avatar/_datastream_io.py插件依赖声明pyproject.toml【免费下载链接】agentsA framework for building realtime voice AI agents ️项目地址: https://gitcode.com/GitHub_Trending/agen/agents创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表