给项目装上“AI身体”:3D数字人零门槛接入实战,免费体验额度已就绪
不用高端GPU、不写复杂代码,一个下午让项目拥有会说话、带表情手势的3D数字人。新用户注册送免费额度,零成本验证。

一、当别人还在用文字框,你的项目已经用上3D数字人了
先问你一个问题:你的用户打开你的产品,看到的是一个文字聊天框,还是一个会说话、有表情、能互动的3D数字人?
前者是工具,后者是体验。
在AI应用同质化严重的今天,体验就是竞争力。一个能说话、有表情、做手势的3D数字人,能让你的项目瞬间从“普通”变得“高端大气”。用户停留时长、互动意愿、品牌好感度,都会明显提升。
最近我用魔珐星云做了一个小Demo——数字人讲师。它可以朗读故事、诵读诗词,配合恰当的表情和肢体动作。用户随时可以打断或切换内容,交互非常流畅。
最关键的是:
- 核心调用不到50行代码
- 硬件用的是百元级国产芯片(RK3566)
- 从零到跑通只花了半小时
- 新用户有免费额度,零成本验证
这篇文章不讲复杂的技术原理,重点分享:怎么用魔珐星云的低门槛SDK,给你的项目快速接入一个能提升档次的3D数字人。文章最后还有问卷抽奖活动,完成体验即可参与——详见文末。
二、传统方案太贵太难,魔珐星云把门槛打下来了
过去,要在项目中集成3D数字人,往往面临三重阻碍:
| 痛点 | 说明 |
|---|---|
| 硬件门槛高 | 需要高端GPU(如RTX 3090以上),部署成本动辄数万元 |
| 延迟严重 | 云端渲染导致交互延迟>3秒,对话体验“卡顿” |
| 开发复杂 | 需自研3D渲染、动作生成、语音合成等,没有图形学专家基本做不了 |
魔珐星云:给AI“装上身体”,让数字人触手可及
魔珐星云是魔珐科技推出的具身智能3D数字人开放平台,面向全行业提供基础设施。它的核心作用就一句话:让AI从“文字/语音”进化到“有表情、有动作、可交互”的3D数字人。
对外开放全套API和SDK,整合了3D角色制作、多模态驱动、端侧AI渲染等全栈能力,四大核心优势直接把行业门槛打穿:
| 优势 | 说明 |
|---|---|
| 硬件友好 | 无需高端GPU,百元级国产芯片即可稳定运行,部署成本降低90% |
| 交互流畅 | 云端传参数、本地渲染,端到端延迟<1.5秒,支持实时对话、随时打断 |
| 全终端适配 | 支持Web、手机、大屏、车载、人形机器人等,一套代码多端运行 |
| 开发简单 | SDK封装了底层渲染和动作生成,开发者只需关心业务逻辑,不用从零研究3D |
最重磅的是:新用户注册即送免费体验额度,完全够跑通一个完整Demo,零成本验证价值。
注册地址:https://xingyun3d.com/login-register
注册时填写邀请码【XDXAC7K2XC】会直接赠送1000积分,完全能支撑你尝试
三、半小时跑通数字人讲师Demo
光说不练假把式。我基于魔珐星云LiteSDK做了一个数字人讲师的互动Demo,支持故事点播、诗词朗读、快捷话术和自由对话。
最终效果
| 模块 | 功能说明 |
|---|---|
| 数字人舞台 | 3D数字人实时渲染,带表情、口型、手势动作 |
| 故事点播 | 内置5个经典故事(小王子/龟兔赛跑/狼来了等),点击即朗读 |
| 诗词学习 | 内置8篇长诗词(离骚/琵琶行/长恨歌/蜀道难等) |
| 快捷话术 | 常用语一键发送 |
| 控制动作 | 挥手/问候/鼓励/告别,带文案和动作 |
| 自由对话 | 输入任意文本,数字人即时朗读 |
接入有多简单?
先进行注册
注册地址:https://xingyun3d.com/login-register
注册时填写邀请码【XDXAC7K2XC】会直接赠送1000积分,完全能支撑你尝试
注册后创建一个应用

然后有很多的形象可以选择,可以自己DIY数字人形象,然后点击右上角接入SDK,然后复制下面亮相内容

整个Demo的核心驱动逻辑不到50行代码。流程就是:
- 引入SDK:一行script标签加载魔珐星云LiteSDK
- 填入凭证:在控制台填入App ID和App Secret,上面复制的
- 初始化:调用
XmovAvatar创建实例 - 驱动说话:调用
sdk.speak(text),数字人自动朗读
剩下的——语音合成、口型同步、表情生成、动作编排——全部由魔珐星云在云端完成,本地只做渲染。

完整代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>魔珐星云数字人 · 互动故事 Demo</title>
<style>
*{box-sizing:border-box;margin:0;padding:0;scrollbar-width:none}
*::-webkit-scrollbar{width:0;height:0}
html,body{height:100%}
body{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;background:radial-gradient(1000px 600px at 15% -10%,rgba(255,45,149,.18) 0,transparent 60%),radial-gradient(900px 600px at 100% 110%,rgba(0,229,255,.14) 0,transparent 55%),linear-gradient(180deg,#0d0420 0,#060212 100%);color:#f0e6ff;min-height:100vh;padding:20px;overflow-x:hidden}
.topbar{max-width:1320px;margin:0 auto 18px;display:flex;align-items:center;justify-content:space-between;gap:16px;flex-wrap:wrap}
.brand{display:flex;align-items:center;gap:12px}
.brand .logo{width:38px;height:38px;border-radius:11px;background:linear-gradient(135deg,#ff2d95,#b026ff);display:flex;align-items:center;justify-content:center;box-shadow:0 6px 18px rgba(255,45,149,.4)}
.brand .logo svg{width:22px;height:22px;stroke:#fff}
.brand .title h1{font-size:17px;font-weight:600;background:linear-gradient(90deg,#5eeaff,#c89cff);-webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent}
.brand .title .sub{font-size:11px;color:#9a86b8;margin-top:2px}
.status-row{display:flex;gap:8px;flex-wrap:wrap}
.badge{display:inline-flex;align-items:center;gap:6px;padding:5px 11px;border-radius:999px;background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);font-size:11px;color:#d9c8f0}
.dot{width:8px;height:8px;border-radius:50%;background:#7a6a8e}
.dot.connected{background:#27c07b;animation:pulse 1.6s infinite}
.dot.busy{background:#ffbf3f;animation:pulse 1.2s infinite}
.dot.error{background:#ff5b78}
@keyframes pulse{0%{box-shadow:0 0 0 0 rgba(39,192,123,.5)}70%{box-shadow:0 0 0 8px rgba(39,192,123,0)}100%{box-shadow:0 0 0 0 rgba(39,192,123,0)}}
.layout{max-width:1320px;margin:0 auto;display:grid;grid-template-columns:320px 540px 1fr;gap:18px;align-items:start}
.panel{background:linear-gradient(180deg,rgba(255,255,255,.06),rgba(255,255,255,.02));border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:18px;backdrop-filter:blur(12px);box-shadow:0 10px 40px rgba(0,0,0,.35);position:sticky;top:20px;display:flex;flex-direction:column;height:calc(100vh - 140px)}
.panel-section{margin-bottom:16px}
.panel-section:last-child{margin-bottom:0}
.list-section{flex:1;min-height:0;display:flex;flex-direction:column}
.list-wrap{flex:1;min-height:0;position:relative}
.section-title{font-size:11px;font-weight:600;color:#9a86b8;letter-spacing:1px;text-transform:uppercase;margin-bottom:10px;display:flex;align-items:center;gap:8px}
.section-title::before{content:'';width:3px;height:12px;border-radius:2px;background:linear-gradient(180deg,#ff2d95,#b026ff)}
.field{margin-bottom:10px}
.field label{display:block;font-size:12px;color:#a894c0;margin-bottom:6px}
.field input,.field textarea{width:100%;background:rgba(20,10,40,.6);border:1px solid rgba(255,255,255,.1);border-radius:10px;padding:10px 12px;color:#f0e6ff;font-size:13px;outline:none;transition:border-color .2s,box-shadow .2s;font-family:inherit}
.field input:focus,.field textarea:focus{border-color:#00e5ff;box-shadow:0 0 0 3px rgba(0,229,255,.15)}
.field input:disabled,.field textarea:disabled{opacity:.5;cursor:not-allowed}
.btn-row{display:flex;gap:8px}
button{flex:1;padding:10px 12px;border:none;border-radius:10px;font-size:13px;font-weight:600;cursor:pointer;color:#fff;letter-spacing:.3px;transition:transform .15s,box-shadow .2s,opacity .2s,background .2s}
button:active{transform:translateY(1px)}
button:disabled{opacity:.45;cursor:not-allowed}
.btn-primary{background:linear-gradient(135deg,#ff2d95,#b026ff);box-shadow:0 6px 18px rgba(255,45,149,.35)}
.btn-warn{background:linear-gradient(135deg,#ff2d6b,#ff5b78);box-shadow:0 6px 18px rgba(255,45,107,.3)}
.btn-ghost{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);flex:0 0 auto;padding:10px 14px}
.btn-ghost:hover:not(:disabled){background:rgba(255,255,255,.14)}
.divider{height:1px;background:rgba(255,255,255,.08);margin:16px 0}
.tabs{display:flex;gap:4px;background:rgba(0,0,0,.25);border:1px solid rgba(255,255,255,.08);border-radius:10px;padding:4px;margin-bottom:14px}
.tab{flex:1;text-align:center;padding:8px 10px;font-size:13px;font-weight:600;color:#a894c0;border-radius:8px;cursor:pointer;user-select:none;transition:background .2s,color .2s}
.tab:hover{color:#d9c8f0}
.tab.active{background:linear-gradient(135deg,rgba(255,45,149,.25),rgba(176,38,255,.25));color:#fff;box-shadow:inset 0 0 0 1px rgba(0,229,255,.4)}
.tab-pane{display:none}
.tab-pane.active{display:block;position:absolute;inset:0;overflow-y:auto;scrollbar-width:thin;scrollbar-color:rgba(255,45,149,.4) transparent;padding-right:6px}
.tab-pane.active::-webkit-scrollbar{width:6px;height:6px;display:block}
.tab-pane.active::-webkit-scrollbar-track{background:transparent}
.tab-pane.active::-webkit-scrollbar-thumb{background:rgba(255,45,149,.4);border-radius:999px}
.tab-pane.active::-webkit-scrollbar-thumb:hover{background:rgba(255,45,149,.6)}
.poem-list{display:flex;flex-direction:column;gap:10px}
.story-list{display:flex;flex-direction:column;gap:8px}
.poem-card{position:relative;display:flex;align-items:center;gap:12px;background:linear-gradient(135deg,rgba(255,255,255,.05),rgba(255,255,255,.02));border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:11px 13px 11px 14px;cursor:pointer;transition:border-color .2s,background .2s,transform .15s,box-shadow .2s;overflow:hidden}
.poem-card::before{content:'';position:absolute;left:0;top:0;bottom:0;width:3px;background:linear-gradient(180deg,#ff2d95,#b026ff);opacity:.5;transition:opacity .2s}
.poem-card:hover:not(.disabled){border-color:rgba(0,229,255,.5);background:linear-gradient(135deg,rgba(0,229,255,.1),rgba(176,38,255,.06));transform:translateY(-1px);box-shadow:0 8px 22px rgba(255,45,149,.18)}
.poem-card:hover:not(.disabled)::before{opacity:1}
.poem-card.active{border-color:#00e5ff;background:linear-gradient(135deg,rgba(0,229,255,.16),rgba(176,38,255,.1));box-shadow:0 0 0 1px rgba(0,229,255,.3)}
.poem-card.active::before{opacity:1}
.poem-card.disabled{opacity:.4;cursor:not-allowed}
.poem-card .idx{flex:0 0 auto;width:26px;height:26px;border-radius:8px;background:linear-gradient(135deg,rgba(255,45,149,.25),rgba(176,38,255,.25));border:1px solid rgba(0,229,255,.25);color:#5eeaff;font-size:12px;font-weight:700;display:flex;align-items:center;justify-content:center;font-family:"SF Mono",Menlo,Consolas,monospace}
.poem-card .body{flex:1;min-width:0}
.poem-card .body .t{font-size:13px;font-weight:600;color:#e8dcff;margin-bottom:3px;line-height:1.35;letter-spacing:.2px}
.poem-card .body .a{font-size:11px;color:#9a86b8;display:flex;align-items:center;gap:5px}
.poem-card .body .a::before{content:'✍';font-size:10px;opacity:.7}
.poem-card .go{flex:0 0 auto;font-size:11px;color:#00e5ff;opacity:0;transition:opacity .2s;letter-spacing:.5px}
.poem-card:hover:not(.disabled) .go{opacity:.9}
.poem-card.active .go{opacity:.9}
.story-item{display:flex;align-items:center;gap:10px;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.08);border-radius:12px;padding:10px 12px;cursor:pointer;text-align:left;transition:border-color .2s,background .2s,transform .15s;color:#f0e6ff;font-family:inherit;font-size:13px;width:100%}
.story-item:hover:not(:disabled){border-color:rgba(0,229,255,.5);background:rgba(0,229,255,.08);transform:translateY(-1px)}
.story-item.active{border-color:#00e5ff;background:rgba(0,229,255,.14);box-shadow:0 0 0 1px rgba(0,229,255,.3)}
.story-item .ico{width:32px;height:32px;border-radius:9px;flex:0 0 auto;display:flex;align-items:center;justify-content:center;background:linear-gradient(135deg,rgba(255,45,149,.3),rgba(176,38,255,.3));font-size:16px}
.story-item .meta{flex:1;min-width:0}
.story-item .meta .t{font-weight:600;color:#e8dcff;margin-bottom:2px}
.story-item .meta .d{font-size:11px;color:#9a86b8;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.story-item:disabled{opacity:.4;cursor:not-allowed}
.stage{display:flex;flex-direction:column;align-items:center;gap:14px}
.stage-frame{width:100%;max-width:540px;height:min(960px,calc(100vh - 220px));aspect-ratio:540/960;border-radius:20px;overflow:hidden;position:relative;background:linear-gradient(180deg,#160a2e,#0a0418);border:1px solid rgba(255,255,255,.08);box-shadow:0 20px 60px rgba(0,0,0,.5);margin:0 auto}
.stage-frame #sdk{width:100%;height:100%;display:flex!important;align-items:center!important;justify-content:center!important}
.stage-frame #sdk canvas,.stage-frame #sdk video{position:static!important;top:auto!important;left:auto!important;transform:none!important;max-width:100%!important;max-height:100%!important;width:auto!important;height:auto!important;object-fit:contain!important}
.stage-placeholder{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:14px;color:#6a5a82;text-align:center;padding:20px;pointer-events:none}
.stage-placeholder svg{opacity:.5}
.stage-placeholder .tip{font-size:13px;line-height:1.6}
.stage-placeholder.hidden{display:none}
.progress-wrap{width:100%;max-width:540px;margin:0 auto;display:none}
.progress-wrap.show{display:block}
.progress-bar{height:6px;background:rgba(255,255,255,.08);border-radius:999px;overflow:hidden}
.progress-fill{height:100%;width:0%;background:linear-gradient(90deg,#ff2d95,#b026ff);transition:width .25s ease}
.progress-text{font-size:11px;color:#9a86b8;margin-top:6px;text-align:center}
.chat-panel{background:linear-gradient(180deg,rgba(255,255,255,.06),rgba(255,255,255,.02));border:1px solid rgba(255,255,255,.08);border-radius:16px;backdrop-filter:blur(12px);box-shadow:0 10px 40px rgba(0,0,0,.35);position:sticky;top:20px;display:flex;flex-direction:column;height:calc(100vh - 140px);max-height:calc(100vh - 140px)}
.chat-head{padding:14px 16px;border-bottom:1px solid rgba(255,255,255,.08);display:flex;align-items:center;justify-content:space-between;gap:8px}
.chat-head .title{font-size:13px;font-weight:600;color:#e8dcff;display:flex;align-items:center;gap:8px}
.chat-head .title::before{content:'';width:3px;height:14px;border-radius:2px;background:linear-gradient(180deg,#ff2d95,#b026ff)}
.chat-head .clear{background:rgba(255,255,255,.08);border:1px solid rgba(255,255,255,.12);color:#d9c8f0;font-size:11px;padding:4px 10px;border-radius:999px;cursor:pointer;flex:0 0 auto;transition:background .2s}
.chat-head .clear:hover{background:rgba(255,255,255,.16)}
.chat-body{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:10px}
.msg{max-width:86%;padding:10px 13px;border-radius:14px;font-size:13px;line-height:1.6;word-break:break-word;animation:fadeIn .25s ease}
@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.msg .role{font-size:10px;color:#9a86b8;margin-bottom:4px;letter-spacing:.5px;text-transform:uppercase}
.msg.user{align-self:flex-end;background:linear-gradient(135deg,rgba(255,45,149,.25),rgba(176,38,255,.25));border:1px solid rgba(0,229,255,.3);color:#f0e6ff;border-bottom-right-radius:4px}
.msg.bot{align-self:flex-start;background:rgba(255,255,255,.05);border:1px solid rgba(255,255,255,.08);color:#e8dcff;border-bottom-left-radius:4px}
.msg.sys{align-self:center;background:transparent;color:#7a6a8e;font-size:11px;padding:4px 10px;border:1px dashed rgba(255,255,255,.12);border-radius:999px;max-width:90%;text-align:center}
.chat-empty{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;color:#6a5a82;text-align:center;font-size:12px;line-height:1.7}
.chat-empty svg{opacity:.4}
.quick-row{padding:10px 16px 0;display:flex;flex-wrap:wrap;gap:6px}
.chip{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);color:#d9c8f0;font-size:12px;padding:6px 11px;border-radius:999px;cursor:pointer;transition:background .2s,border-color .2s;flex:0 0 auto;font-family:inherit}
.chip:hover:not(:disabled){background:rgba(0,229,255,.12);border-color:rgba(0,229,255,.4)}
.chip:disabled{opacity:.4;cursor:not-allowed}
.ctrl-row{padding:10px 16px 0;display:flex;flex-wrap:wrap;gap:6px}
.ctrl-btn{background:rgba(255,255,255,.06);border:1px solid rgba(255,255,255,.1);color:#d9c8f0;font-size:12px;padding:7px 12px;border-radius:10px;cursor:pointer;transition:background .2s,border-color .2s;flex:0 0 auto;font-family:inherit}
.ctrl-btn:hover:not(:disabled){background:rgba(0,229,255,.12);border-color:rgba(0,229,255,.4)}
.ctrl-btn:disabled{opacity:.4;cursor:not-allowed}
.composer{padding:14px 16px;border-top:1px solid rgba(255,255,255,.08);display:flex;gap:8px;align-items:flex-end}
.composer textarea{flex:1;background:rgba(20,10,40,.6);border:1px solid rgba(255,255,255,.1);border-radius:12px;padding:10px 12px;color:#f0e6ff;font-size:13px;outline:none;resize:none;max-height:120px;min-height:42px;line-height:1.5;font-family:inherit;transition:border-color .2s,box-shadow .2s}
.composer textarea:focus{border-color:#00e5ff;box-shadow:0 0 0 3px rgba(0,229,255,.15)}
.composer textarea:disabled{opacity:.5;cursor:not-allowed}
.composer .send{flex:0 0 auto;width:44px;height:42px;padding:0;border-radius:12px;background:linear-gradient(135deg,#ff2d95,#b026ff);box-shadow:0 6px 18px rgba(255,45,149,.35);display:flex;align-items:center;justify-content:center}
.composer .send svg{width:18px;height:18px;stroke:#fff}
@media (max-width:1180px){.layout{grid-template-columns:300px 1fr}.chat-panel{grid-column:1/-1;position:static;height:360px;max-height:360px}}
@media (max-width:760px){.layout{grid-template-columns:1fr}.panel{position:static}.stage-frame{height:70vh;max-height:70vh}}
</style>
</head>
<body>
<header class="topbar">
<div class="brand">
<div class="logo"><svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8"/></svg></div>
<div class="title"><h1>魔珐星云数字人</h1><div class="sub">实时驱动 LiteSDK · 互动故事 Demo</div></div>
</div>
<div class="status-row">
<span class="badge"><span class="dot" id="statusDot"></span><span id="statusText">未连接</span></span>
<span class="badge" id="voiceBadge" style="display:none">语音:—</span>
</div>
</header>
<div class="layout">
<aside class="panel">
<div class="panel-section">
<div class="section-title">连接配置</div>
<div class="field"><label for="appId">App ID</label><input id="appId" type="text" placeholder="your_appid" autocomplete="off" /></div>
<div class="field"><label for="appSecret">App Secret</label><input id="appSecret" type="password" placeholder="your_appsecret" autocomplete="off" /></div>
<div class="btn-row">
<button class="btn-primary" id="btnConnect">连接数字人</button>
<button class="btn-warn" id="btnClear" disabled>清除</button>
</div>
</div>
<div class="divider"></div>
<div class="panel-section list-section">
<div class="tabs">
<div class="tab" data-tab="story">故事点播</div>
<div class="tab active" data-tab="poem">学习诗词</div>
</div>
<div class="list-wrap">
<div class="tab-pane" id="pane-story">
<div class="story-list" id="storyList"></div>
</div>
<div class="tab-pane active" id="pane-poem">
<div class="poem-list" id="poemList"></div>
</div>
</div>
<div class="btn-row" style="margin-top:12px">
<button class="btn-ghost" id="btnStop" disabled>停止</button>
<button class="btn-ghost" id="btnIdle" disabled>待机</button>
</div>
</div>
</aside>
<main class="stage">
<div class="stage-frame">
<div id="sdk"></div>
<div class="stage-placeholder" id="placeholder">
<svg width="72" height="72" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.3"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4.4 3.6-8 8-8s8 3.6 8 8"/></svg>
<div class="tip">在左侧填入 App ID 与 App Secret<br/>点击「连接数字人」开始</div>
</div>
</div>
<div class="progress-wrap" id="progressWrap">
<div class="progress-bar"><div class="progress-fill" id="progressFill"></div></div>
<div class="progress-text" id="progressText">资源加载中… 0%</div>
</div>
</main>
<section class="chat-panel">
<div class="chat-head">
<span class="title">互动对话</span>
<button class="clear" id="btnChatClear">清空</button>
</div>
<div class="chat-body" id="chatBody">
<div class="chat-empty" id="chatEmpty">
<svg width="56" height="56" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.3"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
<div>连接数字人后,可在下方输入文字让它开口说话<br/>或点击「故事点播」与「快捷话术」直接朗读</div>
</div>
</div>
<div class="quick-row" id="quickRow"></div>
<div class="ctrl-row" id="ctrlRow"></div>
<div class="composer">
<textarea id="composerInput" placeholder="输入要让数字人说的话,回车发送,Shift+回车换行" disabled></textarea>
<button class="send" id="btnSend" disabled title="发送"><svg viewBox="0 0 24 24" fill="none" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M22 2L11 13"/><path d="M22 2l-7 20-4-9-9-4z"/></svg></button>
</div>
</section>
</div>
<script src="https://media.xingyun3d.com/xingyun3d/general/litesdk/xmovAvatar@latest.js"></script>
<script>
const STORIES = [
{ icon: '🌙', title: '小王子', desc: '关于驯养与离别', text: '小王子离开了他的星球,开始了一段漫长的旅行。他经过了六颗小行星,遇见了国王、爱慕虚荣的人、酒鬼、商人、点灯人和地理学家。最后他来到地球,在玫瑰园里看到了五千朵和他的玫瑰一模一样的花,他哭了。这时一只狐狸出现了,狐狸对他说:对我来说,你还只是一个小男孩,就像其他千万个小男孩一样。我不需要你,你也同样不需要我。但是,如果你驯养了我,我们就会彼此需要。对我而言,你将是举世无双的,对你而言,我也是独一无二的。小王子终于明白了,是他的玫瑰之所以独一无二,是因为他在她身上倾注了时间与爱。' },
{ icon: '🐢', title: '龟兔赛跑', desc: '骄兵必败', text: '从前有一只骄傲的兔子,总爱嘲笑乌龟爬得慢。有一天,乌龟实在受不了,便向兔子下战书,要和它赛跑。比赛开始了,兔子像离弦的箭一样冲了出去,转眼就把乌龟远远甩在身后。兔子心想:反正乌龟一时半会儿也追不上,不如先睡一觉。于是它躺在树下呼呼大睡起来。而乌龟呢,一步一个脚印,从不停歇,慢慢地爬呀爬呀。等兔子醒来,发现乌龟已经快到终点了,连忙飞奔追赶,可为时已晚,乌龟先一步冲过了终点。这个故事告诉我们:骄傲会让人失败,而坚持到底才能胜利。' },
{ icon: '🐺', title: '狼来了', desc: '诚实最可贵', text: '从前有个放羊的孩子,每天都要到山上去放羊。一天,他觉得无聊,就对着山下大喊:狼来了!狼来了!村民们听到喊声,急忙拿着锄头和木棒赶上山来,却发现根本没有狼,只是孩子在撒谎。孩子看到村民们慌张的样子,乐得哈哈大笑。过了几天,他又故伎重演,村民们又上山来,又被骗了。第三次,狼真的来了,孩子拼命喊:狼来了!狼来了!可这一次,再也没有人相信他,村民们都没有上山。结果他的羊被狼吃了个精光。这个故事告诉我们:经常撒谎的人,即使说了真话,也不会有人相信。' },
{ icon: '🦁', title: '狮子和老鼠', desc: '善有善报', text: '一只凶猛的狮子正在树下打盹,一只小老鼠不小心跑过它的爪子,把它吵醒了。狮子一把抓住小老鼠,张开大嘴就要吃掉它。小老鼠吓得瑟瑟发抖,哀求道:大王,求您放过我吧!如果有一天您需要帮助,我一定会报答您的。狮子听了哈哈大笑:你这么小,能帮我什么呢?不过它觉得好玩,就把老鼠放了。几天后,狮子不小心落入了猎人设的陷阱,被绳网紧紧缠住,怎么挣扎也挣不脱。小老鼠听到了狮子的吼声,连忙赶来,用它锋利的牙齿一根一根咬断了绳索。狮子终于得救了,它感激地说:谢谢你,小老鼠,我以前小看你了。这个故事告诉我们:不要小看任何人,小小的善意也会有回报。' },
{ icon: '🌸', title: '桃花源记', desc: '陶渊明', text: '晋太元中,武陵人捕鱼为业。缘溪行,忘路之远近。忽逢桃花林,夹岸数百步,中无杂树,芳草鲜美,落英缤纷。渔人甚异之,复前行,欲穷其林。林尽水源,便得一山,山有小口,仿佛若有光。便舍船,从口入。初极狭,才通人。复行数十步,豁然开朗。土地平旷,屋舍俨然,有良田美池桑竹之属。阡陌交通,鸡犬相闻。其中往来种作,男女衣着,悉如外人。黄发垂髫,并怡然自乐。见渔人,乃大惊,问所从来。具答之。便要还家,设酒杀鸡作食。村中闻有此人,咸来问讯。自云先世避秦时乱,率妻子邑人来此绝境,不复出焉,遂与外人间隔。问今是何世,乃不知有汉,无论魏晋。此人一一为具言所闻,皆叹惋。余人各复延至其家,皆出酒食。停数日,辞去。此中人语云:不足为外人道也。既出,得其船,便扶向路,处处志之。及郡下,诣太守,说如此。太守即遣人随其往,寻向所志,遂迷,不复得路。南阳刘子骥,高尚士也,闻之,欣然规往。未果,寻病终。后遂无问津者。' },
{ icon: '🏮', title: '孔融让梨', desc: '谦让美德', text: '从前有一个叫孔融的小男孩,他四岁的时候,家里买了一筐梨。长辈让孔融先挑一个梨吃,孔融看了看,挑了一个最小的梨。父亲觉得很奇怪,就问他:你为什么不挑大的,反而挑最小的呢?孔融回答说:我年纪小,应该吃小的,大的应该留给哥哥们吃。父亲听了非常高兴,又问:那你还有弟弟呢?孔融说:我是哥哥,应该把大的让给弟弟。家里人听了都很佩服他。孔融让梨的故事流传了一千多年,告诉我们从小就要懂得谦让,尊敬兄长,爱护弟妹,这是中华民族的传统美德。' }
];
const QUICK = [
'你好,欢迎来到星云数字人!',
'今天天气真不错,要不要聊聊天?',
'我来给你讲一个故事吧。',
'请问有什么我可以帮忙的吗?',
'让我们一起学习吧,知识改变命运!',
'生活就像一盒巧克力,你永远不知道下一颗是什么味道。'
];
const CTRL = [
{ label: '挥手', text: '大家好,很高兴见到你们!' },
{ label: '问候', text: '早上好,新的一天开始了,愿你充满活力!' },
{ label: '鼓励', text: '加油,你一定能做到的,相信自己!' },
{ label: '告别', text: '今天就到这里吧,我们下次再见,拜拜!' }
];
const POEMS = [];
POEMS.push({ title: '离骚·蜀道难·梦游天姥·春江花月夜', author: '屈原·李白·张若虚', text: '帝高阳之苗裔兮,朕皇考曰伯庸。摄提贞于孟陬兮,惟庚寅吾以降。皇览揆余初度兮,肇锡余以嘉名。名余曰正则兮,字余曰灵均。纷吾既有此内美兮,又重之以修能。扈江离与辟芷兮,纫秋兰以为佩。汩余若将不及兮,恐年岁之不吾与。朝搴阰之木兰兮,夕揽洲之宿莽。日月忽其不淹兮,春与秋其代序。惟草木之零落兮,恐美人之迟暮。不抚壮而弃秽兮,何不改乎此度?乘骐骥以驰骋兮,来吾道夫先路!昔三后之纯粹兮,固众芳之所在。杂申椒与菌桂兮,岂维纫夫蕙茝!彼尧舜之耿介兮,既遵道而得路。何桀纣之猖披兮,夫唯捷径以窘步。惟夫党人之偷乐兮,路幽昧以险隘。岂余身之惮殃兮,恐皇舆之败绩!忽奔走以先后兮,及前王之踵武。荃不察余之中情兮,反信谗而齗怒。余固知謇謇之为患兮,忍而不能舍也。指九天以为正兮,夫唯灵修之故也。曰黄昏以为期兮,羌中道而改路。初既与余成言兮,后悔遁而有他。余既不难夫离别兮,伤灵修之数化。噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。上有六龙回日之高标,下有冲波逆折之回川。黄鹤之飞尚不得过,猿猱欲度愁攀援。青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒倚绝壁。飞湍瀑流争喧豗,砯崖转石万壑雷。其险也如此,嗟尔远道之人胡为乎来哉!剑阁峥嵘而崔嵬,一夫当关,万夫莫开。所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。蜀道之难,难于上青天,侧身西望长咨嗟!海客谈瀛洲,烟涛微茫信难求;越人语天姥,云霞明灭或可睹。天姥连天向天横,势拔五岳掩赤城。天台四万八千丈,对此欲倒东南倾。我欲因之梦吴越,一夜飞度镜湖月。湖月照我影,送我至剡溪。谢公宿处今尚在,渌水荡漾清猿啼。脚著谢公屐,身登青云梯。半壁见海日,空中闻天鸡。千岩万转路不定,迷花倚石忽已暝。熊咆龙吟殷岩泉,栗深林兮惊层巅。云青青兮欲雨,水澹澹兮生烟。列缺霹雳,丘峦崩摧。洞天石扉,訇然中开。青冥浩荡不见底,日月照耀金银台。霓为衣兮风为马,云之君兮纷纷而来下。虎鼓瑟兮鸾回车,仙之人兮列如麻。忽魂悸以魄动,恍惊起而长嗟。惟觉时之枕席,失向来之烟霞。世间行乐亦如此,古来万事东流水。别君去兮何时还?且放白鹿青崖间,须行即骑访名山。安能摧眉折腰事权贵,使我不得开心颜!春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。江流宛转绕芳甸,月照花林皆似霰。空里流霜不觉飞,汀上白沙看不见。江天一色无纤尘,皎皎空中孤月轮。江畔何人初见月,江月何年初照人。人生代代无穷已,江月年年望相似。不知江月待何人,但见长江送流水。白云一片去悠悠,青枫浦上不胜愁。谁家今夜扁舟子,何处相思明月楼。可怜楼上月徘徊,应照离人妆镜台。玉户帘中卷不去,捣衣砧上拂还来。此时相望不相闻,愿逐月华流照君。鸿雁长飞光不度,鱼龙潜跃水成文。昨夜闲潭梦落花,可怜春半不还家。江水流春去欲尽,江潭落月复西斜。斜月沉沉藏海雾,碣石潇湘无限路。不知乘月几人归,落月摇情满江树。' });
POEMS.push({ title: '琵琶行·长恨歌', author: '白居易', text: '浔阳江头夜送客,枫叶荻花秋瑟瑟。主人下马客在船,举酒欲饮无管弦。醉不成欢惨将别,别时茫茫江浸月。忽闻水上琵琶声,主人忘归客不发。寻声暗问弹者谁,琵琶声停欲语迟。移船相近邀相见,添酒回灯重开宴。千呼万唤始出来,犹抱琵琶半遮面。转轴拨弦三两声,未成曲调先有情。弦弦掩抑声声思,似诉平生不得志。低眉信手续续弹,说尽心中无限事。轻拢慢捻抹复挑,初为霓裳后六幺。大弦嘈嘈如急雨,小弦切切如私语。嘈嘈切切错杂弹,大珠小珠落玉盘。间关莺语花底滑,幽咽泉流冰下难。冰泉冷涩弦凝绝,凝绝不通声暂歇。别有幽愁暗恨生,此时无声胜有声。银瓶乍破水浆迸,铁骑突出刀枪鸣。曲终收拨当心画,四弦一声如裂帛。东船西舫悄无言,唯见江心秋月白。沉吟放拨插弦中,整顿衣裳起敛容。自言本是京城女,家在虾蟆陵下住。十三学得琵琶成,名属教坊第一部。曲罢曾教善才服,妆成每被秋娘妒。五陵年少争缠头,一曲红绡不知数。钿头银篦击节碎,血色罗裙翻酒污。今年欢笑复明年,秋月春风等闲度。弟走从军阿姨死,暮去朝来颜色故。门前冷落鞍马稀,老大嫁作商人妇。商人重利轻别离,前月浮梁买茶去。去来江口守空船,绕船月明江水寒。夜深忽梦少年事,梦啼妆泪红阑干。我闻琵琶已叹息,又闻此语重唧唧。同是天涯沦落人,相逢何必曾相识。我从去年辞帝京,谪居卧病浔阳城。浔阳地僻无音乐,终岁不闻丝竹声。住近湓江地低湿,黄芦苦竹绕宅生。其间旦暮闻何物,杜鹃啼血猿哀鸣。春江花朝秋月夜,往往取酒还独倾。岂无山歌与村笛,呕哑嘲哳难为听。今夜闻君琵琶语,如听仙乐耳暂明。莫辞更坐弹一曲,为君翻作琵琶行。感我此言良久立,却坐促弦弦转急。凄凄不似向前声,满座重闻皆掩泣。座中泣下谁最多,江州司马青衫湿。汉皇重色思倾国,御宇多年求不得。杨家有女初长成,养在深闺人未识。天生丽质难自弃,一朝选在君王侧。回眸一笑百媚生,六宫粉黛无颜色。春寒赐浴华清池,温泉水滑洗凝脂。侍儿扶起娇无力,始是新承恩泽时。云鬓花颜金步摇,芙蓉帐暖度春宵。春宵苦短日高起,从此君王不早朝。承欢侍宴无闲暇,春从春游夜专夜。后宫佳丽三千人,三千宠爱在一身。金屋妆成娇侍夜,玉楼宴罢醉和春。姊妹弟兄皆列土,可怜光彩生门户。遂令天下父母心,不重生男重生女。骊宫高处入青云,仙乐风飘处处闻。缓歌慢舞凝丝竹,尽日君王看不足。渔阳鼙鼓动地来,惊破霓裳羽衣曲。九重城阙烟尘生,千乘万骑西南行。翠华摇摇行复止,西出都门百余里。六军不发无奈何,宛转蛾眉马前死。花钿委地无人收,翠翘金雀玉搔头。君王掩面救不得,回看血泪相和流。黄埃散漫风萧索,云栈萦纡登剑阁。峨嵋山下少人行,旌旗无光日色薄。蜀江水碧蜀山青,圣主朝朝暮暮情。行宫见月伤心色,夜雨闻铃肠断声。天旋地转回龙驭,到此踌躇不能去。马嵬坡下泥土中,不见玉颜空死处。君臣相顾尽沾衣,东望都门信马归。归来池苑皆依旧,太液芙蓉未央柳。芙蓉如面柳如眉,对此如何不泪垂。春风桃李花开日,秋雨梧桐叶落时。西宫南内多秋草,落叶满阶红不扫。梨园弟子白发新,椒房阿监青娥老。夕殿萤飞思悄然,孤灯挑尽未成眠。迟迟钟鼓初长夜,耿耿星河欲曙天。鸳鸯瓦冷霜华重,翡翠衾寒谁与共。悠悠生死别经年,魂魄不曾来入梦。临邛道士鸿都客,能以精诚致魂魄。为感君王辗转思,遂教方士殷勤觅。排空驭气奔如电,升天入地求之遍。上穷碧落下黄泉,两处茫茫皆不见。忽闻海上有仙山,山在虚无缥缈间。楼阁玲珑五云起,其中绰约多仙子。中有一人字太真,雪肤花貌参差是。金阙西厢叩玉扃,转教小玉报双成。闻道汉家天子使,九华帐里梦魂惊。揽衣推枕起徘徊,珠箔银屏迤逦开。云鬓半偏新睡觉,花冠不整下堂来。风吹仙袂飘飘举,犹似霓裳羽衣舞。玉容寂寞泪阑干,梨花一枝春带雨。含情凝睇谢君王,一别音容两渺茫。昭阳殿里恩爱绝,蓬莱宫中日月长。回头下望人寰处,不见长安见尘雾。惟将旧物表深情,钿合金钗寄将去。钗留一股合一扇,钗擘黄金合分钿。但教心似金钿坚,天上人间会相见。临别殷勤重寄词,词中有誓两心知。七月七日长生殿,夜半无人私语时。在天愿作比翼鸟,在地愿为连理枝。天长地久有时尽,此恨绵绵无绝期。' });
POEMS.push({ title: '前出师表·阿房宫赋·离骚', author: '诸葛亮·杜牧·屈原', text: '臣亮言:先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎。陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。今当远离,临表涕零,不知所言。六王毕,四海一,蜀山兀,阿房出。覆压三百余里,隔离天日。骊山北构而西折,直走咸阳。二川溶溶,流入宫墙。五步一楼,十步一阁;廊腰缦回,檐牙高啄;各抱地势,钩心斗角。盘盘焉,囷囷焉,蜂房水涡,矗不知其几千万落。长桥卧波,未云何龙?复道行空,不霁何虹?高低冥迷,不知西东。歌台暖响,春光融融;舞殿冷袖,风雨凄凄。一日之内,一宫之间,而气候不齐。妃嫔媵嫱,王子皇孙,辞楼下殿,辇来于秦。朝歌夜弦,为秦宫人。明星荧荧,开妆镜也;绿云扰扰,梳晓鬟也;渭流涨腻,弃脂水也;烟斜雾横,焚椒兰也。雷霆乍惊,宫车过也;辘辘远听,杳不知其所之也。一肌一容,尽态极妍,缦立远视,而望幸焉。有不见者,三十六年。燕赵之收藏,韩魏之经营,齐楚之精英,几世几年,剽掠其人,倚叠如山。一旦不能有,输来其间。鼎铛玉石,金块珠砾,弃掷逦迤,秦人视之,亦甚惜。嗟乎!一人之心,千万人之心也。秦爱纷奢,人亦念其家。奈何取之尽锱铢,用之如泥沙?使负栋之柱,多于南亩之农夫;架梁之椽,多于机上之工女;钉头磷磷,多于在庾之粟粒;瓦缝参差,多于周身之帛缕;直栏横槛,多于九土之城郭;管弦呕哑,多于市人之言语。可使有形者,皆可灭也。灭六国者六国也,非秦也。族秦者秦也,非天下也。嗟乎!使六国各爱其人,则足以拒秦;使秦复爱六国之人,则递三世可至万世而为君,谁得而族灭也?秦人不暇自哀,而后人哀之;后人哀之而不鉴之,亦使后人而复哀后人也。帝高阳之苗裔兮,朕皇考曰伯庸。摄提贞于孟陬兮,惟庚寅吾以降。皇览揆余初度兮,肇锡余以嘉名。名余曰正则兮,字余曰灵均。纷吾既有此内美兮,又重之以修能。扈江离与辟芷兮,纫秋兰以为佩。汩余若将不及兮,恐年岁之不吾与。朝搴阰之木兰兮,夕揽洲之宿莽。日月忽其不淹兮,春与秋其代序。惟草木之零落兮,恐美人之迟暮。不抚壮而弃秽兮,何不改乎此度?乘骐骥以驰骋兮,来吾道夫先路!' });
POEMS.push({ title: '长恨歌·蜀道难·春江花月夜', author: '白居易·李白·张若虚', text: '汉皇重色思倾国,御宇多年求不得。杨家有女初长成,养在深闺人未识。天生丽质难自弃,一朝选在君王侧。回眸一笑百媚生,六宫粉黛无颜色。春寒赐浴华清池,温泉水滑洗凝脂。侍儿扶起娇无力,始是新承恩泽时。云鬓花颜金步摇,芙蓉帐暖度春宵。春宵苦短日高起,从此君王不早朝。承欢侍宴无闲暇,春从春游夜专夜。后宫佳丽三千人,三千宠爱在一身。金屋妆成娇侍夜,玉楼宴罢醉和春。姊妹弟兄皆列土,可怜光彩生门户。遂令天下父母心,不重生男重生女。骊宫高处入青云,仙乐风飘处处闻。缓歌慢舞凝丝竹,尽日君王看不足。渔阳鼙鼓动地来,惊破霓裳羽衣曲。九重城阙烟尘生,千乘万骑西南行。翠华摇摇行复止,西出都门百余里。六军不发无奈何,宛转蛾眉马前死。花钿委地无人收,翠翘金雀玉搔头。君王掩面救不得,回看血泪相和流。黄埃散漫风萧索,云栈萦纡登剑阁。峨嵋山下少人行,旌旗无光日色薄。蜀江水碧蜀山青,圣主朝朝暮暮情。行宫见月伤心色,夜雨闻铃肠断声。天旋地转回龙驭,到此踌躇不能去。马嵬坡下泥土中,不见玉颜空死处。君臣相顾尽沾衣,东望都门信马归。归来池苑皆依旧,太液芙蓉未央柳。芙蓉如面柳如眉,对此如何不泪垂。春风桃李花开日,秋雨梧桐叶落时。西宫南内多秋草,落叶满阶红不扫。梨园弟子白发新,椒房阿监青娥老。夕殿萤飞思悄然,孤灯挑尽未成眠。迟迟钟鼓初长夜,耿耿星河欲曙天。鸳鸯瓦冷霜华重,翡翠衾寒谁与共。悠悠生死别经年,魂魄不曾来入梦。临邛道士鸿都客,能以精诚致魂魄。为感君王辗转思,遂教方士殷勤觅。排空驭气奔如电,升天入地求之遍。上穷碧落下黄泉,两处茫茫皆不见。忽闻海上有仙山,山在虚无缥缈间。楼阁玲珑五云起,其中绰约多仙子。中有一人字太真,雪肤花貌参差是。金阙西厢叩玉扃,转教小玉报双成。闻道汉家天子使,九华帐里梦魂惊。揽衣推枕起徘徊,珠箔银屏迤逦开。云鬓半偏新睡觉,花冠不整下堂来。风吹仙袂飘飘举,犹似霓裳羽衣舞。玉容寂寞泪阑干,梨花一枝春带雨。含情凝睇谢君王,一别音容两渺茫。昭阳殿里恩爱绝,蓬莱宫中日月长。回头下望人寰处,不见长安见尘雾。惟将旧物表深情,钿合金钗寄将去。钗留一股合一扇,钗擘黄金合分钿。但教心似金钿坚,天上人间会相见。临别殷勤重寄词,词中有誓两心知。七月七日长生殿,夜半无人私语时。在天愿作比翼鸟,在地愿为连理枝。天长地久有时尽,此恨绵绵无绝期。噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。上有六龙回日之高标,下有冲波逆折之回川。黄鹤之飞尚不得过,猿猱欲度愁攀援。青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒倚绝壁。飞湍瀑流争喧豗,砯崖转石万壑雷。其险也如此,嗟尔远道之人胡为乎来哉!剑阁峥嵘而崔嵬,一夫当关,万夫莫开。所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。蜀道之难,难于上青天,侧身西望长咨嗟!春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。江流宛转绕芳甸,月照花林皆似霰。空里流霜不觉飞,汀上白沙看不见。江天一色无纤尘,皎皎空中孤月轮。江畔何人初见月,江月何年初照人。人生代代无穷已,江月年年望相似。不知江月待何人,但见长江送流水。白云一片去悠悠,青枫浦上不胜愁。谁家今夜扁舟子,何处相思明月楼。可怜楼上月徘徊,应照离人妆镜台。玉户帘中卷不去,捣衣砧上拂还来。此时相望不相闻,愿逐月华流照君。鸿雁长飞光不度,鱼龙潜跃水成文。昨夜闲潭梦落花,可怜春半不还家。江水流春去欲尽,江潭落月复西斜。斜月沉沉藏海雾,碣石潇湘无限路。不知乘月几人归,落月摇情满江树。' });
POEMS.push({ title: '离骚·前出师表·春江花月夜', author: '屈原·诸葛亮·张若虚', text: '帝高阳之苗裔兮,朕皇考曰伯庸。摄提贞于孟陬兮,惟庚寅吾以降。皇览揆余初度兮,肇锡余以嘉名。名余曰正则兮,字余曰灵均。纷吾既有此内美兮,又重之以修能。扈江离与辟芷兮,纫秋兰以为佩。汩余若将不及兮,恐年岁之不吾与。朝搴阰之木兰兮,夕揽洲之宿莽。日月忽其不淹兮,春与秋其代序。惟草木之零落兮,恐美人之迟暮。不抚壮而弃秽兮,何不改乎此度?乘骐骥以驰骋兮,来吾道夫先路!昔三后之纯粹兮,固众芳之所在。杂申椒与菌桂兮,岂维纫夫蕙茝!彼尧舜之耿介兮,既遵道而得路。何桀纣之猖披兮,夫唯捷径以窘步。惟夫党人之偷乐兮,路幽昧以险隘。岂余身之惮殃兮,恐皇舆之败绩!忽奔走以先后兮,及前王之踵武。荃不察余之中情兮,反信谗而齗怒。余固知謇謇之为患兮,忍而不能舍也。指九天以为正兮,夫唯灵修之故也。曰黄昏以为期兮,羌中道而改路。初既与余成言兮,后悔遁而有他。余既不难夫离别兮,伤灵修之数化。臣亮言:先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎。陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。今当远离,临表涕零,不知所言。春江潮水连海平,海上明月共潮生。滟滟随波千万里,何处春江无月明。江流宛转绕芳甸,月照花林皆似霰。空里流霜不觉飞,汀上白沙看不见。江天一色无纤尘,皎皎空中孤月轮。江畔何人初见月,江月何年初照人。人生代代无穷已,江月年年望相似。不知江月待何人,但见长江送流水。白云一片去悠悠,青枫浦上不胜愁。谁家今夜扁舟子,何处相思明月楼。可怜楼上月徘徊,应照离人妆镜台。玉户帘中卷不去,捣衣砧上拂还来。此时相望不相闻,愿逐月华流照君。鸿雁长飞光不度,鱼龙潜跃水成文。昨夜闲潭梦落花,可怜春半不还家。江水流春去欲尽,江潭落月复西斜。斜月沉沉藏海雾,碣石潇湘无限路。不知乘月几人归,落月摇情满江树。' });
POEMS.push({ title: '蜀道难·梦游天姥吟留别·阿房宫赋', author: '李白·杜牧', text: '噫吁嚱,危乎高哉!蜀道之难,难于上青天!蚕丛及鱼凫,开国何茫然!尔来四万八千岁,不与秦塞通人烟。西当太白有鸟道,可以横绝峨眉巅。地崩山摧壮士死,然后天梯石栈相钩连。上有六龙回日之高标,下有冲波逆折之回川。黄鹤之飞尚不得过,猿猱欲度愁攀援。青泥何盘盘,百步九折萦岩峦。扪参历井仰胁息,以手抚膺坐长叹。问君西游何时还?畏途巉岩不可攀。但见悲鸟号古木,雄飞雌从绕林间。又闻子规啼夜月,愁空山。蜀道之难,难于上青天,使人听此凋朱颜!连峰去天不盈尺,枯松倒倚绝壁。飞湍瀑流争喧豗,砯崖转石万壑雷。其险也如此,嗟尔远道之人胡为乎来哉!剑阁峥嵘而崔嵬,一夫当关,万夫莫开。所守或匪亲,化为狼与豺。朝避猛虎,夕避长蛇;磨牙吮血,杀人如麻。锦城虽云乐,不如早还家。蜀道之难,难于上青天,侧身西望长咨嗟!海客谈瀛洲,烟涛微茫信难求;越人语天姥,云霞明灭或可睹。天姥连天向天横,势拔五岳掩赤城。天台四万八千丈,对此欲倒东南倾。我欲因之梦吴越,一夜飞度镜湖月。湖月照我影,送我至剡溪。谢公宿处今尚在,渌水荡漾清猿啼。脚著谢公屐,身登青云梯。半壁见海日,空中闻天鸡。千岩万转路不定,迷花倚石忽已暝。熊咆龙吟殷岩泉,栗深林兮惊层巅。云青青兮欲雨,水澹澹兮生烟。列缺霹雳,丘峦崩摧。洞天石扉,訇然中开。青冥浩荡不见底,日月照耀金银台。霓为衣兮风为马,云之君兮纷纷而来下。虎鼓瑟兮鸾回车,仙之人兮列如麻。忽魂悸以魄动,恍惊起而长嗟。惟觉时之枕席,失向来之烟霞。世间行乐亦如此,古来万事东流水。别君去兮何时还?且放白鹿青崖间,须行即骑访名山。安能摧眉折腰事权贵,使我不得开心颜!六王毕,四海一,蜀山兀,阿房出。覆压三百余里,隔离天日。骊山北构而西折,直走咸阳。二川溶溶,流入宫墙。五步一楼,十步一阁;廊腰缦回,檐牙高啄;各抱地势,钩心斗角。盘盘焉,囷囷焉,蜂房水涡,矗不知其几千万落。长桥卧波,未云何龙?复道行空,不霁何虹?高低冥迷,不知西东。歌台暖响,春光融融;舞殿冷袖,风雨凄凄。一日之内,一宫之间,而气候不齐。妃嫔媵嫱,王子皇孙,辞楼下殿,辇来于秦。朝歌夜弦,为秦宫人。明星荧荧,开妆镜也;绿云扰扰,梳晓鬟也;渭流涨腻,弃脂水也;烟斜雾横,焚椒兰也。雷霆乍惊,宫车过也;辘辘远听,杳不知其所之也。一肌一容,尽态极妍,缦立远视,而望幸焉。有不见者,三十六年。燕赵之收藏,韩魏之经营,齐楚之精英,几世几年,剽掠其人,倚叠如山。一旦不能有,输来其间。鼎铛玉石,金块珠砾,弃掷逦迤,秦人视之,亦甚惜。嗟乎!一人之心,千万人之心也。秦爱纷奢,人亦念其家。奈何取之尽锱铢,用之如泥沙?使负栋之柱,多于南亩之农夫;架梁之椽,多于机上之工女;钉头磷磷,多于在庾之粟粒;瓦缝参差,多于周身之帛缕;直栏横槛,多于九土之城郭;管弦呕哑,多于市人之言语。可使有形者,皆可灭也。灭六国者六国也,非秦也。族秦者秦也,非天下也。嗟乎!使六国各爱其人,则足以拒秦;使秦复爱六国之人,则递三世可至万世而为君,谁得而族灭也?秦人不暇自哀,而后人哀之;后人哀之而不鉴之,亦使后人而复哀后人也。' });
POEMS.push({ title: '长恨歌·阿房宫赋', author: '白居易·杜牧', text: '汉皇重色思倾国,御宇多年求不得。杨家有女初长成,养在深闺人未识。天生丽质难自弃,一朝选在君王侧。回眸一笑百媚生,六宫粉黛无颜色。春寒赐浴华清池,温泉水滑洗凝脂。侍儿扶起娇无力,始是新承恩泽时。云鬓花颜金步摇,芙蓉帐暖度春宵。春宵苦短日高起,从此君王不早朝。承欢侍宴无闲暇,春从春游夜专夜。后宫佳丽三千人,三千宠爱在一身。金屋妆成娇侍夜,玉楼宴罢醉和春。姊妹弟兄皆列土,可怜光彩生门户。遂令天下父母心,不重生男重生女。骊宫高处入青云,仙乐风飘处处闻。缓歌慢舞凝丝竹,尽日君王看不足。渔阳鼙鼓动地来,惊破霓裳羽衣曲。九重城阙烟尘生,千乘万骑西南行。翠华摇摇行复止,西出都门百余里。六军不发无奈何,宛转蛾眉马前死。花钿委地无人收,翠翘金雀玉搔头。君王掩面救不得,回看血泪相和流。黄埃散漫风萧索,云栈萦纡登剑阁。峨嵋山下少人行,旌旗无光日色薄。蜀江水碧蜀山青,圣主朝朝暮暮情。行宫见月伤心色,夜雨闻铃肠断声。天旋地转回龙驭,到此踌躇不能去。马嵬坡下泥土中,不见玉颜空死处。君臣相顾尽沾衣,东望都门信马归。归来池苑皆依旧,太液芙蓉未央柳。芙蓉如面柳如眉,对此如何不泪垂。春风桃李花开日,秋雨梧桐叶落时。西宫南内多秋草,落叶满阶红不扫。梨园弟子白发新,椒房阿监青娥老。夕殿萤飞思悄然,孤灯挑尽未成眠。迟迟钟鼓初长夜,耿耿星河欲曙天。鸳鸯瓦冷霜华重,翡翠衾寒谁与共。悠悠生死别经年,魂魄不曾来入梦。临邛道士鸿都客,能以精诚致魂魄。为感君王辗转思,遂教方士殷勤觅。排空驭气奔如电,升天入地求之遍。上穷碧落下黄泉,两处茫茫皆不见。忽闻海上有仙山,山在虚无缥缈间。楼阁玲珑五云起,其中绰约多仙子。中有一人字太真,雪肤花貌参差是。金阙西厢叩玉扃,转教小玉报双成。闻道汉家天子使,九华帐里梦魂惊。揽衣推枕起徘徊,珠箔银屏迤逦开。云鬓半偏新睡觉,花冠不整下堂来。风吹仙袂飘飘举,犹似霓裳羽衣舞。玉容寂寞泪阑干,梨花一枝春带雨。含情凝睇谢君王,一别音容两渺茫。昭阳殿里恩爱绝,蓬莱宫中日月长。回头下望人寰处,不见长安见尘雾。惟将旧物表深情,钿合金钗寄将去。钗留一股合一扇,钗擘黄金合分钿。但教心似金钿坚,天上人间会相见。临别殷勤重寄词,词中有誓两心知。七月七日长生殿,夜半无人私语时。在天愿作比翼鸟,在地愿为连理枝。天长地久有时尽,此恨绵绵无绝期。六王毕,四海一,蜀山兀,阿房出。覆压三百余里,隔离天日。骊山北构而西折,直走咸阳。二川溶溶,流入宫墙。五步一楼,十步一阁;廊腰缦回,檐牙高啄;各抱地势,钩心斗角。盘盘焉,囷囷焉,蜂房水涡,矗不知其几千万落。长桥卧波,未云何龙?复道行空,不霁何虹?高低冥迷,不知西东。歌台暖响,春光融融;舞殿冷袖,风雨凄凄。一日之内,一宫之间,而气候不齐。妃嫔媵嫱,王子皇孙,辞楼下殿,辇来于秦。朝歌夜弦,为秦宫人。明星荧荧,开妆镜也;绿云扰扰,梳晓鬟也;渭流涨腻,弃脂水也;烟斜雾横,焚椒兰也。雷霆乍惊,宫车过也;辘辘远听,杳不知其所之也。一肌一容,尽态极妍,缦立远视,而望幸焉。有不见者,三十六年。燕赵之收藏,韩魏之经营,齐楚之精英,几世几年,剽掠其人,倚叠如山。一旦不能有,输来其间。鼎铛玉石,金块珠砾,弃掷逦迤,秦人视之,亦甚惜。嗟乎!一人之心,千万人之心也。秦爱纷奢,人亦念其家。奈何取之尽锱铢,用之如泥沙?使负栋之柱,多于南亩之农夫;架梁之椽,多于机上之工女;钉头磷磷,多于在庾之粟粒;瓦缝参差,多于周身之帛缕;直栏横槛,多于九土之城郭;管弦呕哑,多于市人之言语。可使有形者,皆可灭也。灭六国者六国也,非秦也。族秦者秦也,非天下也。嗟乎!使六国各爱其人,则足以拒秦;使秦复爱六国之人,则递三世可至万世而为君,谁得而族灭也?秦人不暇自哀,而后人哀之;后人哀之而不鉴之,亦使后人而复哀后人也。' });
POEMS.push({ title: '琵琶行·前出师表·梦游天姥吟留别', author: '白居易·诸葛亮·李白', text: '浔阳江头夜送客,枫叶荻花秋瑟瑟。主人下马客在船,举酒欲饮无管弦。醉不成欢惨将别,别时茫茫江浸月。忽闻水上琵琶声,主人忘归客不发。寻声暗问弹者谁,琵琶声停欲语迟。移船相近邀相见,添酒回灯重开宴。千呼万唤始出来,犹抱琵琶半遮面。转轴拨弦三两声,未成曲调先有情。弦弦掩抑声声思,似诉平生不得志。低眉信手续续弹,说尽心中无限事。轻拢慢捻抹复挑,初为霓裳后六幺。大弦嘈嘈如急雨,小弦切切如私语。嘈嘈切切错杂弹,大珠小珠落玉盘。间关莺语花底滑,幽咽泉流冰下难。冰泉冷涩弦凝绝,凝绝不通声暂歇。别有幽愁暗恨生,此时无声胜有声。银瓶乍破水浆迸,铁骑突出刀枪鸣。曲终收拨当心画,四弦一声如裂帛。东船西舫悄无言,唯见江心秋月白。沉吟放拨插弦中,整顿衣裳起敛容。自言本是京城女,家在虾蟆陵下住。十三学得琵琶成,名属教坊第一部。曲罢曾教善才服,妆成每被秋娘妒。五陵年少争缠头,一曲红绡不知数。钿头银篦击节碎,血色罗裙翻酒污。今年欢笑复明年,秋月春风等闲度。弟走从军阿姨死,暮去朝来颜色故。门前冷落鞍马稀,老大嫁作商人妇。商人重利轻别离,前月浮梁买茶去。去来江口守空船,绕船月明江水寒。夜深忽梦少年事,梦啼妆泪红阑干。我闻琵琶已叹息,又闻此语重唧唧。同是天涯沦落人,相逢何必曾相识。我从去年辞帝京,谪居卧病浔阳城。浔阳地僻无音乐,终岁不闻丝竹声。住近湓江地低湿,黄芦苦竹绕宅生。其间旦暮闻何物,杜鹃啼血猿哀鸣。春江花朝秋月夜,往往取酒还独倾。岂无山歌与村笛,呕哑嘲哳难为听。今夜闻君琵琶语,如听仙乐耳暂明。莫辞更坐弹一曲,为君翻作琵琶行。感我此言良久立,却坐促弦弦转急。凄凄不似向前声,满座重闻皆掩泣。座中泣下谁最多,江州司马青衫湿。臣亮言:先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必能裨补阙漏,有所广益。将军向宠,性行淑均,晓畅军事,试用于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐托付不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎。陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。今当远离,临表涕零,不知所言。海客谈瀛洲,烟涛微茫信难求;越人语天姥,云霞明灭或可睹。天姥连天向天横,势拔五岳掩赤城。天台四万八千丈,对此欲倒东南倾。我欲因之梦吴越,一夜飞度镜湖月。湖月照我影,送我至剡溪。谢公宿处今尚在,渌水荡漾清猿啼。脚著谢公屐,身登青云梯。半壁见海日,空中闻天鸡。千岩万转路不定,迷花倚石忽已暝。熊咆龙吟殷岩泉,栗深林兮惊层巅。云青青兮欲雨,水澹澹兮生烟。列缺霹雳,丘峦崩摧。洞天石扉,訇然中开。青冥浩荡不见底,日月照耀金银台。霓为衣兮风为马,云之君兮纷纷而来下。虎鼓瑟兮鸾回车,仙之人兮列如麻。忽魂悸以魄动,恍惊起而长嗟。惟觉时之枕席,失向来之烟霞。世间行乐亦如此,古来万事东流水。别君去兮何时还?且放白鹿青崖间,须行即骑访名山。安能摧眉折腰事权贵,使我不得开心颜!' });
const $ = (id) => document.getElementById(id);
const appIdInput = $('appId'), appSecretInput = $('appSecret');
const btnConnect = $('btnConnect'), btnClear = $('btnClear');
const btnStop = $('btnStop'), btnIdle = $('btnIdle');
const statusDot = $('statusDot'), statusText = $('statusText'), voiceBadge = $('voiceBadge');
const placeholder = $('placeholder');
const progressWrap = $('progressWrap'), progressFill = $('progressFill'), progressText = $('progressText');
const storyList = $('storyList'), chatBody = $('chatBody'), chatEmpty = $('chatEmpty');
const quickRow = $('quickRow'), ctrlRow = $('ctrlRow');
const composerInput = $('composerInput'), btnSend = $('btnSend');
let sdk = null, connected = false, speaking = false, selectedStoryIdx = 0;
let pendingText = null;
function sleep(ms) { return new Promise((r) => setTimeout(r, ms)); }
function setVoice(s) { voiceBadge.style.display = 'inline-flex'; voiceBadge.textContent = '语音:' + s; }
function setStatus(text, mode) { statusText.textContent = text; statusDot.className = 'dot' + (mode ? ' ' + mode : ''); }
function setControlsEnabled(en) { btnStop.disabled = !en; btnIdle.disabled = !en; composerInput.disabled = !en; btnSend.disabled = !en; document.querySelectorAll('.story-item,.chip,.ctrl-btn').forEach((b) => b.disabled = !en); }
function addMsg(role, text) {
if (chatEmpty) { chatEmpty.style.display = 'none'; }
const m = document.createElement('div');
m.className = 'msg ' + role;
const roleLabel = role === 'user' ? '我' : (role === 'bot' ? '数字人' : '系统');
m.innerHTML = '<div class="role">' + roleLabel + '</div>' + String(text).replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>').replace(/\n/g,'<br>');
chatBody.appendChild(m);
chatBody.scrollTop = chatBody.scrollHeight;
return m;
}
function renderStories() {
storyList.innerHTML = '';
STORIES.forEach((s, i) => {
const b = document.createElement('button');
b.className = 'story-item' + (i === selectedStoryIdx ? ' active' : '');
b.disabled = !connected;
b.innerHTML = '<div class="ico">' + s.icon + '</div><div class="meta"><div class="t">' + s.title + '</div><div class="d">' + s.desc + '</div></div>';
b.addEventListener('click', () => { selectedStoryIdx = i; renderStories(); speakText(STORIES[i].text, '故事:《' + s.title + '》'); });
storyList.appendChild(b);
});
}
function renderQuick() {
quickRow.innerHTML = '';
QUICK.forEach((q) => {
const c = document.createElement('button');
c.className = 'chip'; c.textContent = q; c.disabled = !connected;
c.addEventListener('click', () => speakText(q, q));
quickRow.appendChild(c);
});
}
function renderCtrl() {
ctrlRow.innerHTML = '';
CTRL.forEach((c) => {
const b = document.createElement('button');
b.className = 'ctrl-btn'; b.textContent = c.label; b.disabled = !connected;
b.addEventListener('click', () => speakText(c.text, c.label));
ctrlRow.appendChild(b);
});
}
let selectedPoemIdx = 0;
function renderPoems() {
const poemList = $('poemList');
if (!poemList) return;
poemList.innerHTML = '';
POEMS.forEach((p, i) => {
const card = document.createElement('div');
card.className = 'poem-card' + (i === selectedPoemIdx ? ' active' : '') + (connected ? '' : ' disabled');
card.innerHTML = '<div class="idx">' + String(i + 1).padStart(2, '0') + '</div>'
+ '<div class="body"><div class="t">' + p.title + '</div><div class="a">' + p.author + '</div></div>'
+ '<div class="go">朗读 ▸</div>';
if (connected) {
card.addEventListener('click', () => { selectedPoemIdx = i; renderPoems(); speakText(POEMS[i].text, '诗词:《' + p.title + '》'); });
}
poemList.appendChild(card);
});
}
function speakText(text, label) {
if (!sdk || !connected) { alert('请先连接数字人'); return; }
if (!text) return;
addMsg('user', label || text);
try {
if (speaking) {
pendingText = text;
try { sdk.interactiveidle(); } catch (e) {}
addMsg('sys', '已排队,将在当前朗读结束后切换');
} else {
sdk.speak(text, true, true);
speaking = true;
setStatus('朗读中', 'busy');
}
} catch (err) {
addMsg('sys', '朗读失败:' + (err && err.message ? err.message : err));
}
}
function teardownSdk(msg) {
if (sdk) { try { sdk.destroy(); } catch (e) {} sdk = null; }
connected = false; speaking = false; pendingText = null;
placeholder.classList.remove('hidden');
progressWrap.classList.remove('show');
progressFill.style.width = '0%';
progressText.textContent = '资源加载中… 0%';
setStatus('未连接', '');
voiceBadge.style.display = 'none';
setControlsEnabled(false);
btnClear.disabled = true;
btnConnect.disabled = false;
btnConnect.textContent = '连接数字人';
renderStories(); renderQuick(); renderCtrl(); renderPoems();
if (msg) addMsg('sys', msg);
}
function teardownAfterSpeech() {
setTimeout(() => teardownSdk('朗读完成,已断开连接,请重新点击「连接数字人」'), 100);
}
function createSdk(appId, appSecret) {
return new Promise((resolve, reject) => {
if (typeof XmovAvatar === 'undefined') { reject(new Error('SDK 脚本未加载')); return; }
if (sdk) { try { sdk.destroy(); } catch (e) {} sdk = null; }
connected = false;
let initErr = null;
const setInitErr = (e) => { if (!initErr) initErr = e; };
try {
sdk = new XmovAvatar({
containerId: '#sdk',
appId: appId,
appSecret: appSecret,
gatewayServer: 'https://nebula-agent.xingyun3d.com/user/v1/ttsa/session',
headers: { 'Authorization': '888jn' },
hardwareAcceleration: 'prefer-hardware',
enableLogger: false,
onError(e) { setInitErr(e); },
onMessage(m) {
const s = (typeof m === 'string' ? m : JSON.stringify(m)).toLowerCase();
if (/(error|fail|unauthorized|forbidden|invalid|拒绝|失败|错误|鉴权|认证)/.test(s)) setInitErr(m);
},
onStateChange(s) {
const st = (typeof s === 'string' ? s : JSON.stringify(s)).toLowerCase();
if (/(error|fail|disconnect|拒绝|失败|错误)/.test(st)) setInitErr(s);
},
onStatusChange(s) {
const st = (typeof s === 'string' ? s : JSON.stringify(s)).toLowerCase();
if (/(error|fail|disconnect|拒绝|失败|错误)/.test(st)) setInitErr(s);
},
onVoiceStateChange(status) {
setVoice(status);
if (status === 'voice_start' || status === 'start') {
speaking = true; setStatus('朗读中', 'busy');
} else if (status === 'voice_end' || status === 'end') {
speaking = false;
if (pendingText) {
const t = pendingText; pendingText = null;
try { sdk.speak(t, true, true); speaking = true; return; } catch (e) {}
}
teardownAfterSpeech();
}
}
});
} catch (e) { sdk = null; reject(e); return; }
sdk.init({
onDownloadProgress: (p) => {
const pct = Math.round(p);
progressFill.style.width = pct + '%';
progressText.textContent = '资源加载中… ' + pct + '%';
}
}).then(async () => {
const sdkEl = document.getElementById('sdk');
const start = Date.now();
let rendered = false;
while (Date.now() - start < 8000) {
if (initErr) break;
const cv = sdkEl && sdkEl.querySelector('canvas');
const vid = sdkEl && sdkEl.querySelector('video');
const el = cv || vid;
if (el) { const r = el.getBoundingClientRect(); if (r.width > 50 && r.height > 50) { rendered = true; break; } }
await sleep(400);
}
if (initErr) {
try { if (sdk) sdk.destroy(); } catch (e) {} sdk = null; connected = false;
const msg = typeof initErr === 'string' ? initErr : (initErr && initErr.message ? initErr.message : JSON.stringify(initErr));
reject(new Error('数字人初始化失败:' + msg)); return;
}
if (!rendered) {
try { if (sdk) sdk.destroy(); } catch (e) {} sdk = null; connected = false;
reject(new Error('未检测到数字人画面,请检查 App ID / App Secret 是否正确')); return;
}
connected = true;
placeholder.classList.add('hidden');
progressWrap.classList.remove('show');
resolve(sdk);
}).catch((err) => {
try { if (sdk) sdk.destroy(); } catch (e) {} sdk = null; reject(err);
});
});
}
btnConnect.addEventListener('click', async () => {
const appId = appIdInput.value.trim();
const appSecret = appSecretInput.value.trim();
if (!appId || !appSecret) { alert('请填写 App ID 和 App Secret'); return; }
btnConnect.disabled = true; btnConnect.textContent = '连接中…';
setStatus('正在连接…', 'busy');
placeholder.classList.remove('hidden');
progressWrap.classList.add('show');
progressFill.style.width = '0%';
progressText.textContent = '资源加载中… 0%';
try {
await createSdk(appId, appSecret);
setStatus('已连接', 'connected');
setControlsEnabled(true);
btnClear.disabled = false;
btnConnect.textContent = '已连接';
addMsg('sys', '数字人已连接,可以开始互动了');
renderStories(); renderQuick(); renderCtrl(); renderPoems();
setControlsEnabled(true);
} catch (err) {
setStatus('连接失败', 'error');
btnConnect.disabled = false;
btnConnect.textContent = '连接数字人';
progressWrap.classList.remove('show');
addMsg('sys', '连接失败:' + (err && err.message ? err.message : err));
alert('连接失败:' + (err && err.message ? err.message : err));
}
});
btnStop.addEventListener('click', () => {
if (!sdk) return;
try { pendingText = null; sdk.interactiveidle(); } catch (e) {}
teardownSdk('已停止并断开连接,请重新点击「连接数字人」');
});
btnIdle.addEventListener('click', () => {
if (!sdk) return;
try { sdk.idle(); addMsg('sys', '已切换到待机状态'); } catch (e) {}
});
btnClear.addEventListener('click', () => {
teardownSdk('已清除实例并清空输入');
appIdInput.value = ''; appSecretInput.value = '';
});
btnSend.addEventListener('click', () => {
const text = composerInput.value.trim();
if (!text) return;
if (!sdk || !connected) { alert('请先连接数字人'); return; }
speakText(text, text);
composerInput.value = '';
});
composerInput.addEventListener('keydown', (e) => {
if (e.key === 'Enter' && !e.shiftKey) {
e.preventDefault();
btnSend.click();
}
});
$('btnChatClear').addEventListener('click', () => {
chatBody.innerHTML = '';
chatBody.appendChild(chatEmpty);
chatEmpty.style.display = '';
});
renderStories(); renderQuick(); renderCtrl(); renderPoems();
setControlsEnabled(false);
addMsg('sys', '页面已加载,请填入 App ID 与 App Secret 后点击连接');
document.querySelectorAll('.tab').forEach((t) => {
t.addEventListener('click', () => {
document.querySelectorAll('.tab').forEach((x) => x.classList.remove('active'));
document.querySelectorAll('.tab-pane').forEach((x) => x.classList.remove('active'));
t.classList.add('active');
$('pane-' + t.dataset.tab).classList.add('active');
});
});
</script>
</body>
</html>
成本对比有多明显?
| 对比项 | 传统自研方案 | 魔珐星云方案 |
|---|---|---|
| GPU成本 | ≥ ¥10,000(RTX 3090+) | ¥0(百元级芯片可用) |
| 开发团队 | 需图形学/3D引擎专家 | 普通前端工程师 |
| 开发周期 | 3~6个月 | 1天 |
| 交互延迟 | 3~5秒 | <1.5秒 |
| 免费试用 | 无 | 新用户注册送额度 |
如果没有魔珐星云,光是一个3D数字人的渲染引擎、口型同步算法、动作生成系统,就足够一个专业团队忙活半年。
六、适用场景:谁在用?用在哪儿?
魔珐星云的数字人能力已经广泛应用于多个行业:
| 行业 | 应用场景 |
|---|---|
| 零售/电商 | AI导购、品牌数字人客服、7×24小时服务 |
| 展厅/展馆 | 数字讲解员、互动导览、产品介绍 |
| 政务服务 | 政务导办、政策解答、自助服务终端 |
| 汽车 | 车载座舱助手、智能副驾 |
| 教育 | 虚拟讲师、AI助教、语言学习陪练 |
| 医疗 | 医药销售陪练、健康咨询导引 |
| 文旅 | 景区讲解、文化数字人 |
无论你是个人开发者、创业团队还是大型企业,都能用得上。
三、不只是固定内容:接入大模型,秒变智能客服
让数字人朗读固定的故事和诗词,只是一个起点。当它接入大模型后,就从一个“复读机”进化成了真正能实时交互、理解意图、有温度回应的智能体。
为什么智能客服需要一个“身体”?
传统智能客服大多停留在文字对话框,虽然能回答问题,但用户始终觉得冷冰冰、机械感强。魔珐星云通过LAM大模型,能够将文本或语音输入实时转换为语音、表情、眼神、手势和身体动作,给AI装上了一副“身体”。
试想两个场景:
- 传统方案:用户问“这款冰箱省电吗?”,屏幕弹出几行文字“该冰箱为一级能效,日均耗电0.8度……”
- 数字人方案:一个3D数字人微笑点头,配合手势说:“这款冰箱是一级能效,一天不到一度电,您一个月电费大概省下一杯奶茶钱。”
后者不仅传达了信息,还传递了情绪和亲切感。有数据显示,具身智能客服能将客户满意度提升至92分。
接入大模型有多简单?
- 选一个大模型——DeepSeek、豆包、Kimi,或者本地部署的Ollama,都支持
- 配置API密钥——在魔珐星云控制台填入大模型的API Key,平台原生支持OpenAI标准协议
- 开启对话——用户输入文字或语音,大模型流式输出回复,数字人实时生成语音、表情和动作
端到端响应可低至220-510ms,跟真人对话几乎没差别。
真实案例:已经有人在用了
| 场景 | 案例 |
|---|---|
| 智能客服 | 方太将传统App客服升级为具身智能数字人客服,7×24小时服务 |
| 电商导购 | 某头部电商引入数字人客服,降低40%人力成本,满意度提升至92分 |
| 政务导办 | 接入DeepSeek大模型的数字人导办,指引窗口位置、解答政策问题 |
| 门店导购 | 数字人边听顾客需求边回答,像真人导购一样自然交流 |
| 智慧教育 | 四川开放大学引入AI数字教师,提供有温度的启发式解题引导 |
把你的大模型和3D数字人结合起来,本质上就是给AI一个看得见、听得见、能互动的“身体” 。你的项目从此不再只是一个“文字回复工具”,而是一个有温度、有表情、能共情的智能体。
体验有礼:完成Demo即参与抽奖
为了感谢你花时间读完这篇文章并动手实践,我们准备了实体奖品福利:
只要你完成以下两步,即可参与抽奖:
- 跑通Demo:可以用我的代码,也可以自己DIY,成功运行,并且掉用SDK两次,每次五分钟以上
- 填写体验问卷:注册后右上角的昵称和微信号进行提交
奖品池(中奖后自选):
| 奖品类型 | 具体内容 |
|---|---|
| 📚 技术图书 | 前沿AI/编程/产品类正版纸质书(多本可选,包邮到家) |
| 🧸 大厂周边 | 互联网大厂限定周边礼盒(T恤/公仔/保温杯/笔记本等随机盲盒) |
参与方式:
👉 点击填写问卷
仅限前100名
总结
通过这个Demo,我想传递的核心观点很简单:
- 3D数字人不再是“大厂专属” ——魔珐星云把门槛降到了普通开发者都能轻松接入的程度
- 硬件友好 ——百元级芯片就能跑,数字人可以进入更多场景
- 文本驱动一切 ——不需要3D建模师、动画师,一段文本就能让数字人“活”起来
- 免费体验 ——新用户有免费额度,完全可以先跑通再决定是否商用
如果你的项目还在用文字框跟用户对话,不妨花一个下午试试魔珐星云——给AI装上身体,体验真的不一样。
更多推荐




所有评论(0)