基于 Vue3 + 3D数字人SDK搭建医美科普顾问系统实战指南

本文记录了如何利用 Vue3 前端框架结合3D数字人渲染SDK,快速搭建一个面向医美/美业场景的智能科普顾问系统,实现门店大屏的数字人讲解护肤项目、疗程流程及术后注意事项等功能。

一、场景背景与需求分析

医美门店常见痛点:

  • 顾客对项目原理、疗程流程了解不足,过度依赖咨询师逐一讲解
  • 术后注意事项口头传达易遗漏,信息传递不可控
  • 咨询话术缺乏标准化,存在合规风险

数字人顾问可以解决的核心问题:

  • 7×24 大屏循环播报,覆盖顾客等待时段
  • 话术模板化输出,规避夸大宣传和医疗承诺
  • 温和、专业的形象提升顾客信任感

二、技术架构概览

本方案采用 前后端分离 的架构设计:

层级 技术选型 职责
前端框架 Vue 3 + Vite 4.x 组件化UI、响应式数据绑定
3D数字人渲染 星云LiteSDK 模型加载、动作驱动、TTS播报
AI对话引擎 OpenAI兼容接口(如豆包大模型) 自然语言理解与生成
后端服务 Express 5.x API代理、密钥安全、CORS处理
样式方案 原生CSS + 组件内样式 轻量、无额外依赖

通信方式:

  • AI大模型:HTTP 接口通信(流式/非流式)
  • 数字人控制:WebSocket 实时通信
  • 前后端交互:RESTful API

三、项目结构

digital-human-advisor/
├── index.html                 # 入口HTML,加载SDK脚本
├── package.json               # 依赖配置
├── vite.config.ts             # Vite构建配置
├── src/
│   ├── main.ts                # 应用入口,挂载Vue实例
│   ├── App.vue                # 根组件
│   ├── styles/
│   │   └── main.css           # 全局样式
│   ├── services/
│   │   ├── llm.service.js     # 大模型服务封装
│   │   └── xingyun.service.js # 数字人SDK服务封装
│   ├── components/
│   │   └── CustomerService.vue # 客服主组件
│   ├── config/                # 配置文件(密钥、模型参数等)
│   └── utils/                 # 工具函数
└── dist/                      # 构建产物

四、关键实现步骤

4.1 入口页面与SDK加载

index.html 中引入数字人渲染SDK,这是整个系统的核心依赖:

<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>医美科普顾问 - 专业护肤指导</title>
    <!-- 引入3D数字人SDK -->
    <script src="https://media.xingyun3d.com/xingyun3d/general/litesdk/xmovAvatar@latest.js"></script>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>

要点:

  • SDK 通过 CDN 直接加载,无需 npm 安装
  • @latest 标签确保获取最新版本,生产环境建议锁定具体版本号
  • Vue 应用挂载在 #app 容器上

4.2 Vite 构建配置

import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'

export default defineConfig({
  plugins: [vue()],
  resolve: {
    alias: {
      '@': '/src'
    }
  },
  server: {
    port: 3000,
    open: true
  }
})

关键配置说明:

  • @ 路径别名简化模块引用
  • 开发服务器端口设为 3000,自动打开浏览器
  • 由于SDK是外部脚本,Vite无需额外处理

4.3 核心服务层封装

4.3.1 数字人SDK服务(xingyun.service.js)

这是与3D数字人交互的核心封装层,关键方法包括:

// 初始化数字人实例
async function initAvatar(container, config) {
  // container: DOM挂载容器
  // config: 包含appId、appSecret等认证信息
  const avatar = new XmovAvatar({
    container,
    appId: config.appId,
    appSecret: config.appSecret,
    // 数字人形象配置
    avatarId: config.avatarId,
    // TTS语音配置
    ttsConfig: {
      voiceId: config.voiceId,
      speed: 1.0,
      pitch: 1.0
    }
  })
  await avatar.init()
  return avatar
}

// 驱动数字人语音播报
async function speak(avatar, text) {
  await avatar.speak(text)
}

// 驱动数字人执行动作
async function playAction(avatar, actionId) {
  await avatar.playAction(actionId)
}

使用流程:

  1. 调用 initAvatar 初始化数字人,传入容器和认证配置
  2. 通过 speak 方法让数字人播报指定文本(内部自动完成TTS + 口型同步)
  3. 通过 playAction 触发预设动作(如点头、挥手等)
4.3.2 大模型服务(llm.service.js)

封装AI对话能力,支持普通模式和流式模式:

import OpenAI from 'openai'

// 初始化客户端(兼容豆包等国产大模型)
const client = new OpenAI({
  apiKey: process.env.LLM_API_KEY,
  baseURL: process.env.LLM_BASE_URL  // 如:https://ark.cn-beijing.volces.com/api/v3
})

// 医美科普场景的System Prompt
const SYSTEM_PROMPT = `你是一位专业的医美科普顾问,名叫小颜。你的职责是:
1. 用通俗易懂的语言讲解护肤项目的原理和流程
2. 提供术后注意事项的专业建议
3. 话术温和专业,避免夸大效果和医疗承诺
4. 遇到超出科普范围的问题,建议顾客咨询专业医生
5. 绝对不使用"保证治愈"、"100%有效"等绝对化用语`

// 非流式对话
async function sendMessage(messages) {
  const response = await client.chat.completions.create({
    model: process.env.LLM_MODEL_ID,
    messages: [
      { role: 'system', content: SYSTEM_PROMPT },
      ...messages
    ],
    temperature: 0.7,
    max_tokens: 1024
  })
  return response.choices[0].message.content
}

// 流式对话(推荐,体验更好)
async function* sendMessageStream(messages) {
  const stream = await client.chat.completions.create({
    model: process.env.LLM_MODEL_ID,
    messages: [
      { role: 'system', content: SYSTEM_PROMPT },
      ...messages
    ],
    temperature: 0.7,
    max_tokens: 1024,
    stream: true
  })
  for await (const chunk of stream) {
    const content = chunk.choices[0]?.delta?.content || ''
    if (content) yield content
  }
}

要点:

  • 使用 OpenAI SDK 的兼容接口对接豆包等国产大模型,只需修改 baseURLapiKey
  • System Prompt 是规避风险的核心:明确规定禁止使用绝对化用语,超出范围引导咨询医生
  • 流式输出配合数字人的逐句播报,体验更自然

4.4 客服主组件(CustomerService.vue)

核心交互逻辑组件,连接数字人渲染与AI对话:

<template>
  <div class="customer-service">
    <!-- 数字人展示区 -->
    <div class="avatar-container" ref="avatarContainer"></div>
    
    <!-- 对话交互区 -->
    <div class="chat-area">
      <div class="messages" ref="messagesContainer">
        <div v-for="(msg, index) in messages" :key="index"
             :class="['message', msg.role === 'user' ? 'user' : 'assistant']">
          {{ msg.content }}
        </div>
        <div v-if="loading" class="message assistant typing">
          正在思考中...
        </div>
      </div>
      
      <!-- 快捷问题按钮 -->
      <div class="quick-questions">
        <button v-for="q in quickQuestions" :key="q" @click="askQuestion(q)">
          {{ q }}
      </div>
      
      <!-- 输入区域 -->
      <div class="input-area">
        <input v-model="userInput" @keyup.enter="sendMessage"
               placeholder="请输入您想了解的护肤问题..." />
        <button @click="sendMessage" :disabled="loading">发送</button>
      </div>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'
import { initAvatar, speak } from '@/services/xingyun.service'
import { sendMessageStream } from '@/services/llm.service'

const avatarContainer = ref(null)
const messagesContainer = ref(null)
const messages = ref([])
const userInput = ref('')
const loading = ref(false)
let avatarInstance = null

// 医美场景快捷问题
const quickQuestions = [
  '光子嫩肤的原理是什么?',
  '水光针术后如何护理?',
  '热玛吉和超声刀有什么区别?',
  '敏感肌可以做哪些项目?'
]

onMounted(async () => {
  // 初始化数字人
  avatarInstance = await initAvatar(avatarContainer.value, {
    appId: import.meta.env.VITE_XINGYUN_APP_ID,
    appSecret: import.meta.env.VITE_XINGYUN_APP_SECRET,
    avatarId: import.meta.env.VITE_AVATAR_ID,
    voiceId: import.meta.env.VITE_VOICE_ID
  })
  
  // 数字人开场白
  const greeting = '您好,我是您的专业护肤顾问小颜。有什么护肤或医美方面的问题,都可以问我哦。'
  messages.value.push({ role: 'assistant', content: greeting })
  await speak(avatarInstance, greeting)
})

async function sendMessage() {
  const text = userInput.value.trim()
  if (!text || loading.value) return
  
  messages.value.push({ role: 'user', content: text })
  userInput.value = ''
  loading.value = true
  
  let fullResponse = ''
  
  try {
    // 流式获取AI回复
    for await (const chunk of sendMessageStream(messages.value)) {
      fullResponse += chunk
    }
    
    messages.value.push({ role: 'assistant', content: fullResponse })
    
    // 驱动数字人播报回复
    await speak(avatarInstance, fullResponse)
  } catch (error) {
    console.error('对话异常:', error)
    messages.value.push({
      role: 'assistant',
      content: '抱歉,我暂时无法回答这个问题,建议您咨询门店专业医生。'
    })
  } finally {
    loading.value = false
    await nextTick()
    messagesContainer.value?.scrollTo({
      top: messagesContainer.value.scrollHeight,
      behavior: 'smooth'
    })
  }
}

function askQuestion(question) {
  userInput.value = question
  sendMessage()
}
</script>

4.5 后端代理服务

出于安全考虑,大模型API密钥不应暴露在前端。使用 Express 搭建轻量代理:

import express from 'express'
import cors from 'cors'
import dotenv from 'dotenv'
import OpenAI from 'openai'

dotenv.config()

const app = express()
app.use(cors())
app.use(express.json())

const client = new OpenAI({
  apiKey: process.env.LLM_API_KEY,
  baseURL: process.env.LLM_BASE_URL
})

// 流式对话代理
app.post('/api/chat', async (req, res) => {
  try {
    const { messages } = req.body
    const stream = await client.chat.completions.create({
      model: process.env.LLM_MODEL_ID,
      messages,
      stream: true,
      temperature: 0.7,
      max_tokens: 1024
    })

    res.setHeader('Content-Type', 'text/event-stream')
    res.setHeader('Cache-Control', 'no-cache')
    res.setHeader('Connection', 'keep-alive')

    for await (const chunk of stream) {
      const content = chunk.choices[0]?.delta?.content || ''
      if (content) {
        res.write(`data: ${JSON.stringify({ content })}\n\n`)
      }
    }
    res.write('data: [DONE]\n\n')
    res.end()
  } catch (error) {
    console.error('API代理异常:', error)
    res.status(500).json({ error: '服务暂时不可用' })
  }
})

app.listen(3001, () => {
  console.log('代理服务运行在 http://localhost:3001')
})

五、环境变量配置

在项目根目录创建 .env 文件:

# 数字人SDK配置
VITE_XINGYUN_APP_ID=your_app_id
VITE_XINGYUN_APP_SECRET=your_app_secret
VITE_AVATAR_ID=your_avatar_id
VITE_VOICE_ID=your_voice_id

# 大模型配置(后端使用,不以VITE_开头)
LLM_API_KEY=your_api_key
LLM_BASE_URL=https://ark.cn-beijing.volces.com/api/v3
LLM_MODEL_ID=your_model_endpoint_id

安全提示:

  • VITE_ 前缀的变量会暴露到前端,仅放SDK相关配置
  • API密钥等敏感信息放在后端,通过代理接口访问
  • .env 文件必须加入 .gitignore,禁止提交到代码仓库

六、安装与运行

# 安装依赖
npm install

# 启动开发服务器(前端)
npm run dev

# 启动后端代理(另一个终端)
node server.js

# 构建生产版本
npm run build

# 预览生产版本
npm run preview

七、医美场景话术规范要点

在 System Prompt 设计中,以下规则至关重要:

  1. 禁止绝对化用语:不使用"保证治愈"、“100%有效”、"完全无副作用"等表述
  2. 效果描述留余地:用"多数用户反馈"、"一般而言"等限定词
  3. 主动引导线下咨询:涉及具体方案和定价时,建议"到店由专业医生面诊"
  4. 术后护理标准化:按项目类型提供通用护理要点,同时声明"个体差异请遵医嘱"
  5. 敏感问题转人工:涉及不适症状、过敏反应等,立即建议就医
    在这里插入图片描述

八、部署建议

  1. 大屏适配:数字人展示区建议使用 16:99:16 比例,CSS通过 vw/vh 适配大屏
  2. 离线兜底:AI服务不可用时,预设常见问题的标准回复脚本,确保数字人仍可播报
  3. 自动循环:闲时自动播放项目介绍、护理知识等预设内容,提高大屏利用率
  4. 并发控制:门店大屏场景通常为单用户交互,无需考虑高并发,但流式接口需做好超时处理
    在这里插入图片描述

九、核心依赖版本

{
  "dependencies": {
    "cors": "^2.8.5",
    "dotenv": "^17.2.3",
    "express": "^5.2.1",
    "openai": "^6.9.1",
    "vue": "^3.3.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^4.2.0",
    "vite": "^4.5.14"
  }
}

十、总结

本方案的核心思路是:

  • 3D数字人SDK 负责"看得见"的交互层——模型渲染、动作驱动、语音播报
  • 大模型API 负责"听得懂"的智能层——自然语言理解、科普内容生成
  • Vue3 + Express 负责"跑得稳"的工程层——组件化开发、密钥安全、前后端分离

三者结合,就能在医美门店场景中实现一个既有专业形象又有智能内核的数字人科普顾问。整个搭建过程对前端开发者来说门槛不高,重点在于 System Prompt 的话术规范设计和数字人形象/语音的选型配置。

[DuMate AI生成]

Logo

电影级数字人,免显卡端渲染SDK,十行代码即可调用,工业级demo免费开源下载!

更多推荐