从“播放故事”到“让角色站到你面前”机器人升级落到文娱场景里不只是让设备开口讲故事而是让一个 AI 角色能听懂用户选择、实时回应情绪并用表情、语气和动作推进剧情。这样的体验可以发生在陪伴机器人上也可以发生在屏幕端数字人或 AR/VR 眼镜里的虚拟角色身上。魔珐星云的优势在于它不是把数字人做成一段固定视频而是提供可跨终端落地的具身交互能力屏幕端可以做互动数字人机器人端可以做故事陪伴伙伴AR/VR 端则可以把角色放进沉浸式空间里。这次我没有机器人本体可以联调所以先选择屏幕端作为实验入口。屏幕虽然不是完整机器人但已经能验证最关键的部分AI 是否能根据用户选择改变剧情数字人是否能用声音、表情和姿态把故事“演”出来。基于这个思路我用魔珐星云具身智能数字人开放平台搭建了一个互动叙事 Agent。它把大模型的剧情生成能力接到实时 3D 表达和多模态交互中让用户从“听故事的人”变成“参与故事的人”。下面就从这个实验开始记录我在设计交互逻辑、数字人表达和剧情状态管理时的技术思考。魔珐星云官网魔珐星云官网一、文字Agent的交互天花板做这个项目之前我认真梳理了一下当前AI Agent在交互层面的瓶颈。不讨论模型能力只讨论交互形态。1.1 文字交互的信息密度陷阱文字是一种低信息密度媒介。一句话他推开门看到了一个令人震惊的场景读者需要自己脑补画面、情绪、氛围。这在读小说的时候没问题因为小说的价值恰恰在于激发想象。但在互动场景中这就成了障碍——用户的注意力在阅读文字和做出选择之间反复切换很难保持沉浸感。更致命的是纯文字无法传递语气和情绪。同样一句你确定要打开那扇门吗用平静的语气说和在诡异的低语中说传递的信息完全不同。文字Agent做不到这种区分。1.2 语音交互的单一通道局限加个TTS变成语音Agent呢好一些但还不够。语音只有听觉通道没有视觉反馈。用户在听故事的时候眼睛无处安放——盯着一个空白聊天框看滚动字幕这和听有声书没什么本质区别依然缺少在场感。1.3 传统数字人的视频流陷阱那加个3D数字人不就好了问题没那么简单。市面上大部分数字人产品采用云端渲染视频流推送的方案。数字人确实看得见了但交互体验反而更差了——用户选了一个分支要等2-3秒数字人才开始讲故事视频编码传输解码的延迟带宽成本随用户数线性增长一个互动叙事场景动辄十几分钟的会话时长费用让人头皮发麻表情动作僵硬每次都是同样的微笑挥手预设讲恐怖故事也在微笑这不是某个产品的实现问题而是视频流架构的根本缺陷。视频流的本质是把一个动态画面压缩成一串图像帧这个编码-传输-解码的过程不可避免地引入延迟而且带宽消耗跟画质直接挂钩——你要高清就得加带宽要低延迟就得降画质。二、单点拼接为什么不work看到这里很多人可能会想那我分别把LLM、TTS、3D渲染做到最好再用API串起来不就行了这条路我也走过。结论是单点最优不等于全局最优。2.1 LLM的语义盲区大模型生成文本的能力毋庸置疑但它不知道自己的文字将要由一个数字人说出来。这意味着它不会在需要停顿的地方加标点不会在需要强调的地方加重语气更不会在需要配合手势的地方留出空间。举个例子LLM可能生成这样的文本突然一个黑影从角落里窜了出来你往后退了一步却发现身后也是一堵墙。如果只是阅读这段文字很有画面感。但如果要由数字人说出来你需要额外的信息在窜了出来的时候数字人应该做出惊讶的表情在一堵墙的时候语气应该变得紧张。LLM不提供这些信息——它只管生成文本。2.2 TTS的情感真空现代TTS在自然度上已经做得很好了但大部分TTS是情感扁平的——它用同一种语气念完一整段话不管是惊喜、恐惧还是平静。有的TTS支持SSML标记来调整语速和音调但手动标注既低效又不精确。2.3 渲染的预设陷阱3D渲染引擎能做出逼真的数字人但谁来驱动它的表情和动作如果你用预设动画说话时循环播放挥手用户三分钟就能识破。如果你用AI实时生成动作又回到了延迟问题——生成动作参数本身就需要时间。2.4 串行拼接的延迟雪崩最致命的还是延迟。当LLM500ms→ TTS300ms→ 动作生成200ms→ 渲染100ms→ 网络传输300ms串行执行时总延迟轻松超过1.4秒。在互动叙事场景中这意味着用户选了一个分支后要对着一个正在思考的数字人发呆两秒——沉浸感瞬间碎了一地。三、换一条路参数流AI端渲和端侧解算技术魔珐星云的技术路线完全绕开了视频流的思路用的是参数流架构。理解这个概念对后续的开发实践很关键。3.1 核心思路只传参数不传画面传统视频流方案会在服务端完成全帧画面绘制、编码再将内容分发至终端自研参数流架构仅在云端生成语音、面部与肢体动画等轻量化参数并下发终端依托AI 端渲和解算能力调用本地 GPU 实时生成 3D 数字人画面。打个比方视频流像是看直播参数流像是玩3D游戏。直播的画质和流畅度取决于网络带宽和主播的编码能力游戏的画质取决于你本地显卡的性能网络只影响延迟。这个思路转换带来几个直接好处带宽从Mbps级降到Kbps级参数数据量远小于视频数据量一百个并发用户和一万个并发用户对服务端的带宽压力差异不大客户端渲染意味着画质不降级本机GPU能跑多好就多好AI端渲和端侧解算技术让延迟结构更扁平不需要视频编解码环节百元级芯片就能部署RK3588跑1080pRK3566跑720p不挑硬件3.2 三层打通从感知到表达魔珐星云的三层架构把感知-认知-表达端到端打通Plaintext 多模态感知层理解用户输入 ↓ 大模型智能体认知层LLM推理决策 ↓ 多模态具身表达层语音表情身体动画端侧渲染关键在于这三层不是独立拼接而是联合优化的。从用户输入到数字人开口回应整条链路的延迟控制在端到端≈500ms。这个数字在我的实际测试中是可信的——在网络正常的情况下用户做出选择后数字人几乎秒开口不需要等一个尴尬的空白期。3.3 对互动叙事意味着什么在互动叙事场景中≈500ms的端到端响应意味着用户选完分支后数字人立刻开始讲下一段没有出戏的等待长对话不心疼参数流的带宽消耗极低十几分钟的叙事会话成本几乎可以忽略表情动作跟内容同步SDK的SSML机制允许在文本中嵌入动作指令数字人在讲小心的时候会配合紧张的表情和手势四、实战搭建一个AI互动叙事Agent4.1 项目核心点以实时交互为核心打造沉浸式互动叙事体验传统叙事类数字人方案交互功能与故事内容相互割裂仅作为附加模块存在本项目依托魔珐星云自研参数流架构 AI 端渲和解算带来的低延迟实时交互能力实现交互与叙事深度融合用户行为、情绪可全链路实时作用于整体叙事流程。分支选择即时驱动剧情每到关键节点用户从3个分支中做出选择每一次选择都会实时生效直接带来实质性的剧情分化。不是选A还是B的表面选择而是每个选择都会让后续剧情产生实质性的分歧。自由对话支持随时打断用户不必被动等待分支选项随时可以开口说话跟数字人对话。你的想法、情绪、意图都会被AI感知并反映到接下来的剧情生成中——比如你对某个角色表达了好奇后续剧情就会围绕这个角色展开更多内容。情绪感知联动形象表达系统可实时捕捉用户情绪驱动数字人同步调整语音、神态与肢体状态。当用户流露紧张情绪数字语气、神态随之变得沉稳当用户情绪高涨则搭配对应的肢体动作与表情。全程动态联动告别固定化内容输出模式。三类实时交互模式层层叠加、有机结合打破单向内容输出的局限让用户彻底告别单调的单向收听沉浸式玩转整个故事。4.2 场景设计用户进入页面后选择一个故事世界比如赛博朋克城市数字人开始讲述开场剧情。讲完后屏幕上出现3个分支选项用户选择一个数字人继续讲对应的剧情然后再次出现新的分支——如此循环。这个场景的技术核心是三件事LLM流式生成故事内容用Qwen或DeepSeek数字人实时朗读用星云SDK的流式speak接口向量检索上下文确保AI记住之前的剧情4.3 极简Demo代码下面是完整可运行的HTML代码。LLM部分使用DeepSeek作为故事生成的大脑。HTML !DOCTYPE html html langzh-CN head meta charsetUTF-8 title灵魂讲述者 - AI互动叙事Demo/title style * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: -apple-system, sans-serif; background: #0d1117; color: #c9d1d9; display: flex; height: 100vh; overflow: hidden; } /* 数字人区域 */ #avatar-container { width: 45%; height: 100%; position: relative; background: #161b22; } /* 故事面板 */ .story-panel { width: 55%; display: flex; flex-direction: column; padding: 24px; overflow-y: auto; } h1 { font-size: 20px; color: #58a6ff; margin-bottom: 16px; } .story-text { flex: 1; line-height: 1.8; font-size: 15px; padding: 16px; background: #161b22; border-radius: 10px; border: 1px solid #30363d; margin-bottom: 16px; white-space: pre-wrap; } /* 分支选项 */ .choices { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; } .choice-btn { padding: 14px 18px; background: #21262d; border: 1px solid #30363d; border-radius: 8px; color: #c9d1d9; cursor: pointer; text-align: left; font-size: 14px; transition: all 0.2s; } .choice-btn:hover { border-color: #58a6ff; background: #1c2333; } .choice-btn:disabled { opacity: 0.4; cursor: not-allowed; } /* 配置区 */ .config { display: flex; gap: 8px; margin-bottom: 12px; flex-wrap: wrap; } .config input { flex: 1; min-width: 140px; padding: 8px 12px; border: 1px solid #30363d; border-radius: 6px; background: #161b22; color: #c9d1d9; font-size: 12px; } .btn { padding: 8px 16px; border: 1px solid #30363d; border-radius: 6px; background: #21262d; color: #c9d1d9; cursor: pointer; font-size: 13px; } .btn:hover { border-color: #58a6ff; } .btn-primary { background: #238636; border-color: #238636; color: white; } .status { font-size: 12px; color: #8b949e; text-align: center; margin-bottom: 8px; } /style /head body div idavatar-container/div div classstory-panel h1灵魂讲述者 - AI互动叙事/h1 div classconfig input idapp-id placeholder星云 App ID / input idapp-secret typepassword placeholder星云 App Secret / input idllm-key typepassword placeholderDeepSeek API Key / button classbtn btn-primary onclickstartStory()开始冒险/button /div div classstatus idstatus填写密钥后点击开始冒险/div div classstory-text idstory-text 欢迎来到互动叙事世界。连接数字人后AI将为你讲述一个专属故事在每个关键时刻你的选择将改变剧情走向…… /div div classchoices idchoices/div /div !-- 加载星云SDK -- script srchttps://media.xingyun3d.com/xingyun3d/general/litesdk/xmovAvatarlatest.js/script script let sdk null; let storyHistory []; // 剧情历史用于LLM上下文 let currentChoices []; // 世界观设定 const WORLD { name: 赛博暗巷, setting: 故事发生在2147年的新上海。城市被巨型企业天穹集团控制 居民生活在霓虹灯与废墟交织的底层街区。你是一个信息贩子 靠在暗网中买卖秘密为生。今晚你收到了一条加密信息—— 来自一个已经消失三年的老朋友。, style: 赛博朋克黑暗悬疑以第二人称叙事 }; // 星云SDK连接 async function initAvatar() { const appId document.getElementById(app-id).value; const appSecret document.getElementById(app-secret).value; if (!appId || !appSecret) { setStatus(请填写星云密钥xingyun3d.com 免费获取); return false; } setStatus(正在连接数字人...); sdk new XmovAvatar({ containerId: #avatar-container, appId, appSecret, gatewayServer: https://nebula-agent.xingyun3d.com/user/v1/ttsa/session, onStateChange: (state) setStatus(数字人: state), onVoiceStateChange: (status) { if (status end) { // 数字人讲完当前段落显示选项让用户选择 showChoices(); } }, onMessage: (msg) { if (msg.code 40000) console.error(SDK:, msg); }, enableLogger: true }); try { await sdk.init({ onDownloadProgress: (p) setStatus(资源加载: p %) }); await sdk.idle(); setStatus(数字人就绪); return true; } catch (e) { setStatus(连接失败: e.message); return false; } } // 故事生成LLM流式数字人驱动 async function startStory() { const llmKey document.getElementById(llm-key).value; if (!llmKey) { setStatus(请填写 DeepSeek API Key); return; } // 连接数字人 const ok await initAvatar(); if (!ok) return; // 生成开场 await generateNarrative(这是故事的开始请生成开场剧情。); } async function generateNarrative(userAction) { const llmKey document.getElementById(llm-key).value; // 构建上下文保留最近5段剧情 const context storyHistory.slice(-5).map( (h, i) 第${i 1}段: ${h.narrative} ).join(\n); setStatus(AI正在构思...); // 切换数字人状态为思考 if (sdk) await sdk.think(); try { // 调用DeepSeek生成剧情分支一次API调用 const response await fetch(https://api.deepseek.com/chat/completions, { method: POST, headers: { Content-Type: application/json, Authorization: Bearer llmKey }, body: JSON.stringify({ model: deepseek-chat, messages: [ { role: system, content: 你是一个互动小说引擎。 世界观${WORLD.setting} 风格${WORLD.style} 规则 1. 每次续写150-250字的剧情段落 2. 在剧情末尾提供3个不同的分支选项 3. 选项应该有明确的后果提示 4. 严格按以下JSON格式输出不要加任何其他文字 {narrative:剧情内容,choices:[{text:选项1,hint:可能的后果},{text:选项2,hint:可能的后果},{text:选项3,hint:可能的后果}]} }, { role: user, content: 已有剧情\n${context || 无这是开头}\n\n用户行动${userAction}\n\n请续写剧情。 } ], temperature: 0.9, response_format: { type: json_object } }) }); const data await response.json(); const result JSON.parse(data.choices[0].message.content); // 记录到历史 storyHistory.push(result); // 让数字人讲故事 await speakNarrative(result.narrative); currentChoices result.choices; } catch (e) { setStatus(生成失败: e.message); if (sdk) await sdk.idle(); } } // 数字人流式朗读 async function speakNarrative(text) { if (!sdk) return; // 按标点分块模拟自然的说话节奏 const chunks text.match(/[^。…][。…]?/g) || [text]; let isFirst true; for (let i 0; i chunks.length; i) { const chunk chunks[i].trim(); if (!chunk) continue; const isEnd (i chunks.length - 1); await sdk.speak(chunk, isFirst, isEnd); isFirst false; } // 在页面文字区域同步显示 const storyEl document.getElementById(story-text); storyEl.textContent \n\n text; storyEl.scrollTop storyEl.scrollHeight; } // 分支选择 function showChoices() { const container document.getElementById(choices); container.innerHTML ; currentChoices.forEach((choice, index) { const btn document.createElement(button); btn.className choice-btn; btn.textContent ${index 1}. ${choice.text}${choice.hint ? —— choice.hint : }; btn.onclick () makeChoice(choice); container.appendChild(btn); }); } async function makeChoice(choice) { // 清空选项 document.getElementById(choices).innerHTML ; setStatus(你选择了${choice.text}); // 切换到倾听状态 if (sdk) await sdk.listen(); // 生成新剧情 await generateNarrative(玩家选择了${choice.text}${choice.hint}); } // 工具函数 function setStatus(text) { document.getElementById(status).textContent text; } // 页面关闭时释放资源 window.addEventListener(beforeunload, () { if (sdk) sdk.destroy(); }); /script /body /html使用方式保存为 .html 文件通过本地服务器打开npx serve . 或 python -m http.server填入星云密钥和DeepSeek API Key点击开始冒险即可体验。4.4 代码中的几个关键设计1一次API调用同时生成剧情分支注意我在system prompt中要求LLM同时输出 narrative剧情内容和 choices3个分支选项用JSON格式约束输出。这比先让LLM写剧情再调一次API生成分支要高效得多——一次调用解决两个问题且内容与选项之间的语义一致性更好。2按标点分块的流式朗读speakNarrative() 函数用正则按标点。…把文本分块然后逐块送入 sdk.speak()。这样做的好处是数字人的说话节奏更自然——在每个句号处自然停顿而不是一口气念完一整段。首块 isStarttrue 立即开始驱动后续块顺序追加末块 isEndtrue 收尾。3语音状态回调驱动的UI同步核心的交互节奏是由 onVoiceStateChange 回调驱动的。数字人讲完一段话status end后才显示分支选项让用户选择。这保证了用户是在听完全部内容后才做决定而不是边听边被选项分散注意力。4.5 从Demo到完整产品我的项目经验上面的Demo是最小实现。我的完整项目——灵魂讲述者——还加入了几个让体验质变的特性向量检索上下文故事越讲越长LLM的上下文窗口放不下所有历史剧情。我用Qwen3-Embedding把每段剧情向量化存入IndexedDB生成新剧情时先语义检索最相关的历史段落作为上下文。这确保了AI能记住几十个节点前的关键伏笔。SSML动作系统星云SDK支持在文本中嵌入 ue4event 标签触发特定手势。比如开场时用Welcome动作悬疑场景用Think动作遇到转折时用Surprise动作。这些动作和文本内容同步执行让数字人的表演与故事氛围匹配。情绪感知对话除了选分支用户还可以自由对话影响剧情。后台用Qwen3-VL分析用户发言的情绪数字人的回应风格和SSML动作会根据情绪自动调整——用户说我害怕时数字人语气变温柔配合安慰手势用户说冲时数字人语气变激昂。背景动态切换10张不同氛围的背景图随故事节点循环切换悬疑场景用暗色背景温馨场景用暖色背景增强视觉沉浸感。五、开发者视角SDK、API与架构5.1 SDK接入比想象中简单魔珐星云的JS SDK接入门槛不高。CDN加载一个脚本文件核心API只有七八个方法。我整理了一个快速上手清单Plaintext 1. 加载SDK: script src...xmovAvatarlatest.js 2. 创建实例: new XmovAvatar({ containerId, appId, appSecret, gatewayServer, callbacks }) 3. 初始化: await sdk.init({ onDownloadProgress }) 4. 控制状态: sdk.idle() / .listen() / .think() / .interactiveidle() 5. 驱动说话: sdk.speak(text, isStart, isEnd) 6. 销毁实例: sdk.destroy()需要注意的几个坑必须通过 localhost 或 HTTPS 访问file:// 协议不支持WebSocket容器元素必须有明确的宽高不能用 flex: 1 之类的弹性布局撑出来页面离开时必须调用destroy()否则服务端会话不释放很快触发并发限制5.2 SSML动作让数字人会表演星云SDK的SSML机制是让数字人活起来的关键。三种动作类型语义动作ka_intent按意图触发手势。比如在欢迎各位前嵌入Welcome意图数字人会做出欢迎手势。XML speak热烈ue4eventtypeka_intent/type dataka_intentWelcome/ka_intent/data /ue4event欢迎你来到这个世界.../speak技能动作ka触发预定义的特定动画如dance、wave等。说话动作ka action_semantic在说话的同时执行动作实现边说边比划。在我的叙事项目中我建立了一个情绪到动作的映射表场景SSML动作效果故事开场Welcome数字人做出欢迎手势悬疑紧张Think数字人皱眉、抱臂剧情转折Surprise数字人做出惊讶表情悲伤片段Comfort数字人做出安慰手势战斗场景Celebrate数字人做出胜利姿势这种语义驱动的动作系统比纯预设动画高明得多——动作不是机械循环的而是跟正在说的内容语义相关的。5.3 AI Coding工具的实际价值我在这个项目中实际使用了三种AI编程工具各有分工Claude Code负责架构设计和复杂逻辑实现。比如向量检索服务、流式文本分块策略、状态机的异常处理——这些需要理解全局上下文的任务交给Claude Code更合适。它能在项目级别理解代码之间的关系避免局部修改引入全局问题。Cursor 星云AI Coding Skill负责SDK集成和前端组件开发。星云官方的Skill文件本质上是一个结构化的Prompt教会AI编辑器如何正确使用SDK——包括初始化流程、状态机管理、中断协议、生命周期保护。部署后在Cursor中输入初始化星云数字人项目AI就能生成完整的可运行代码。DeepSeek / Qwen作为项目的AI大脑。DeepSeek擅长创意写作和JSON结构化输出适合故事生成。Qwen3-VL支持多模态在需要分析用户上传图片的场景中表现更好。两个模型都通过OpenAI兼容API调用切换成本低。5.4 架构全景完整项目的技术架构Plaintext ┌──────────────────── 前端React TypeScript────────────────────┐ │ │ │ ┌────────────┐ ┌──────────────┐ ┌───────────────────────────┐ │ │ │ 数字人容器 │ │ 故事界面 │ │ 对话面板 │ │ │ │ (星云SDK) │ │ (分支选择) │ │ (自由对话影响剧情) │ │ │ └─────┬──────┘ └──────┬───────┘ └──────────┬────────────────┘ │ │ │ │ │ │ │ ┌─────▼──────┐ ┌──────▼───────┐ ┌──────────▼────────────────┐ │ │ │ 星云SDK │ │ StoryEngine │ │ IntelligentDialogue │ │ │ │ 状态机驱动 │ │ 节点流转管理 │ │ 情绪分析SSML动作选择 │ │ │ └─────┬──────┘ └──────┬───────┘ └──────────┬────────────────┘ │ │ │ │ │ │ │ ┌─────▼──────┐ ┌──────▼───────┐ ┌──────────▼────────────────┐ │ │ │ WebSocket │ │ VectorStore │ │ EmotionAnalysis │ │ │ │ 参数流接收 │ │ IndexedDB │ │ Qwen3-VL情绪检测 │ │ │ └────────────┘ └──────────────┘ └───────────────────────────┘ │ └──────────────────────────┬────────────────────────────────────────┘ │ ┌────────────┼────────────┐ │ │ │ ┌────────▼──┐ ┌──────▼──────┐ ┌─▼──────────────┐ │ 星云服务 │ │ DeepSeek │ │ Qwen3-Embedding │ │ TTS动画 │ │ 故事生成 │ │ 向量化 │ │ 参数流 │ │ 分支生成 │ │ 语义检索 │ └───────────┘ └─────────────┘ └─────────────────┘六、总结具身Agent——下一代交互入口的雏形做完这个项目后我对AI Agent这个概念有了新的理解。传统的Agent定义是能自主感知、决策、执行的AI系统。但在我看来如果Agent的执行只是生成一段文字或调用一个API那它的能力边界其实很窄。真正的执行应该包括表达——不只是输出信息而是用语音、表情、手势等方式像一个真实的存在一样与用户交互。魔珐星云给我的体验是它补上了Agent的表达层。LLM提供认知能力星云提供具身表达能力两者结合就形成了一个AI具身智能体——能思考、能表达、能感知用户状态、能自然交互。几个具体的体验感受关于延迟。端到端≈500ms的响应速度在互动叙事场景中体感非常明显。用户选完分支后数字人几乎秒接话不会出现那种选完了盯着屏幕等半天的尴尬。这不是锦上添花而是决定了用户是否愿意继续聊下去的关键。关于成本。参数流架构的带宽消耗极低。我做过一个粗略对比同样10分钟的互动会话视频流方案大约需要1.5GB数据传输量参数流方案只需要约30MB。这意味着在高并发场景下参数流的运营成本可能是视频流的几十分之一。关于开发者体验。星云SDK的设计哲学是少即是多——核心API只有几个方法但足够灵活。配合AI Coding工具和官方Skill文件一个前端开发者从零到跑通一个数字人应用一个下午就能搞定。这种开发效率对于快速验证创意想法非常重要。关于场景想象。互动叙事只是冰山一角。我尝试过在教育场景AI数学老师、情绪陪伴场景心灵伴侣、企业展厅场景智能讲解中使用星云SDK——每次都有新的发现。具身Agent的可能性远比我们想象的要大。最后说一句2025年的AI行业在疯狂卷模型能力但交互形态的创新同样重要。当所有Agent都还在用文字对话的时候谁能率先给Agent一副身体让交互从读写变成听看说——谁就可能在下一代人机交互的竞争中占据先机魔珐星云至少给出了一个可行的技术路径。相关资源魔珐星云开发者文档https://xingyun3d.com/developers/52-183魔珐星云AI Coding Skill文档https://rsjqcmnt5p.feishu.cn/wiki/ULNQwoiKwid2tVkTpAlcMb49nKgDeepSeek API故事生成https://platform.deepseek.com/ModelScope API国产LLM/Embeddinghttps://modelscope.cn/魔珐星云魔珐星云官网原文链接https://blog.csdn.net/hanwangyyds/article/details/161931197