说一句话镜头就换装:实时 AI 虚拟试衣智能体快速上手
【免费下载链接】Vision-AgentsOpen Vision Agents by Stream. Build voice and vision agents quickly with any model or video provider. Uses Stream's edge network for ultra-low latency.项目地址: https://gitcode.com/GitHub_Trending/vi/Vision-Agents
用 Vision-Agents 仓库的 Decart 示例,带你看怎么搭一个语音驱动的实时虚拟试衣(AI 试衣)智能体:说一句“换一套超级英雄服装”,镜头里的造型立刻变好,还会用语音念出结果。
开场:说一句,镜头里的衣服就换了
想象一下你在视频通话里,随口说了句“给我换超级英雄造型”。几秒钟后,你摄像头里的人已经穿着超级英雄服装,旁边还有声音补一句“换好了”。这不是后期特效:你的摄像头视频流被 Decart 的 Lucy 模型逐帧重写,而整个过程由你的一句话触发。
这个玩法在仓库里有一个现成示例:入口是plugins/decart/example/decart_example.py,重绘的核心是 Decart 插件里的RestylingProcessor(plugins/decart/vision_agents/plugins/decart/decart_restyling_processor.py)。下面的内容都围绕这一份代码展开,看完你就知道每段在干什么、哪里可以改成自己的样子。
能力全景:五个组件各管一段
先给结论:一句话要经过五道环节——语音转文字、意图理解、视频重绘、语音播报,再叠加一张低延迟传输网络。
| 环节 | 组件 | 干什么 |
|---|---|---|
| 语音转文字(STT) | Deepgram | 把你的话变成文本 |
| 意图理解 | OpenAI GPT-5 | 判断这是换装请求,并决定调用哪个工具 |
| 实时视频重绘 | Decart Lucy(lucy_2_rt) | 按提示词和参考图逐帧重画摄像头画面 |
| 语音播报(TTS) | ElevenLabs | 用有表现力的声音念出换装结果 |
| 音视频传输 | Stream edge 网络 | 保证视频与音频低延迟送达 |
重绘这一环由RestylingProcessor负责。它属于VideoProcessorPublisher类型(agents-core 里定义的一类处理器:既消费入向视频轨,又把处理结果发布回通话),所以它一头接你的摄像头流,一头把重写后的新视频轨发回通话,中间走 WebSocket 与 Decart Realtime API 通信。
五分钟跑通示例:准备、安装、配键、启动 ⚙️
读完这一节,示例就能在你机器上跑起来,浏览器里出现演示页面,你只管开口。
准备
需要 Python 3.10 及以上环境,外加五组密钥:OpenAI、Decart、ElevenLabs、Deepgram、Stream。如果还没拿到仓库,先克隆:
git clone https://gitcode.com/GitHub_Trending/vi/Vision-Agents依赖怎么装
示例目录由 uv 管理,进入plugins/decart/example/后执行uv sync即可,它会把vision-agents、Decart 插件以及 openai、elevenlabs、getstream、deepgram 四个插件包一起装好。插件本身基于 Decart 官方 SDK,版本固定在>=0.0.34,<0.0.36区间。若只在自己的项目里复用 Decart 插件,执行uv add "vision-agents[decart]"就够了。
环境变量怎么配
在示例目录放一个.env文件,示例代码开头会调用load_dotenv()把它读进来:
OPENAI_API_KEY=... DECART_API_KEY=... ELEVENLABS_API_KEY=... DEEPGRAM_API_KEY=... STREAM_API_KEY=... STREAM_API_SECRET=...其中 Decart 的密钥也可以直接写进构造函数RestylingProcessor(api_key="...");两处都没有时,构造阶段就会抛出ValueError。
启动后会发生什么
在示例目录执行uv run decart_example.py run。随后智能体依次:创建视频通话、在浏览器打开演示 UI、加入通话、先说一短句问候,然后进入待命,等你的换装请求。
跟一次换装走完全链路:听到、理解、重绘、播报
这一节把“换超级英雄服装”这句请求从头跟到尾,顺便解释示例为什么登记了两个换装工具。
听到。你的声音先到 Deepgram,转成文本。
理解。文本交给 GPT-5。组装智能体时,示例给它登记了两个函数:change_costume(name)管预设服装,change_outfit(description, image_url)管自由描述。走哪条路由模型自己定,边界写在instructions里——预设列表内的名字走前者,列表外的请求走后者,且每次变换后要用一句短句口头播报。预设库就是示例文件顶部的COSTUMES字典:
COSTUMES: dict[str, dict[str, Optional[str]]] = { "jacket": { "prompt": "A person wearing a jacket", "image": "https://images.unsplash.com/...", # 参考图 URL }, "superhero": { "prompt": "A person wearing a superhero costume", "image": "https://images.unsplash.com/...", # 参考图 URL }, }每个条目有两样东西:驱动重绘的prompt,以及可选的参考图image;image为None时,风格完全由 prompt 决定。
重绘。预设路径下,change_costume("superhero")查到条目后,把 prompt 和图片一次性交给处理器(细节下一节展开)。自由路径则分叉:带参考图 URL 时同样走update_state,不带图就走只改提示词的update_prompt。这样设计的取舍很清楚——预设通道效果可控,适合策展式衣橱;自由通道兜住长尾请求,但画面效果取决于模型发挥。
整个智能体的组装比组件列表长不了多少:
processor = decart.RestylingProcessor(model="lucy_2_rt") llm = openai.LLM(model="gpt-5") agent = Agent( edge=getstream.Edge(), agent_user=User(name="Virtual Wardrobe", id="agent"), instructions="You are a playful virtual wardrobe assistant...", llm=llm, tts=elevenlabs.TTS(voice_id="N2lVS1w4EtoT3dr4eOWO"), stt=deepgram.STT(), processors=[processor], )instructions里还允许模型在播报文本中嵌入[sigh]、[excited]、[pause]、[rushed]、[tired]这类音频标签,让语音更有戏。
播报。工具执行返回后,GPT-5 生成一句短句,ElevenLabs 把它念出来,你在通话里听到“换好了”。
值得深挖的三个细节 🔍
管线里其他部分可以略读,下面三点最影响行为,也最容易踩坑。
为什么换装不会出现中间态
重绘参数通过update_state下发:prompt 与参考图被打包进同一个SetInput,一次送出——这就是原子更新(两项参数同帧生效,画面里不会出现换到一半的状态)。两个参数一个都不传会直接抛ValueError;与 Decart 的连接尚未建立时,调用是静默的 no-op(只记一条调试日志)。update_prompt则是“只改提示词”的快捷方式,内部等价于只传 prompt 的update_state调用:
# 预设通道:prompt 与参考图原子下发 await processor.update_state(prompt=costume["prompt"], image=costume["image"]) # 自由通道,带参考图 URL await processor.update_state(prompt=description, image=image_url) # 自由通道,不带参考图 await processor.update_prompt(description)两个方法都接受可选的enhance参数,临时覆盖实例默认值。
参考图支持哪五种输入
同一个image位置接受五种形态:bytes原始字节、本地文件路径(如"./costumes/superhero.png")、http(s) URL、data:URI、原始 base64 字符串。字符串入参会被 SDK 依次按“本地路径 → URL/data URI/base64”的次序解析,所以这五种你挑着方便的那种用即可。
连接断了要不要重连,看错误分类
asyncio.Lock加两个状态标志保证不会两处同时建连;一个后台循环每秒检查一次连接状态,发现掉线就自动发起重连。是否重连并不一刀切,而是按错误分类判断:WebSocket 被关闭,或 SDK 报错文本里含 "connection"、"disconnect"、"timeout" 这类字样,视为瞬时故障,重连;而密钥失效这类认证问题重试无意义,直接放弃。参与者离开通话时,stop_processing()会主动断开连接并清理轨道。
顺带一句其余部分:重写后的帧进入DecartVideoTrack,它只缓存最近 2 帧,超尺寸帧统一缩放到 1280×720,长时间没新帧时回退到一块深色占位帧,输出轨因此永远不会卡住。
想自己改:定制清单 ✂️
所有改动集中在两处:COSTUMES字典,和RestylingProcessor的构造函数。
服装库怎么增删
直接往字典里加条目或删条目。每条目必须有prompt,image可省,参考图五种形态任选其一。
让首帧就穿好
希望通话一开始用户就已经“穿着”某套造型,就把初始提示词和初始参考图交给处理器——首次建连时它们会随ModelState一起下发,第一帧即为重绘后的画面:
processor = decart.RestylingProcessor( model="lucy_2_rt", initial_prompt="A person wearing a superhero costume", initial_image="./costumes/superhero.png", )处理器参数一览
| 参数 | 默认值 | 说明 |
|---|---|---|
api_key | 读环境变量DECART_API_KEY | 两处皆空时抛ValueError |
model | "lucy_2_rt" | Decart 实时模型名 |
initial_prompt | "Cyberpunk city" | 初始风格提示词 |
initial_image | None | 初始参考图,五种形态均可 |
enhance | True | 是否对提示词做增强 |
mirror | True | 前置摄像头镜像模式 |
width/height | 1280/720 | 输出分辨率(模型偏好值) |
换个音色
组装代码里elevenlabs.TTS(voice_id=...)换成任意 ElevenLabs 音色 ID,播报的声音就变了。
想校验行为是否符合预期,把仓库里的两个测试文件当“行为契约”来用:plugins/decart/tests/test_decart_restyling.py覆盖了连接生命周期、原子更新与 no-op、参数校验、自动重连与错误分类,plugins/decart/tests/test_decart_video_track.py覆盖了输出轨的尺寸、入队、超尺寸缩放和占位帧回退——改动重绘逻辑后,对着逐条跑一遍即可。还想继续看的话,plugins/decart/README.md讲了插件的通用用法(比如只用update_prompt做动态风格切换);examples/02_golf_coach_example与examples/05_security_camera_example和本示例共用同一套 Agent / Runner / 插件体系,放在一起对照读效率很高。
【免费下载链接】Vision-AgentsOpen Vision Agents by Stream. Build voice and vision agents quickly with any model or video provider. Uses Stream's edge network for ultra-low latency.项目地址: https://gitcode.com/GitHub_Trending/vi/Vision-Agents
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考