
一、交互范式重构大模型如何从“理解世界”走向“进入现场”1.1 从文本问答到具身在场AI 交互边界的重新定义以 DeepSeek 为代表的大模型已经赋予 AI 完善的语言理解、逻辑推理、文本生成能力各类纯文本 Agent 也成为主流工具。但这类 AI 只拥有 “思考大脑”缺少完整具身交互智能落地展厅、门店等线下终端时仅靠文字聊天框无法实现真人化现场沟通。在办公和编程场景中用户可以耐心阅读长文本也可以随时复制、修改和反复追问而在展厅环境中参观者通常站在屏幕前希望直接提出问题并迅速判断系统是否已经接收、是否正在处理以及何时开始回答。若整个过程仍依赖键盘输入和大段文字阅读大模型虽然接入了展厅系统却还没有真正融入展厅的交互环境。真实场景中的交流不只有信息内容还包括声音、节奏、姿态和即时反馈。面对真人讲解员时我们能够从眼神、表情和动作中判断对方是否正在倾听也可以在讲解过程中随时插话调整交流方向。文本 AI 通常只能呈现最终答案用户很难感知其等待、处理和表达过程。因此AI 正在经历一次从文本交互到具身交互智能的变化。具身交互智能不是简单给 AI 贴虚拟皮肤也不止实体人形机器人是一套完整人机交互体系。网页中的 3D 数字人、展厅大屏上的虚拟讲解员、移动应用中的数字助手以及人形机器人都可以成为 AI 的身体载体。在我看来具身交互智能是指让 AI 拥有可被看见的身体、可被感知的状态、可自然表达的语音表情动作以及可实时响应、可随时打断的交互能力。这意味着AI 的回答不再只是屏幕上的一段文字而是一个完整的表达过程用户提出问题后智能体能够呈现交互状态通过语音和动作组织答案并允许用户中途打断或继续追问。从这个角度看大模型负责 AI 认知推理Agent 负责任务调度执行魔珐星云作为全域具身交互智能开放平台补齐 AI 缺失的实体载体、多模态表达、实时双向交互整套能力。它绝非简单数字人渲染工具也不是 Agent 外层视觉装饰依托自研参数流、AI 端侧解算为所有大模型、Agent 提供标准化具身交互底层能力。换句话说大模型解决 AI 的“大脑”魔珐星云补齐 AI 的“身体、表达和交互”。只有当认知能力与表达载体真正连接起来AI 才可能从一个隐藏在聊天框中的文本工具转变为能够进入真实服务场景的智能体。为了验证这种交互方式本文选择展厅讲解作为具体场景搭建一个由 DeepSeek 与魔珐星云 SDK 共同驱动的 3D 讲解 Agent。二、从能力拼接到体验断层传统数字人方案的系统性瓶颈2.1 LLM、TTS 与渲染链路之间的协同断点从功能层面看构建一个“能回答、会说话、有形象”的数字人并不复杂大模型负责理解问题并生成文本TTS 将文本转换为语音数字人渲染模块再完成口型、表情和动作呈现。多个模块依次连接似乎已经具备完整的交互能力。但真实体验并不取决于单个模块是否可用而取决于整条链路能否协同运行。当大模型、语音合成和数字人渲染彼此独立时开发者仍需自行处理文本切分、语音队列、口型同步、动作衔接、状态切换与异常恢复。模型已经输出内容语音却仍在等待声音已经播放人物动作尚未跟进用户发起新问题后旧回答仍在继续。这些看似细微的不同步最终都会转化为用户能够直接感知的等待感和割裂感。因此具身交互的技术门槛并不是简单地把 LLM、TTS 和 3D 形象串联起来而是让认知、声音、动作和交互状态围绕同一轮对话形成统一节奏。2.2 从预制播报到实时交互的能力分界传统数字人更擅长处理预先确定的内容例如宣传视频、固定讲解词或循环播放脚本。这类场景以内容展示为核心语音、口型和动作可以提前制作用户也不需要在播放过程中改变交流方向。展厅讲解 Agent 面对的则是动态交互。参观者的问题无法提前预设回答需要由大模型实时生成数字人的语音、表情和动作也必须随内容同步变化。更重要的是参观者可能在讲解过程中继续追问、补充条件甚至直接插话系统需要迅速结束旧任务并进入下一轮交流。这正是“能播报”与“能交互”的根本区别。前者关注内容是否能够被完整呈现后者关注智能体能否理解现场变化、及时组织表达并保持多轮对话的连续性。数字人的核心价值也由此从“像人展示内容”转向“代表 AI 与人自然交流”。2.3 参数流与端侧技术重构实时交互链路面对实时交互需求继续依赖多套独立系统叠加不仅会拉长处理链路也会增加云端渲染、网络传输和终端部署的复杂度。尤其在展厅大屏、门店终端和公共服务设备中数字人既要保持稳定呈现也要及时回应用户的提问与打断。魔珐星云采用自研参数流架构并结合 AI 端渲和端侧解算技术将具身表达所需的渲染与解算能力下沉至终端。与持续传输完整视频画面的方式不同参数流侧重传递数字人的驱动信息再由终端完成实时呈现从而打通内容生成、语音表达、动作驱动和画面渲染之间的链路。在这一体系中大模型继续承担理解与生成魔珐星云则将文本内容转化为语音、口型、表情和身体动作并通过 SDK 接入网页、应用、大屏、机器人及 AR/VR 等终端形态。官方资料给出的端到端约 500ms 响应、实时打断、多终端兼容等能力也正是面向具身交互场景而设计。由此可见数字人从展示工具走向具身智能体关键不在于增加多少独立功能而在于是否建立了一条真正端到端、可实时响应、可持续交互的表达链路。三、重构具身交互链路DeepSeek 与魔珐星云的协同架构3.1 从认知生成到具身呈现展厅讲解 Agent 的分层设计搭建展厅讲解 Agent 时并不是简单地把 DeepSeek 的回答交给一个数字人朗读而是将系统拆分为认知生成、对话编排和具身交互三个层次。参观者提出问题后DeepSeek 负责理解问题并生成符合展厅讲解语境的回答应用层负责维护对话上下文、接收流式内容、划分可播报语句并处理新旧任务的切换魔珐星云 SDK 则负责加载和驱动 3D 数字人将文本内容转化为语音、口型、表情与动作最终呈现在网页或展厅屏幕上。三层架构的价值在于职责清晰。更换大模型不会改变数字人的呈现方式调整角色形象和音色也不需要重写对话逻辑。当后续需要加入展品知识库、路线查询或业务接口时只需在 Agent 编排层扩展对应能力。3.2 从文本回答到多模态表达星云 SDK 承担的关键环节DeepSeek 返回的结果本质上仍是一段文本。要将这段内容变成用户能够直接感知的讲解过程还需要处理声音生成、口型同步、人物动作、角色状态以及 3D 画面呈现。魔珐星云官方将具身驱动描述为将 AI 表达由文本升级为 3D 多模态。其 JavaScript SDK 提供实时 3D 数字人渲染与驱动、语音合成与口型同步、多状态行为控制以及字幕、图片、视频等 Widget 事件能力。开发者还可以通过状态、语音和网络回调掌握数字人的运行过程。在本文的展厅 Demo 中星云 SDK 主要承担四项工作首先在网页中初始化加载 3D 具身交互智能数字人其次接收 DeepSeek 生成的讲解文本同步驱动数字人完成语音、口型和动作表达最后实时反馈交互状态支撑双向对话。由此DeepSeek 与魔珐星云并不是简单的两个接口串联而是分别负责“讲什么”和“如何讲”。前者生成讲解内容后者让内容获得形象、声音和动作。3.3 从信息展示到智能服务展厅屏幕的交互升级传统展厅屏幕承担的主要是内容展示按照预设顺序播放图片、视频和讲解词。它可以持续、稳定地传递信息却很难根据参观者的兴趣改变讲解内容。当用户对某件展品产生疑问时屏幕展示的仍然是既定内容真正的交流还需要依赖现场工作人员。接入大模型后屏幕开始具备理解问题和动态组织内容的能力但一段生成在界面上的文字还不足以替代现场讲解。展厅环境强调直观、即时和连续的体验参观者需要看到讲解者、听到讲解内容并通过角色的口型、表情和动作感知交流过程。DeepSeek 与魔珐星云的组合正好对应了这两个层面的需求DeepSeek 根据参观者的问题生成讲解内容魔珐星云则将文字回答转化为数字人的语音、口型、表情和动作。原本只能循环播放内容的屏幕由此获得了一位能够围绕现场问题进行动态表达的 3D 讲解员。此时展厅数字人的价值也不再只是“形象更生动”而是成为展览内容与参观者之间的交互入口。例如参观者可以继续追问展品背景、补充问题或改变讲解方向系统则围绕当前对话重新组织内容并通过数字人完成下一轮表达。屏幕由单向的信息出口转变为能够接收问题、生成回答并持续交流的服务终端。四、基于魔珐星云 JS SDK 的展厅讲解 Agent 实战前面几章讨论的是为什么展厅 Agent 需要具身表达层以及 DeepSeek 和魔珐星云分别负责什么。从这一章开始不再停留在架构图上直接进入接入过程。这次使用的技术栈是 Vue 3、TypeScript 和 ViteDeepSeek-V4-Pro 作为讲解内容生成模型魔珐星云 JavaScript SDK 负责数字人的渲染与表达。下面按照你原稿中“后台配置—SDK 初始化—流式驱动—动作与 Widget—中断状态机”的实战顺序重写业务统一为DeepSeek-V4-Pro 展厅讲解 Agent。原稿里的注册流程和开发踩坑得以保留但删去了与商场服务台相关的内容。4.1 在魔珐星云后台创建具身交互智能体应用写代码之前需要先在魔珐星云平台中把数字讲解员配置出来。角色形象、音色和表演风格都属于应用配置SDK 初始化时只需要提供对应应用的 App ID 和 App Secret。Step 1:注册账号打开专属链接 https://xingyun3d.com/?utm_campaignjixinghuiKoc228utm_sourcejixinghuiKocutm_mediumutm_termutm_content ,点右上角登录注册。Step 2:进入体验中心 → 具身驱动登录之后第一件事不是急着创建应用,先去体验中心 → 具身驱动那一页玩一玩。这个页面是星云的试驾区,你能直接在浏览器里和内置数字人对话——我看到的有睡前陪伴(萌萌的水獭形象,海滩主题)、元气段子手、AI 男友等几个内置形象。直接点底部任意一个,然后右下角开始聊天,就能和它说话。Step 3:点击创建具身应用体验完之后,右上角创建具身应用按钮就是我们的目的地。点进去会让你做几个关键选择:选择形象这是最影响最终用户体验的一步。星云的形象库分几个风格档位:超写实、美型、卡通、二次元。服务台场景的形象选型逻辑:不要选超写实——商场环境光线复杂,超写实形象在弱光或强光下容易出现恐怖谷。除非你的部署场景光线极度可控(比如展厅射灯),否则避开美型/卡通是首选——既有人形识别度,又有我是数字人的合理预期,不会让顾客产生对方是不是真人的错位感二次元谨慎选——除非是动漫主题商场或者目标客群是 Z 世代,普通商场用二次元会显得不正式我最终选了一个偏成熟的女性卡通形象,30 岁左右,职业装感——既亲切又有服务员该有的专业感。选择音色音色库我快速试听了一遍,大致分:亲切女声 / 专业女声 / 活泼少女音 / 中年男声 / 商务男声。服务台场景我选的是亲切但不娃娃音的女声——太活泼会显得不专业,太成熟会显得有距离感。这一步建议你听一遍每一个音色再选,文字描述很难还原音色的实际气质。选择表演风格这个我理解是数字人的基础节奏——平稳/活泼/专业。服务台选专业,讲解类场景可以选活泼。Step 4:进入应用管理,拿到 appId / appSecret应用创建完之后,左侧菜单应用管理页面里能看到刚才创建的应用,点进去 → 查看密钥,就能拿到appId和appSecret。这两个就是后面所有 SDK 代码里要用的凭证。到这一步,准备工作就完成了。下面进入代码部分。4.2 接入 JavaScript SDK让数字讲解员开口前端工程使用 Vue 3、TypeScript 和 Vite。项目结构不需要设计得过于复杂只要把数字人驱动、模型请求和流式播报分开即可deepseek-exhibition-agent/ ├─ src/ │ ├─ components/ │ │ ├─ AvatarPanel.vue │ │ └─ ExhibitCard.vue │ ├─ composables/ │ │ └─ useStreamSpeak.ts │ ├─ App.vue │ └─ main.ts ├─ server/ │ ├─ index.ts │ └─ data/ │ └─ exhibits.json └─ index.html我使用 ChatGPT 辅助拆分了组件和 TypeScript 类型但给它设置了一个明确约束不得根据常见数字人 SDK 的经验自行补充接口所有方法都必须来自魔珐星云官方文档。引入 SDK在index.html中直接引入 JavaScript SDK!doctype html html langzh-CN head meta charsetUTF-8 / meta nameviewport contentwidthdevice-width, initial-scale1.0 / titleDeepSeek 展厅讲解 Agent/title /head body div idapp/div script srchttps://media.xingyun3d.com/xingyun3d/general/litesdk/xmovAvatarlatest.js/script script typemodule src/src/main.ts/script /body /html然后将 SDK 封装为独立的AvatarPanel.vuetemplate div classavatar-wrapper div idxmov-avatar classavatar-container/div div v-if!ready classloading-mask p数字讲解员资源加载中{{ progress }}%/p progress :valueprogress max100/progress /div /div /template script setup langts import { onBeforeUnmount, onMounted, ref } from vue; declare const XmovAvatar: any; const emit defineEmits{ ready: []; voiceState: [status: string]; widget: [type: string, data: Recordstring, unknown]; }(); const ready ref(false); const progress ref(0); let avatar: any null; onMounted(async () { avatar new XmovAvatar({ containerId: #xmov-avatar, appId: import.meta.env.VITE_XMOV_APP_ID, appSecret: import.meta.env.VITE_XMOV_APP_SECRET, gatewayServer: https://nebula-agent.xingyun3d.com/user/v1/ttsa/session, hardwareAcceleration: prefer-hardware, enableLogger: true, onMessage(message: { code?: number; message?: string }) { console.log([Xmov message], message); if (message.code message.code 10000) { console.error([Xmov error], message.message); } }, onStateChange(state: string) { console.log([Xmov state], state); }, onVoiceStateChange(status: string) { console.log([Xmov voice], status); emit(voiceState, status); }, onNetworkInfo(info: { rtt: number; downlink: number; }) { console.log([Xmov network], info); }, proxyWidget: { widget_exhibit_card(data: Recordstring, unknown) { emit(widget, exhibit_card, data); }, }, }); await avatar.init({ initModel: normal, onDownloadProgress(value: number) { progress.value value; console.log([Xmov loading] ${value}%); }, }); ready.value true; emit(ready); }); onBeforeUnmount(() { avatar?.destroy(); avatar null; }); function speak( content: string, isStart: boolean, isEnd: boolean, ): void { if (!avatar) { throw new Error(数字讲解员尚未初始化); } avatar.speak(content, isStart, isEnd); } function interactiveIdle(): void { avatar?.interactiveidle(); } function idle(): void { avatar?.idle(); } defineExpose({ speak, interactiveIdle, idle, }); /script style scoped .avatar-wrapper { position: relative; width: 540px; height: 960px; overflow: hidden; background: #101522; } .avatar-container { width: 100%; height: 100%; } .loading-mask { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; background: rgba(8, 14, 28, 0.88); } /style官方 SDK 通过XmovAvatar创建实例初始化时需要提供容器、App ID、App Secret 和服务地址。init()负责建立连接并加载资源speak()负责驱动数字人播报interactiveidle()可以将数字人切换回交互待机状态页面卸载前则应调用destroy()清理资源。首次加载不是“黑屏”第一次启动页面时数字人不会立即出现。SDK 需要先加载当前应用对应的角色、表情和动作资源如果界面没有任何提示容器很容易看起来像一块黑屏。因此我将onDownloadProgress直接绑定到了页面进度条上。官方文档说明该回调范围为 0—100如果首次所需资源加载失败进度不会正常到达 100。另一个容易踩到的问题是访问环境。SDK 使用的部分浏览器能力只支持localhost或 HTTPS。本地开发可以直接使用localhost但通过普通 HTTP 局域网地址访问时相关能力可能无法正常工作。([魔珐星云][1])数字人初始化完成后先使用固定欢迎语验证主链路avatarRef.value?.speak( 欢迎来到人工智能主题展区 我是本次展览的数字讲解员。 您可以向我了解展品原理、技术背景和参观路线。, true, true, );speak()的三个参数分别是播报内容、是否为本轮流式内容的开始以及是否为本轮流式内容的结束。一次性提交完整文本时后两个参数都设置为true。点击测试按钮后数字讲解员开始播放欢迎语口型和身体动作随语音一起呈现。到这里数字人这一侧已经跑通接下来需要把固定文本替换为 DeepSeek-V4-Pro 的实时回答。4.3 接入 DeepSeek-V4-Pro实现流式具身讲解服务端主要做三件事接收参观者问题、加载相关展品资料、调用 DeepSeek-V4-Pro 并把流式内容转发给前端。本次先使用一份本地exhibits.json保存展品资料[ { id: robot-arm-01, name: 六轴工业机器人, zone: 智能制造展区, summary: 通过六个旋转关节完成多方向运动可用于装配、搬运、焊接和精密操作。, keywords: [机械臂, 六轴, 机器人, 智能制造] }, { id: edge-chip-01, name: 边缘计算芯片, zone: 人工智能展区, summary: 在终端本地处理部分人工智能任务降低数据频繁往返云端的需求。, keywords: [芯片, 边缘计算, 端侧AI] } ]服务端根据问题筛选相关展品将资料与问题一起交给模型。DeepSeek 官方 API 当前支持deepseek-v4-pro并通过stream: true以 SSE 形式返回内容增量本文关闭思考模式只将最终讲解文本传给数字人。// server/index.ts import dotenv/config; import express from express; import { readFile } from node:fs/promises; const app express(); app.use(express.json()); const exhibits JSON.parse( await readFile( new URL(./data/exhibits.json, import.meta.url), utf-8, ), ); const SYSTEM_PROMPT 你是科技展厅的数字讲解员。 回答要求 1. 只能根据提供的展品资料回答 2. 不编造资料中不存在的信息 3. 使用自然、清晰、适合口头讲解的中文 4. 单次回答控制在100字以内 5. 不使用Markdown标题、列表和表格 6. 讲到具体展品时可以插入对应的展品卡片指令。 ; app.post(/api/chat, async (req, res) { const question String(req.body?.question ?? ).trim(); if (!question) { return res.status(400).json({ message: 问题不能为空, }); } const matched exhibits.filter((item: any) item.keywords.some((keyword: string) question.includes(keyword), ), ); const context (matched.length ? matched : exhibits) .slice(0, 5) .map( (item: any) ID${item.id} 展品${item.name} 展区${item.zone} 资料${item.summary}, ) .join(\n\n); const upstream await fetch( https://api.deepseek.com/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer ${process.env.DEEPSEEK_API_KEY}, }, body: JSON.stringify({ model: deepseek-v4-pro, thinking: { type: disabled, }, stream: true, messages: [ { role: system, content: SYSTEM_PROMPT, }, { role: user, content: 展品资料\n${context}\n\n 参观者问题${question}, }, ], }), }, ); if (!upstream.ok || !upstream.body) { const detail await upstream.text(); return res.status(502).json({ message: DeepSeek 请求失败${detail}, }); } res.setHeader(Content-Type, text/event-stream); res.setHeader(Cache-Control, no-cache); res.setHeader(Connection, keep-alive); const reader upstream.body.getReader(); const decoder new TextDecoder(); let buffer ; while (true) { const { done, value } await reader.read(); if (done) break; buffer decoder.decode(value, { stream: true, }); const lines buffer.split(\n); buffer lines.pop() ?? ; for (const line of lines) { if (!line.startsWith(data: )) continue; const payload line.slice(6).trim(); if (payload [DONE]) { res.write( data: ${JSON.stringify({ done: true, })}\n\n, ); res.end(); return; } const chunk JSON.parse(payload); const token chunk.choices?.[0]?.delta?.content ?? ; if (token) { res.write( data: ${JSON.stringify({ token, })}\n\n, ); } } } res.end(); }); app.listen(3001, () { console.log(Agent server: http://localhost:3001); });前端收到的不是一整段回答而是一串持续到达的文本增量。这里不能每收到一个 Token 就立即调用speak()中文可能被切得过碎SSML 标签也可能在中间断开数字人甚至会把不完整的标签当作普通文本读出来。我的处理方式是先把 Token 写入缓冲区在完整句子或完整标签边界处再提交给 SDK// src/composables/useStreamSpeak.ts import type { Ref } from vue; interface AvatarInstance { speak: ( content: string, isStart: boolean, isEnd: boolean, ) void; interactiveIdle: () void; } export function useStreamSpeak( avatarRef: RefAvatarInstance | null, ) { let buffer ; let firstChunk true; const FIRST_FLUSH_THRESHOLD 20; function start(): void { buffer ; firstChunk true; } function feed(token: string): void { buffer token; if ( firstChunk buffer.length FIRST_FLUSH_THRESHOLD ) { return; } if (!isSafeBoundary(buffer)) { return; } flush(false); } function finish(): void { flush(true); } function flush(isEnd: boolean): void { if (!buffer !isEnd) return; if (buffer) { avatarRef.value?.speak( speak${buffer}/speak, firstChunk, isEnd, ); buffer ; firstChunk false; } } function isSafeBoundary(content: string): boolean { // 当前存在尚未闭合的 XML/SSML 标签 if (/[^]*$/.test(content)) { return false; } return /[。!?]$/.test(content); } return { start, feed, finish, }; }魔珐星云官方文档支持分段调用speak()第一段设置is_starttrue最后一段设置is_endtrue中间内容的两个标记均为false。文档也特别建议首次流式提交前先积累一小段内容避免数字人说话速度追上模型输出速度而产生停顿。加入动作与展品信息卡仅仅做到“边生成边播报”数字人仍然可能像在机械念稿。下一步是使用 SSML 中的 KA 指令为讲解增加动作。例如欢迎参观者时可以插入官方示例中的欢迎动作speak ue4event typeka_intent/type data ka_intentWelcome/ka_intent /data /ue4event 欢迎来到智能制造展区。 /speak魔珐星云 SDK 支持在speak()的 SSML 内容中嵌入语义 KA、技能 KA 等指令。具体可用动作应通过当前应用的 KA 查询结果确认不能凭经验随意填写动作名称。除了动作我还增加了一个自定义的widget_exhibit_card事件。当讲解员介绍某件展品时模型可以在合适的位置嵌入展品 IDspeak 这件展品是六轴工业机器人。 ue4event typewidget_exhibit_card/type data idrobot-arm-01/id /data /ue4event 它通过六个旋转关节完成多方向运动 常用于装配、搬运和焊接。 /speak前面初始化XmovAvatar时已经通过proxyWidget注册了该事件。父组件收到回调后只需要根据 ID 查询本地展品数据function onWidget( type: string, data: Recordstring, unknown, ): void { if (type ! exhibit_card) return; currentExhibit.value exhibits.find( (item) item.id data.id, ) ?? null; }这样数字讲解员说到对应展品时侧边区域会同步出现展品名称、图片、展区和核心介绍。SDK 官方提供onWidgetEvent与proxyWidget两种事件代理方式也允许业务侧实现自定义 Widget。4.4 实现可打断的多轮讲解流式播报跑通以后最后一个关键问题是中断。参观者可能在讲解尚未结束时提出新问题。此时只停止声音还不够旧的 DeepSeek 请求仍可能继续返回内容前端缓冲区里也可能保留尚未提交的句子。如果不统一清理新的问题开始以后旧回答还会重新进入播报队列。因此一次完整中断需要同时处理三层状态模型层终止或忽略旧 DeepSeek 请求 业务层清空文本缓冲与旧任务标记 具身层调用 interactiveidle() 中断当前状态前端使用AbortController管理当前请求let activeController: AbortController | null null; let requestVersion 0; async function askAgent(question: string): Promisevoid { const version requestVersion; activeController?.abort(); activeController new AbortController(); avatarRef.value?.interactiveIdle(); streamSpeak.start(); const response await fetch(/api/chat, { method: POST, headers: { Content-Type: application/json, }, body: JSON.stringify({ question }), signal: activeController.signal, }); if (!response.ok || !response.body) { throw new Error(讲解服务请求失败); } const reader response.body.getReader(); const decoder new TextDecoder(); let sseBuffer ; try { while (true) { const { done, value } await reader.read(); if (done) break; if (version ! requestVersion) return; sseBuffer decoder.decode(value, { stream: true, }); const events sseBuffer.split(\n\n); sseBuffer events.pop() ?? ; for (const event of events) { const line event .split(\n) .find((item) item.startsWith(data: ), ); if (!line) continue; const data JSON.parse(line.slice(6)); if (data.token) { streamSpeak.feed(data.token); } if (data.done) { streamSpeak.finish(); } } } } catch (error) { if ( error instanceof DOMException error.name AbortError ) { return; } throw error; } } function interruptAndAsk( nextQuestion: string, ): void { requestVersion 1; activeController?.abort(); avatarRef.value?.interactiveIdle(); void askAgent(nextQuestion); }interactiveidle()在官方文档中的用途之一就是打断当前状态并返回交互待机一次完整speak()结束以后文档也建议先通过interactive_idle完成状态切换再开始下一轮播报。([魔珐星云][1])数字讲解员的状态可以简化为interactive_idle ↓ 用户提问 waiting_model ↓ 第一段内容到达 speaking ↓ 播报结束 interactive_idle当用户在speaking状态中再次提问时则走另一条路径speaking ↓ 用户插话 interrupting ↓ 取消旧请求、清理旧缓冲 waiting_model ↓ 播放新回答 speaking为了判断数字人什么时候真正开始和结束讲话可以监听onVoiceStateChange。官方文档说明speak()过程中会抛出语音开始和结束事件可用于管理当前播报状态。完成中断逻辑后我连续测试了三种情况正常提问并等待完整回答长回答生成过程中继续追问数字人正在播报时立即改问另一件展品。重点观察旧请求是否停止、缓存内容是否清空、新旧回答是否发生串音以及数字人能否重新进入正常播报状态。到这里展厅讲解 Agent 的完整主链路已经打通这套 Demo 真正解决的并不是“让数字人读出一段大模型回答”而是验证了具身交互智能对纯文本 Agent 的升级作用。DeepSeek 负责内容推理魔珐星云补齐拟人交互全套能力将单向文字输出升级为有形象、有情绪、可实时对话的完整现场交互。当数字讲解员能够根据现场问题动态组织内容、同步展示展品信息并允许参观者随时改变交流方向时屏幕才从一个内容播放器真正变成了可以交流的展厅服务入口。可以。第五章直接承担全文收束不再继续拆流式缓冲、中断机制等实现细节这些都留在第四章实战中完成。第五章重点总结三件事Demo 验证了什么、魔珐星云提供了什么价值、具身交互为什么不仅适用于展厅。五、从展厅原型到具身交互落地开发体验与价值总结5.1 这次接入真正验证了什么回看整个搭建过程这个 Demo 完成的并不只是“让 DeepSeek 驱动一个数字人说话”。如果只是将大模型输出接入 TTS让页面播放一段语音其实很快就能完成。但那种方案依旧停留在文本问答的延伸形态模型负责生成语音负责朗读屏幕上的形象与整个对话过程没有形成真正的协同。这次接入魔珐星云 SDK 后DeepSeek 生成的讲解内容能够继续转化为数字人的语音、口型、表情和身体动作当讲解涉及具体展品时页面还能同步展示对应的信息卡用户在播报过程中改变问题系统也可以停止上一轮任务并切换到新的讲解内容。从用户视角看整个过程不再是“提交问题后等待一段文字”而是提出问题 → 数字讲解员开始组织回答 → 语音、口型和动作同步呈现 → 展品信息随讲解出现 → 用户可以继续追问或中途插话这些变化单独看都不复杂但组合起来之后屏幕的角色就不再只是一个负责播放内容的数字形象而开始具备智能体的基本交互特征。这也是这次实战最重要的结论具身交互的价值不在于让 AI 看起来更像人而在于让 AI 的理解、表达和反馈发生在同一条交互链路中。DeepSeek 解决了“讲什么”Agent 业务层负责组织展品资料、维护对话和管理任务魔珐星云则解决“如何讲出来”。三者各自独立又通过清晰的接口协同工作。这种分层方式也为后续扩展留下了空间。未来无论是将本地展品数据替换为 RAG 知识库还是接入路线查询、展项状态、票务信息和多语言能力变化主要发生在认知层和业务层数字人的具身表达链路不需要重新搭建。5.2 魔珐星云提供的不只是一个数字人形象在接入之前我对数字人产品的第一反应仍然是形象、音色和动作效果。但真正把 SDK 接进项目后能清晰感知魔珐星云绝非单纯 3D 形象工具而是标准化具身交互智能底层基础设施。整合语音合成、3D 渲染、表情肢体解算、交互状态管理全套能力一套 SDK 即可完成完整拟人交互链路解决各类 Agent 缺失具象表达的行业难题。在第四章的实战中这种整合最明显地体现在三个地方。第一是流式表达。DeepSeek 不需要等完整回答全部生成后再交给数字人一次性播放而是可以按照语义片段持续提交。数字讲解员能够更早开始表达现场交流中的等待感也随之降低。第二是非语言表达。语音只是讲解的一部分口型、表情和身体动作决定了这段内容是否像一次真正的交流。通过 KA 指令模型生成的语言内容可以与动作意图一起进入表达链路数字人不再只是站在屏幕中机械念稿。第三是业务界面协同。Widget 事件让数字人的讲解能够与展品卡片、图片、字幕和其他页面内容同步。它不是把一个独立数字人窗口叠加到现有页面上而是让数字人表达与业务界面形成统一节奏。从底层技术路径看魔珐星云强调参数流、AI 端渲和端侧解算。其核心思路不是持续向终端传输已经渲染完成的视频画面而是传递具身表达所需的驱动信息再由终端完成实时呈现。这种方式减少了对完整云端视频流和持续云端渲染资源的依赖也为低延时、高并发和多终端部署提供了基础。因此魔珐星云不是给 Agent 套上一层数字人外观也不是替代 DeepSeek 这样的模型。更准确地说大模型解决 AI 的“大脑”Agent 负责执行与流程魔珐星云补齐 AI 的身体、表达和实时交互。这也是为什么本文最终采用“具身交互”而不是“数字人播报”来描述这套方案。前者关注完整的人机交流后者更接近内容展示。5.3 从一块展厅屏幕走向更多真实服务终端展厅是一个很适合验证具身交互的场景。传统展厅屏幕通常循环播放图片、视频和固定讲解内容。即使加入搜索或问答功能交互入口也大多还是文本框。接入 DeepSeek 和魔珐星云后屏幕可以根据参观者的问题动态组织内容并通过数字讲解员完成语音和动作表达。它所带来的变化不只是“展厅里多了一个数字人”而是原本单向展示的屏幕开始具备接收问题、组织信息和持续交流的能力。这套思路也不局限于展厅。在零售门店中它可以成为能够介绍商品、展示商品卡片并接受顾客插话的导购 Agent在教育培训中它可以将知识问答转化为带有语音、表情和动作的讲解过程在文旅场景中它可以承担景点导览和文化内容介绍在政务、金融和酒店服务中它也可以成为业务系统面向用户的可视化交互入口。终端形态同样可以继续延展。本文使用网页完成接入是为了方便开发和验证但相同的认知与表达链路还可以进入商显大屏、自助终端、应用、人形机器人以及 AR/VR 等设备。这说明具身交互并不是某一种硬件独占的能力。它更像是连接大模型、Agent 与真实终端的一层通用接口上层可以替换模型和业务系统下层可以适配不同的屏幕和设备中间的具身表达能力保持相对稳定。回到文章最开始的问题当 DeepSeek 已经能够理解、推理和生成内容后AI 为什么还需要一个身体经过这次接入我的答案更加明确。身体不是装饰而是 AI 进入真实场景的交互界面。声音、口型、表情、动作和状态反馈共同决定用户是否愿意把屏幕里的系统当作一个可以交流的对象。只有当这些表达能力与大模型的认知能力真正连接起来AI 才能从聊天框中的文本工具转变为出现在展厅、门店、学校和服务终端中的具身智能体。DeepSeek 让数字讲解员知道应该回答什么魔珐星云让它知道应该如何站在用户面前完成表达。从文本问答到具身交互改变的不只是 AI 的外观而是人与 AI 发生交流的方式。写在最后大模型让 AI 学会思考魔珐星云让 AI 进入终端这次实践让我更加确信AI 赛道竞争已经从文本推理比拼转向具身交互智能落地能力比拼。大模型让 AI 学会思考魔珐星云让 AI 进入终端以具身的方式与人交互。从展厅讲解出发这套方案还可以继续延展到零售导购、教育陪练、文旅导览、政务咨询以及机器人等场景。真正有价值的不只是让数字人“开口说话”而是让 AI 能够进入现场、理解用户并以更自然的方式完成持续交流。相关开发资料可参考魔珐星云开发者文档与 SDK:https://xingyun3d.com/developers/52-183魔珐星云 AI Coding 操作手册:https://rsjqcmnt5p.feishu.cn/wiki/ULNQwoiKwid2tVkTpAlcMb49nKg魔珐星云官网:https://xingyun3d.com/?utm_campaignjixinghuiKoc228utm_sourcejixinghuiKocutm_mediumutm_termutm_content