我用 Trea + DeepSeek + 星云 SDK,0开发给国产大模型装上了一副会共情的“身体“
一、大模型已经很强了,但它还困在对话框里
过去一年,国产大模型的发展速度超出了很多人的预期。DeepSeek、Qwen 等模型在推理、编码、多轮对话上的表现,已经可以和海外主流模型正面竞争。我自己在日常开发中,已经习惯把很多问题直接丢给 DeepSeek,得到的回复质量常常让我感到意外。
但用着用着,一个问题越来越清晰:这些模型再强,它们跟用户的交互方式,仍然是单调的文本框**,**缺少面对面沟通的氛围感。
你打字,它回字。你有情绪,它用文字安慰你。你说"我今天好累",它回"辛苦了,记得休息"——逻辑没毛病,内容也到位。但这个体验,说实话,跟我对着搜索引擎打字没什么本质区别。
这在一些场景里是可以接受的。但在另一些场景里——比如情绪陪伴、心理咨询、教育辅导、门店接待——纯文字的交互方式,就像让一个世界顶级的心理咨询师只通过短信跟来访者沟通。话术逻辑完整,但无法传递情绪温度。

大模型补齐了 AI 认知大脑,但想要落地全终端真实场景,必须配套完整具身交互智能体系**——需要被看见、能说话、会表达、能实时回应。**
这就是我最近做的一个实验想探索的问题。
二、一个实验:给 DeepSeek 装上身体
我的想法很简单:把 DeepSeek 的对话能力,和魔珐星云具身交互智能 SDK 接在一起,看看会发生什么。
具体来说,我搭建了一个全天候 AI 情感陪伴导师的 Demo——一个 3D 数字人形象,由 DeepSeek 驱动对话,能根据用户输入的情绪状态,用合适的语气和节奏做出回应。

技术选型是这样的:
| 能力层 | 方案 | 说明 |
|---|---|---|
| Trea AI 开发工具 | Skill技能 + 项目描述 |
实现0代码生成整套逻辑 |
| 对话理解与生成 | DeepSeek Chat API | 负责理解用户情绪、生成专业疏导回复 |
| 上下文记忆 | 前端维护 10 轮对话历史 | 保障连续对话的连贯性 |
| 3D 数字人渲染与语音合成 |
魔珐星云具身交互智能 SDK |
提供数字人形象、TTS、表情动作同步 |
为什么选这个组合?DeepSeek 的对话质量我是信得过的,关键在于如何把它的文字输出"具身化"——让一段文字不只是显示在屏幕上,而是由一个 3D 数字人用温柔的语气说出来,配合相应的表情和动作。
DeepSeek太强啦,我工作中一般都会用到 DeepSeek-V4-Pro

魔珐星云在这件事上是比较直接的选择。它的 SDK 封装了从数字人渲染、语音合成到动作驱动的一整套链路,用一个 sdk.speak() 就能驱动数字人开口说话,不需要自己处理底层渲染和音频管线。
三、Demo 搭建过程
3.1 Trae如何0代码开发
提供的地址skill.md:AI Coding 操作手册
在我们的Trae编辑器中 设置 -> 技能与中心


导入成功后我们在AI输入框输入我们的指令

输入:基于我的数字人skill 以及我的项目描述 帮我可直接运行的情感陪伴交互智能体 Demo
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
Trea编辑器就能够成功调用我们的skill技能,有没有很简单,到现在为止我没有进行任何代码相关的手动开发操作,全程都是通过AI来实现我们的数字人生成。
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
生成完成后,我们进行Html文件的一个预览
\>serve \-p 3000 运行我们的html
运行 http://localhost:3000/ai-emotional/-mentor
我们可以看到这个页面已经成功展示了
现在我们去官网地址:查看官网 官网拿到我们的数字人密钥

拿到密钥
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
再打开https://www.deepseek.com 的 API 开放平台

填充我们的数字人以及大模型APIKey,我们的数字人就可以完美呈现出来~
已将本次使用到项目描述以及 上传至Gitee仓库:https://gitee.com/xiaotietou/xingyun-demo,大家可以根据本篇教程学习。
整个 Demo 是一个单 HTML 文件,打开即用。核心链路是:
用户输入文字 → DeepSeek 生成回复 → 星云 SDK 驱动数字人播报
下面是我实现的几个关键模块。
3.2 数字人初始化
星云 SDK 通过 CDN 引入,初始化只需要容器 ID 和 App 凭证:
sdk = new XmovAvatar({
containerId: "#sdk",
appId: appId,
appSecret: appSecret,
gatewayServer: "https://nebula-agent.xingyun3d.com/user/v1/ttsa/session",
});
await sdk.init({
onDownloadProgress: (p) => {
if (p < 100) updateStatus("loading", `加载 ${p}%`);
},
});
初始化完成后,数字人会出现在页面上,等待后续指令。
3.3 DeepSeek 接入与情感导师人设
DeepSeek 的 API 使用标准的 OpenAI 兼容格式,接入非常简单。关键是系统提示词的设计——我需要让 DeepSeek 的输出既专业又口语化,适合被 TTS 朗读:
const systemPrompt = `你是一名7×24小时全天候在线的专业AI情感导师,人设温柔包容、理性共情、耐心治愈。
你的核心职责:专注解决用户焦虑内耗、情绪低落、职场学业压力、人际情感矛盾、心态迷茫等问题。
固定对话流程:
1. 先共情接纳用户情绪,安抚当下心情;
2. 温和梳理问题核心逻辑;
3. 给出简单、可落地、轻量化的心态调节方法与建议。
语言风格:全程口语化、温柔治愈、逻辑清晰。回复精炼(100-200字),让TTS播报自然流畅。`;
const resp = await fetch("https://api.deepseek.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${dsKey}`,
},
body: JSON.stringify({
model: "deepseek-chat",
messages: [{ role: "system", content: systemPrompt }, ...history],
temperature: 0.75,
max_tokens: 1024,
}),
});
这里 temperature: 0.75 是一个经过几次调试后选定的值——比默认值稍高,让回复更自然、更有温度,但不至于跑偏。max_tokens: 1024 保证回复不会太长,适合 TTS 播报。
3.4 对话记忆
为了不出现"每轮都在重新自我介绍"的情况,我维护了一个 10 轮对话的上下文数组:
let conversationHistory = [];
const MAX_HISTORY = 10;
// 每次对话后维护记忆
conversationHistory.push({ role: "user", content: userMessage });
conversationHistory.push({ role: "assistant", content: reply });
// 截断旧记录
if (conversationHistory.length > MAX_HISTORY * 2) {
conversationHistory = conversationHistory.slice(-MAX_HISTORY * 2);
}
这意味着用户连续倾诉 5-10 轮后,数字人仍然能记住一开始的问题背景,给出连贯的建议,而不是每次都像第一次见面。
3.5 打断播报
情绪陪伴场景里有一个刚需:用户可能在数字人播报到一半的时候想插话。如果只能等它说完才能说下一句,体验会很差。星云 SDK 提供了 interactiveidle() 接口来处理这个场景:
function doInterrupt() {
if (!sdk) return;
sdk.interactiveidle();
}
调用后用户任意时段中途插话,即时切换交互待机状态。
四、实际体验:几个让我意外的瞬间
Demo 搭好后,我让几个朋友试了一下。有几个细节让我印象很深。
第一个是响应速度。 用户输入一句话后,DeepSeek 通常在 1-2 秒内返回结果,然后数字人几乎同时开始播报。星云 SDK 宣称的端到端约 500ms 响应在实际体验中确实感受明显——数字人不会出现"卡一下再开口"的停顿感,整个交互非常流畅。
第二个是打断的自然感。 朋友在数字人说话到一半时点了打断按钮,数字人立刻停止,安静地等待下一条输入。没有生硬的截断音,也没有重置到奇怪的状态。这个细节在真正使用中非常重要——情绪对话不是问答机器,用户需要随时接管对话节奏。
第三个是对话记忆的连贯性。 朋友连续聊了大约 8 轮,从工作压力聊到家庭关系,数字人每一轮都能关联前面的内容。最让我惊讶的是,第 6 轮时朋友说"你说的上次那个呼吸法,我今天试了",DeepSeek 在上下文里确实找到了前面提到的"呼吸练习",回复说"太好了,坚持练习会有帮助的,今天感觉怎么样?"——这个连续性让对话摆脱了"一问一答"的机械感。
五、完整 Demo 代码
我把整个 Demo 整理成了一个可直接运行的 HTML 文件。你只需要填入三个参数——星云 App ID、App Secret 和 DeepSeek API Key——就能在浏览器里看到效果。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>全天候 AI 情感陪伴导师 | 魔珐星云 × DeepSeek</title>
<style>
/* ========== 1. 全局重置与暖色调主题 ========== */
:root {
--primary: #e8a87c;
--primary-hover: #d4956a;
--primary-light: #fdf0e6;
--accent: #f4a261;
--danger: #e76f51;
--success: #2a9d8f;
--bg: #fef9f4;
--card-bg: #ffffff;
--text-main: #3e3640;
--text-sub: #8b7e74;
--text-muted: #b0a59b;
--border: #f0e4d8;
--shadow: 0 4px 24px rgba(0,0,0,0.06);
}
* { box-sizing: border-box; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "PingFang SC", "Microsoft YaHei", sans-serif;
margin: 0;
padding: 30px 20px;
background: linear-gradient(135deg, #fef9f4 0%, #fdf3e7 50%, #fef9f4 100%);
color: var(--text-main);
display: flex;
flex-direction: column;
align-items: center;
min-height: 100vh;
}
/* ========== 2. 主标题 ========== */
.main-title {
text-align: center;
margin-bottom: 10px;
}
.main-title h1 {
font-size: 28px;
font-weight: 700;
color: var(--text-main);
margin: 0;
letter-spacing: 1px;
}
.main-title .slogan {
font-size: 14px;
color: var(--text-sub);
margin-top: 6px;
letter-spacing: 0.5px;
}
/* ========== 3. 配置面板 ========== */
.config-panel {
background: var(--card-bg);
padding: 20px 24px;
border-radius: 16px;
box-shadow: var(--shadow);
width: 100%;
max-width: 960px;
display: flex;
flex-wrap: wrap;
align-items: flex-end;
gap: 14px;
margin-bottom: 24px;
border: 1px solid var(--border);
}
.input-group {
display: flex;
flex-direction: column;
gap: 5px;
flex: 1;
min-width: 180px;
}
.input-group label {
font-size: 11px;
font-weight: 700;
color: var(--text-sub);
text-transform: uppercase;
letter-spacing: 0.6px;
}
.input-group input {
padding: 0 12px;
height: 40px;
border: 1.5px solid var(--border);
border-radius: 10px;
font-size: 13px;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
background: #fefefe;
}
.input-group input:focus {
border-color: var(--primary);
box-shadow: 0 0 0 3px rgba(232,168,124,0.12);
}
.action-group {
display: flex;
align-items: flex-end;
gap: 8px;
flex-wrap: wrap;
}
button {
padding: 0 18px;
height: 40px;
border: none;
border-radius: 10px;
font-weight: 600;
font-size: 13px;
cursor: pointer;
transition: all 0.2s ease;
display: inline-flex;
align-items: center;
justify-content: center;
white-space: nowrap;
}
button:active { transform: scale(0.97); }
.btn-primary {
background: var(--primary);
color: #fff;
}
.btn-primary:hover {
background: var(--primary-hover);
box-shadow: 0 4px 14px rgba(232,168,124,0.35);
}
.btn-danger {
background: var(--danger);
color: #fff;
}
.btn-danger:hover { background: #d45d3e; }
.btn-outline {
background: transparent;
border: 1.5px solid var(--border);
color: var(--text-main);
}
.btn-outline:hover { background: var(--primary-light); border-color: var(--primary); }
.btn-accent {
background: var(--accent);
color: #fff;
}
.btn-accent:hover { box-shadow: 0 4px 14px rgba(244,162,97,0.35); }
.status-badge {
margin-left: auto;
display: flex;
align-items: center;
gap: 8px;
font-size: 13px;
color: var(--text-sub);
background: var(--primary-light);
padding: 0 16px;
border-radius: 20px;
height: 40px;
}
.status-dot {
width: 9px;
height: 9px;
border-radius: 50%;
background: #d1d5db;
transition: background 0.3s, box-shadow 0.3s;
}
.status-dot.success { background: var(--success); box-shadow: 0 0 8px var(--success); }
.status-dot.error { background: var(--danger); box-shadow: 0 0 8px var(--danger); }
.status-dot.loading { background: #f4a261; animation: pulse 1s infinite; }
@keyframes pulse {
0%,100% { opacity: 1; }
50% { opacity: 0.4; }
}
/* ========== 4. 数字人舞台 ========== */
#sdk-wrapper {
display: flex;
justify-content: center;
width: 100%;
margin-bottom: 20px;
}
#sdk {
width: 800px;
height: 450px;
background: radial-gradient(ellipse at center, #2d2838 0%, #1a1720 100%);
border-radius: 16px;
box-shadow: 0 20px 48px -12px rgba(0,0,0,0.25);
transition: all 0.4s cubic-bezier(0.25, 0.8, 0.25, 1);
position: relative;
overflow: hidden;
max-width: 100%;
}
#sdk::before {
content: "✨ 点击上方 [连接] 按钮激活数字人";
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: rgba(255,255,255,0.25);
font-size: 15px;
z-index: 0;
pointer-events: none;
}
/* ========== 5. 底部对话栏 ========== */
.chat-bar {
background: var(--card-bg);
padding: 6px;
border-radius: 50px;
box-shadow: var(--shadow);
width: 100%;
max-width: 700px;
display: flex;
gap: 8px;
border: 1px solid var(--border);
margin-bottom: 12px;
}
.chat-bar input {
flex: 1;
border: none;
background: transparent;
padding: 0 20px;
font-size: 15px;
outline: none;
color: var(--text-main);
height: 42px;
}
.chat-bar input::placeholder { color: var(--text-muted); }
.chat-bar button {
border-radius: 40px;
padding: 0 28px;
}
/* ========== 6. 快捷场景按钮 ========== */
.quick-scenes {
display: flex;
gap: 8px;
flex-wrap: wrap;
justify-content: center;
max-width: 700px;
width: 100%;
margin-bottom: 16px;
}
.quick-scenes button {
font-size: 12px;
height: 32px;
padding: 0 14px;
border-radius: 20px;
background: var(--card-bg);
border: 1px solid var(--border);
color: var(--text-sub);
}
.quick-scenes button:hover {
border-color: var(--primary);
color: var(--primary);
background: var(--primary-light);
}
/* ========== 7. 对话历史面板 ========== */
.chat-history {
background: var(--card-bg);
border-radius: 12px;
box-shadow: var(--shadow);
width: 100%;
max-width: 700px;
max-height: 200px;
overflow-y: auto;
padding: 14px 18px;
margin-bottom: 10px;
border: 1px solid var(--border);
display: none;
font-size: 13px;
line-height: 1.6;
}
.chat-history.show { display: block; }
.chat-history .msg { margin-bottom: 10px; }
.chat-history .msg.user { color: var(--text-sub); text-align: right; }
.chat-history .msg.mentor { color: var(--text-main); }
.chat-history .msg .label {
font-size: 10px;
font-weight: 700;
text-transform: uppercase;
opacity: 0.5;
margin-bottom: 2px;
}
/* ========== 8. 页脚 ========== */
.footer {
margin-top: 12px;
font-size: 11px;
color: var(--text-muted);
text-align: center;
}
.footer a {
color: var(--primary);
text-decoration: none;
}
/* ========== 9. 响应式 ========== */
@media (max-width: 768px) {
.config-panel { flex-direction: column; align-items: stretch; }
.status-badge { margin-left: 0; width: fit-content; }
.action-group { flex-wrap: wrap; }
#sdk { height: 350px; }
.main-title h1 { font-size: 22px; }
}
</style>
</head>
<body>
<!-- 标题 -->
<div class="main-title">
<h1>全天候 AI 情感陪伴导师</h1>
<p class="slogan">深夜有陪伴,情绪有出口 — 24 小时在线的专属情感导师</p>
</div>
<!-- 配置面板 -->
<div class="config-panel">
<div class="input-group">
<label>Xmov App ID</label>
<input type="text" id="inp-appid" placeholder="粘贴你的 App ID" />
</div>
<div class="input-group">
<label>Xmov App Secret</label>
<input type="password" id="inp-secret" placeholder="粘贴你的 App Secret" />
</div>
<div class="input-group">
<label>DeepSeek API Key</label>
<input type="password" id="inp-ds-key" placeholder="sk-xxxxxxxx" />
</div>
<div class="action-group">
<button class="btn-primary" onclick="doConnect()" id="btn-conn">连接</button>
<button class="btn-danger" onclick="doDisconnect()" id="btn-disconn" style="display:none">断开</button>
<button class="btn-outline" onclick="toggleRatio()" id="btn-ratio">切换至竖屏</button>
<button class="btn-outline" onclick="toggleHistory()" id="btn-history">显示对话</button>
</div>
<div class="status-badge">
<div class="status-dot" id="status-light"></div>
<span id="status-text">未连接</span>
</div>
</div>
<!-- 数字人舞台 -->
<div id="sdk-wrapper">
<div id="sdk"></div>
</div>
<!-- 快捷场景 -->
<div class="quick-scenes">
<button onclick="quickScene('最近压力很大,经常失眠,感觉很焦虑')">😰 焦虑失眠</button>
<button onclick="quickScene('和伴侣吵架了,心里很难受')">💔 情感矛盾</button>
<button onclick="quickScene('工作好累,对未来很迷茫')">😞 职场迷茫</button>
<button onclick="quickScene('总是自我否定,觉得自己不够好')">🪞 自我否定</button>
<button onclick="quickScene('帮我做个简单的呼吸放松练习')">🧘 正念放松</button>
</div>
<!-- 对话栏 -->
<div class="chat-bar">
<input type="text" id="tts-input" placeholder="输入你想说的话..." value="最近总是很焦虑,能陪我聊聊吗?" />
<button class="btn-primary" onclick="doSpeak()">💬 发送</button>
<button class="btn-accent" onclick="doInterrupt()" title="打断当前播报">⏹</button>
</div>
<!-- 对话历史 -->
<div class="chat-history" id="chat-history"></div>
<!-- 页脚 -->
<div class="footer">
基于 <a href="https://xingyun3d.com/" target="_blank">魔珐星云</a> SDK + DeepSeek 大模型驱动 |
具身交互智能 · 让 AI 进入终端
</div>
<!-- ==================== 脚本加载 ==================== -->
<script src="https://media.xingyun3d.com/xingyun3d/general/litesdk/xmovAvatar@latest.js"></script>
<script>
// ============ 全局状态 ============
let sdk = null;
let isPortrait = false;
let conversationHistory = []; // 对话上下文记忆
const MAX_HISTORY = 10; // 保留最近 10 轮对话
// ============ 1. 连接 SDK + 初始化 LLM ============
async function doConnect() {
if (sdk) return;
const appId = document.getElementById("inp-appid").value.trim();
const appSecret = document.getElementById("inp-secret").value.trim();
const dsKey = document.getElementById("inp-ds-key").value.trim();
if (!appId || !appSecret) {
alert("请先填写 Xmov App ID 和 App Secret");
return;
}
if (!dsKey) {
alert("请填写 DeepSeek API Key,用于驱动 AI 情感对话能力");
return;
}
updateStatus("loading", "连接中...");
// 实例化 Xmov SDK
sdk = new XmovAvatar({
containerId: "#sdk",
appId: appId,
appSecret: appSecret,
gatewayServer: "https://nebula-agent.xingyun3d.com/user/v1/ttsa/session",
onMessage: (msg) => console.log("[SDK Event]", msg),
});
try {
await sdk.init({
onDownloadProgress: (p) => {
if (p < 100) updateStatus("loading", `加载 ${p}%`);
},
});
updateStatus("success", "已连接");
toggleButtons(true);
console.log("✅ Xmov SDK 初始化成功");
// 连接成功后,发送欢迎语
const welcome = "你好,我是你全天候在线的 AI 情感陪伴导师。无论何时,只要你需要,我都在这儿安静倾听。";
addMessage("mentor", welcome);
sdk.speak(welcome, true, true);
// 🔥 存储 DeepSeek Key 到 sessionStorage,页面刷新后自动回填
sessionStorage.setItem("ds_key", dsKey);
} catch (err) {
console.error("连接失败:", err);
updateStatus("error", "连接失败");
alert("连接失败,请检查凭证是否正确 (F12 查看详情)");
sdk = null;
}
}
// ============ 2. 断开连接 ============
function doDisconnect() {
if (sdk) {
sdk.destroy();
sdk = null;
conversationHistory = [];
document.getElementById("chat-history").innerHTML = "";
updateStatus("error", "已断开");
toggleButtons(false);
}
}
// ============ 3. 切换横竖屏 ============
function toggleRatio() {
const container = document.getElementById("sdk");
const btn = document.getElementById("btn-ratio");
if (isPortrait) {
container.style.width = "800px";
container.style.height = "450px";
isPortrait = false;
btn.innerText = "切换至竖屏";
} else {
container.style.width = "360px";
container.style.height = "640px";
isPortrait = true;
btn.innerText = "切换至横屏";
}
}
// ============ 4. 智能播报(LLM 驱动) ============
async function doSpeak() {
if (!sdk) {
alert("请先点击顶部的 [连接] 按钮");
return;
}
const input = document.getElementById("tts-input");
const userText = input.value.trim();
if (!userText) return;
// 显示用户消息
addMessage("user", userText);
input.value = "";
updateStatus("loading", "思考中...");
// 请求 DeepSeek 生成回复
const reply = await callDeepSeek(userText);
if (reply) {
addMessage("mentor", reply);
sdk.speak(reply, true, true);
updateStatus("success", "已连接");
} else {
updateStatus("success", "已连接");
sdk.speak("抱歉,我暂时无法回应,请稍后再试。", true, true);
}
}
// ============ 5. 打断当前播报 ============
function doInterrupt() {
if (!sdk) return;
sdk.interactiveidle();
console.log("⏹ 已发送打断信号");
}
// ============ 6. 快捷场景 ============
function quickScene(text) {
document.getElementById("tts-input").value = text;
doSpeak();
}
// ============ 7. 切换对话历史显示 ============
function toggleHistory() {
const panel = document.getElementById("chat-history");
const btn = document.getElementById("btn-history");
if (panel.classList.contains("show")) {
panel.classList.remove("show");
btn.innerText = "显示对话";
} else {
panel.classList.add("show");
btn.innerText = "隐藏对话";
}
}
// ============ 8. DeepSeek LLM 调用 ============
async function callDeepSeek(userMessage) {
const dsKey = document.getElementById("inp-ds-key").value.trim()
|| sessionStorage.getItem("ds_key") || "";
if (!dsKey) {
alert("请填写 DeepSeek API Key");
updateStatus("success", "已连接");
return null;
}
// 情感导师系统提示词
const systemPrompt = `你是一名7×24小时全天候在线的专业AI情感导师,人设温柔包容、理性共情、耐心治愈,拒绝鸡汤、拒绝说教、不评判用户。
你的核心职责:专注解决用户焦虑内耗、情绪低落、职场学业压力、人际情感矛盾、心态迷茫等问题。
固定对话流程:
1. 先共情接纳用户情绪,安抚当下心情;
2. 温和梳理问题核心逻辑;
3. 给出简单、可落地、轻量化的心态调节方法与建议。
语言风格:全程口语化、温柔治愈、逻辑清晰,适配全天候私人情感陪伴场景。回复精炼(100-200字),让TTS播报自然流畅。`;
// 拼接历史上下文
const messages = [{ role: "system", content: systemPrompt }];
for (const item of conversationHistory) {
messages.push({ role: item.role, content: item.content });
}
messages.push({ role: "user", content: userMessage });
// 更新记忆
conversationHistory.push({ role: "user", content: userMessage });
if (conversationHistory.length > MAX_HISTORY * 2) {
conversationHistory = conversationHistory.slice(-MAX_HISTORY * 2);
}
try {
const resp = await fetch("https://api.deepseek.com/v1/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${dsKey}`,
},
body: JSON.stringify({
model: "deepseek-chat",
messages: messages,
temperature: 0.75,
top_p: 0.9,
max_tokens: 1024,
}),
});
if (!resp.ok) {
const err = await resp.text();
console.error("DeepSeek API 错误:", err);
throw new Error(`HTTP ${resp.status}`);
}
const data = await resp.json();
const reply = data.choices?.[0]?.message?.content || "";
if (reply) {
conversationHistory.push({ role: "assistant", content: reply });
}
return reply;
} catch (err) {
console.error("LLM 请求失败:", err);
return null;
}
}
// ============ 9. 对话记录 ============
function addMessage(role, text) {
const panel = document.getElementById("chat-history");
const div = document.createElement("div");
div.className = `msg ${role}`;
const label = role === "user" ? "🧑 你" : "💫 导师";
div.innerHTML = `<div class="label">${label}</div><div>${escapeHtml(text)}</div>`;
panel.appendChild(div);
panel.scrollTop = panel.scrollHeight;
}
function escapeHtml(str) {
const div = document.createElement("div");
div.textContent = str;
return div.innerHTML;
}
// ============ 10. UI 状态管理 ============
function updateStatus(state, text) {
const light = document.getElementById("status-light");
const label = document.getElementById("status-text");
light.className = "status-dot";
if (state === "success") light.classList.add("success");
else if (state === "error") light.classList.add("error");
else if (state === "loading") light.classList.add("loading");
label.innerText = text;
}
function toggleButtons(isConnected) {
document.getElementById("btn-conn").style.display = isConnected ? "none" : "inline-flex";
document.getElementById("btn-disconn").style.display = isConnected ? "inline-flex" : "none";
}
// ============ 11. 生命周期 ============
window.addEventListener("beforeunload", () => {
if (sdk) sdk.destroy();
});
// 页面加载时自动回填 DeepSeek Key
window.addEventListener("DOMContentLoaded", () => {
const savedKey = sessionStorage.getItem("ds_key");
if (savedKey) {
document.getElementById("inp-ds-key").value = savedKey;
}
// 默认显示对话历史面板
toggleHistory();
});
</script>
</body>
</html>
整个 Demo 的核心逻辑不到 200 行 JavaScript,但已经覆盖了:SDK 初始化、DeepSeek API 调用、系统提示词注入、对话记忆管理、打断播报、横竖屏切换等完整链路。
我整个UI美化下来也只有600行
六、从 Demo 到落地:几个实际考量
Demo 跑通之后,我也在想,如果要把它变成一个真正可用的产品,还需要考虑什么。
部署门槛。 这个 Demo 是纯前端实现,一个 HTML 文件解决所有问题。但生产环境中,API Key 显然不能放在前端。合理的做法是把 LLM 调用放到后端,前端只负责数字人渲染和交互。魔珐星云的 SDK 设计上并没有绑定特定后端,这个迁移是可行的。
响应延迟。 我实测下来,从用户发送消息到数字人开口,整体链路(DeepSeek API + SDK 渲染)在 2 秒左右。其中 DeepSeek 占了大部分时间(1-2 秒),SDK 本身的启动延迟非常低——这就是参数流架构的优势:不需要等视频渲染完毕再推流,数字人可以"边说边动"。
多终端适配。 目前的 Demo 在浏览器上运行良好。根据星云的文档,同一套 SDK 也适配移动端和大屏设备,这给后续在不同场景落地留了空间。
七、写在最后
这个实验做完,我最大的感受是:大模型和具身交互之间,不是"谁替代谁"的关系,而是"谁补齐谁"的关系。
DeepSeek 赋予 AI 完整思考推理能力,魔珐星云作为标准化具身交互智能基础设施,补齐 AI 线下终端真人化沟通全套能力,让文字层面的共情理解转化为有温度的面对面实时交互。—让它能站在你面前,看着你的眼睛,用合适的语气和节奏说出那些温暖的话。
回到文章最开头的问题:国产大模型已经很能"想"了,但怎么进入终端、进入真实场景?这个实验给我了一个思路——大模型负责理解和生成,具身交互平台负责让这种理解"活"起来。 一个是大脑,一个是身体和表达,两者结合,AI 才能真正走进人们的生活。
对于开发者来说,这个组合的门槛比想象中低。如果你已经熟悉调用大模型 API,加上数字人 SDK 并不会增加太多额外的工作量——本质上就是多了一个"能说话、有表情"的输出通道。而这个通道在某些场景里,可能就是用户体验从"还行"到"很棒"的关键。
魔珐星云官网:https://xingyun3d.com?utm_campaign=daily&utm_source=CSDNwanfen3&utm_medium=&utm_term=&utm_content=
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐


所有评论(0)