
3D模型平台数字人生成 AI知识库从模型资产到可交互智能体的完整实践数字人直播、智能客服、虚拟助教……最近这类需求越来越多。很多团队已经有不错的3D模型资产但做出来的数字人往往只是“能看不能聊”缺少知识问答能力另一边知识库系统也日趋成熟却很难和3D形象、语音交互打通。本文会围绕“3D模型平台 数字人 AI知识库”三件事讲清它们是怎么串联成一个可落地的智能数字人方案的。适合三类读者一是做3D美术和Unity/Unreal开发的工程师二是做大模型应用和知识库后台的算法/后端开发者三是想把“数字人问答”接入业务的架构师。读完你会掌握数字人资产怎么准备、知识库怎么搭建、前端怎么通过接口把两者串起来以及常见的坑和最佳实践。1. 背景与核心概念1.1 三个名词分别是什么先拆开看。3D模型平台指围绕三维模型资产的制作、管理、展示和导出的系统。常见的开源/商业工具有Blender、Unity、Unreal Engine、Ready Player Me以及各类模型托管平台。它解决的是“数字人长什么样、动作怎么动、表情怎么变”的问题。数字人用3D资产驱动出来、能在屏幕里说话/做动作的虚拟形象。它不仅仅是模型还要有骨骼绑定、表情BlendShape、口型动画、动作状态机等支撑。在直播、客服、展厅导览场景里数字人是“前端表现层”。AI知识库把文档、FAQ、操作手册等非结构化内容经过切片、向量化存入向量数据库再通过检索增强生成RAG让大模型基于知识库内容回答问题。它解决的是“数字人说什么、怎么答得准”的问题。1.2 三者的关系以最常见的智能客服数字人为例用户对着网页/大屏说话语音识别ASR把语音转成文字文字交给AI知识库服务检索相关问题并生成回复回复文本传给数字人通过语音合成TTS播放声音3D数字人播放口型动画和手势动作把回答“演”出来。所以3D模型平台负责“形”AI知识库负责“神”数字人是两者的交汇点。这也是本文把三者放一起讲的原因单独做哪一个都有大量资料但把三者串联成最小可运行闭环的完整教程比较少。1.3 为什么现在做这件事很有必要一方面是数字人直播、数字人客服已经进入落地期市场不再满足于纯绿幕真人直播或静态模型出镜另一方面大语言模型大幅降低了“机器回答问题”的门槛但如果不接企业自己的知识库大模型只能泛泛而谈。把知识库接到数字人上等于给虚拟形象装了“企业大脑”价值从“展示”转向“服务”。2. 整体技术方案与架构设计2.1 选择技术栈的原则技术选型不要盲目追新优先考虑团队已有技能、资产格式、部署成本。下面列出的是一个通用型选型你可以按实际情况替换模块可选方案本教程采用3D建模/资产处理Blender、3ds Max、CC/iCloneBlender免费开源支持FBX/GLB实时渲染/交互前端Unity、Unreal、Three.jsUnity跨平台、C#开发效率高数字人驱动Unity动画系统 口型插件/ARKit BlendShapeUnity Animation BlendShape语音转文字(ASR)阿里云/腾讯云/Whisper本地Whisper或云厂商API按需接入文本转语音(TTS)Azure TTS、讯飞、Edge TTSAzure/Edge TTS示例知识库服务LangChain 向量库 FastAPILangChain Chroma/FAISS FastAPI大模型OpenAI、通义千问、DeepSeek、本地Ollama以接口方式抽象不写死厂商这里要强调的是数字人前端和知识库后端是松耦合的通过HTTP接口通信。这样知识库可以独立升级前端也可以随时换模型。2.2 系统架构示意不用花哨的图用文字就能说清用户输入(文本/语音) | v [前端数字人] Unity客户程序 - 3D模型渲染 - 动画/口型播放 - TTS音频播放 | | HTTP (POST /chat) v [AI知识库服务] FastAPI LangChain - 文档切片 - 向量化召回 - LLM 生成回答 | v [3D模型资产 / 知识库文档]前端把用户问题发给知识库服务拿到回答文本后播放TTS和口型动画。核心逻辑全部在后端前端只做“表现 交互”。3. 环境准备与工具链3.1 软件准备版本需要根据你的项目实际情况调整本文示例以常见环境为例重点演示配置思路。Windows 10/11 或 macOS 均可Blender 3.x/4.x用于模型检查和导出Unity 2021 LTS 或 2022 LTS用于数字人前端Python 3.9用于知识库服务Node.js 非必须可选做前端调试熟悉基本命令行操作。3.2 项目目录结构建议整个工程采用以下结构digital-human-project/ |-- unity-client/ # Unity 工程 | |-- Assets/ | | |-- Models/ # 3D数字人模型 | | |-- Animations/ # 动画资源 | | |-- Scripts/ # C# 脚本 | | |-- Scenes/ # 场景 |-- knowledge-base/ # Python 知识库服务 | |-- app.py # FastAPI 入口 | |-- loader.py # 文档加载/切片 | |-- vector_store.py # 向量库封装 | |-- requirements.txt # 依赖 | |-- data/ # 原始文档 |-- docs/ # 说明文档先用这个结构把职责分清楚后面不会乱。4. 核心模块一3D模型资产与数字人准备4.1 数字人模型从哪里来数字人资产一般有四种来源商业平台下载Ready Player Me、MetaHuman、捏脸平台适合快速验证美术人员手工建模质量最高周期长扫描/照片建模适合真人复制开源模型库下载后二次修改。无论哪种来源都要关注三个技术点模型格式Unity最常用FBX如果走Web端GLB/GLTF更方便。骨骼绑定数字人需要标准骨骼HumanoidUnity才能自动映射动画。表情BlendShape驱动口型和表情必备通常在建模阶段用Blender或Maya制作然后导出到FBX。4.2 用 Blender 检查并导出模型假设你拿到了一个glTF格式的模型先在Blender里打开确认骨骼和BlendShape状态。# 可以在Blender的Python Console里执行检查对象类型 import bpy for obj in bpy.data.objects: if obj.type MESH: mesh obj.data print(网格名称:, obj.name) print(BlendShape数量:, len(mesh.shape_keys.key_blocks) if mesh.shape_keys else 0)导出FBX时在File - Export - FBX里注意Path Mode选择 Copy并勾选 Embed Textures避免材质丢失Armature选择模型骨骼所在的骨架Bake Animation如果模型带动画就勾选没有就不勾Scale建议统一为1Unity中缩放容易出问题。4.3 Unity 中的数字人配置FBX导入Unity后按以下流程设置选中模型文件在Inspector里切到Rig标签Animation Type选HumanoidApply切到Animation标签确认导入的动画Clip是否正常把模型拖入场景挂上Animator组件如果已准备好口型BlendShape可以通过代码控制SkinnedMeshRenderer的BlendShape权重这是TTS口型驱动的基础。数字人不是本文的核心难点但要保证模型能正常显示、能播放待机动画。后面接入TTS和知识库时我们只需要在代码里控制模型说话即可。5. 核心模块二AI知识库搭建严格说知识库并不只有“上传文档 问答”这么简单。完整的RAG链路是文档加载 - 切片 - 向量化 - 存储 - 召回 - 生成回答。每一步都有坑下面逐一说明。5.1 文档加载与切片知识库的原始数据通常是PDF、Word、Markdown、TXT。先用LangChain加载再切分。# 文件路径knowledge-base/loader.py from langchain_community.document_loaders import TextLoader, PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter import os def load_documents(data_dir: str data): docs [] for file in os.listdir(data_dir): path os.path.join(data_dir, file) if file.endswith(.txt): loader TextLoader(path, encodingutf-8) docs.extend(loader.load()) elif file.endswith(.pdf): loader PyPDFLoader(path) docs.extend(loader.load()) # Word文档可以额外引入 docx2txt 等按需扩展 return docs def split_documents(docs, chunk_size500, chunk_overlap50): splitter RecursiveCharacterTextSplitter( chunk_sizechunk_size, chunk_overlapchunk_overlap, separators[\n\n, \n, 。, , , , ] ) chunks splitter.split_documents(docs) return chunks为什么要设置chunk_overlap因为跨切片边界的一句话可能会被截断重叠部分可以降低信息丢失概率。chunk_size的选择需要结合检索效果反复调不是越大越好。如果文档结构清晰、章节标题明显可以尝试MarkdownHeaderTextSplitter来保留上下文。5.2 向量化与向量库向量化就是把文本变成一串数字语义相近的文本在向量空间里距离更近。Embedding模型的选择直接影响检索效果。# 文件路径knowledge-base/vector_store.py from langchain_community.embeddings import HuggingFaceEmbeddings from langchain_community.vectorstores import Chroma PERSIST_DIR ./chroma_db def build_vector_store(chunks): embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-large-zh-v1.5 ) vectordb Chroma.from_documents( documentschunks, embeddingembeddings, persist_directoryPERSIST_DIR ) vectordb.persist() return vectordb def load_vector_store(): embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-large-zh-v1.5 ) return Chroma( persist_directoryPERSIST_DIR, embedding_functionembeddings )这里用HuggingFace的bge模型做演示实际项目中也可以换成OpenAI嵌入接口或其他中文Embedding模型。首次运行会下载模型建议在网络环境稳定的条件下操作。如果文档量大需要考虑向量库选型Chroma适合单机小规模Milvus适合百万级以上向量和分布式部署FAISS适合内存检索轻量高效。团队只要数据量不大用Chroma起步即可。5.3 检索 生成RAG主流程RAG的核心在于不直接让大模型随意回答而是先从知识库检索出与问题最相关的若干片段作为“参考资料”拼进Prompt再让大模型根据参考资料回答。# 文件路径knowledge-base/rag_chain.py from langchain.chains import RetrievalQA from langchain.prompts import PromptTemplate from langchain_community.llms import Ollama from vector_store import load_vector_store PROMPT_TEMPLATE 你是一个企业内部知识助手。请基于以下资料回答问题不要编造资料中没有的内容。 【资料】 {context} 【问题】 {question} 如果资料中没有相关内容请直接说“抱歉我没有在资料库中找到相关信息”。 def create_rag_chain(): vectordb load_vector_store() retriever vectordb.as_retriever(search_kwargs{k: 4}) llm Ollama(modelqwen2.5:7b, temperature0.1) prompt PromptTemplate( templatePROMPT_TEMPLATE, input_variables[context, question] ) qa_chain RetrievalQA.from_chain_type( llmllm, retrieverretriever, chain_typestuff, chain_type_kwargs{prompt: prompt}, return_source_documentsTrue ) return qa_chain注意temperature设小控制在0到0.3知识问答场景要求稳定、忠实原文不要“发挥”。返回source_documents便于调试时查看模型依据了哪些资料这一点在正式项目里非常重要。5.4 封装 FastAPI 接口数字人前端通过HTTP调用所以我们用FastAPI暴露一个统一接口。# 文件路径knowledge-base/app.py from fastapi import FastAPI from pydantic import BaseModel from rag_chain import create_rag_chain app FastAPI(titleAI知识库服务) qa_chain create_rag_chain() class ChatRequest(BaseModel): message: str session_id: str default class ChatResponse(BaseModel): reply: str sources: list[str] [] app.post(/chat, response_modelChatResponse) def chat(req: ChatRequest): result qa_chain.invoke({query: req.message}) reply result[result] sources [doc.metadata.get(source, ) for doc in result[source_documents]] return ChatResponse(replyreply, sourcessources) app.get(/health) def health(): return {status: ok}启动服务cd knowledge-base pip install -r requirements.txt uvicorn app:app --host 0.0.0.0 --port 8000测一下接口curl -X POST http://localhost:8000/chat \ -H Content-Type: application/json \ -d {message: 公司年假政策是什么, session_id: test}返回里会包含reply和sourcessources可以用来做引用溯源。6. 核心模块三数字人前端接入知识库6.1 前端职责拆分Unity前端主要做四件事渲染数字人模型接收用户输入文本框或语音调用知识库HTTP接口播放TTS音频和口型/动作。下面给出一个最小可用的C#脚本假设你在Unity场景里放了一个InputField输入问题一个Button按钮触发对话一个Text显示回答。6.2 Unity C# 调用知识库// 文件路径unity-client/Assets/Scripts/ChatClient.cs using System.Collections; using System.Text; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; public class ChatClient : MonoBehaviour { [Header(知识库服务地址)] public string apiUrl http://localhost:8000/chat; [Header(UI组件)] public InputField inputField; public Text answerText; public Button sendButton; private void Start() { sendButton.onClick.AddListener(SendQuestion); } public void SendQuestion() { string question inputField.text.Trim(); if (string.IsNullOrEmpty(question)) { answerText.text 请输入问题; return; } StartCoroutine(RequestAnswer(question)); } private IEnumerator RequestAnswer(string question) { string json JsonUtility.ToJson(new ChatRequestData { message question, session_id unity-demo }); byte[] body Encoding.UTF8.GetBytes(json); using (UnityWebRequest request new UnityWebRequest(apiUrl, POST)) { request.uploadHandler new UploadHandlerRaw(body); request.downloadHandler new DownloadHandlerBuffer(); request.SetRequestHeader(Content-Type, application/json); yield return request.SendWebRequest(); if (request.result UnityWebRequest.Result.Success) { ChatResponseData data JsonUtility.FromJsonChatResponseData(request.downloadHandler.text); answerText.text data.reply; // 拿到回复文本后在这里触发TTS和口型动画 StartCoroutine(SpeakAndMouth(data.reply)); } else { answerText.text 请求失败: request.error; } } } private IEnumerator SpeakAndMouth(string text) { // TODO: 调用TTS接口拿到音频后播放 // 同时根据音频时长/文本长度驱动数字人BlendShape或动画 yield return null; } [System.Serializable] public class ChatRequestData { public string message; public string session_id; } [System.Serializable] public class ChatResponseData { public string reply; public string[] sources; } }记得在Player Settings里勾选“Allow downloads over HTTP”否则Unity在本地调试时会拦截http请求这是新手最容易碰到的问题。6.3 TTS口型驱动的衔接思路拿到知识库返回的文本后数字人需要“开口说话”。推荐流程把文本发给TTS服务得到音频URL或音频字节流Unity里用AudioSource播放音频根据音频播放进度或文本长度动态设置数字人SkinnedMeshRenderer上BlendShape的权重让嘴一张一合。口型同步如果要做到精确需要Viseme级映射如微软Azure TTS返回的viseme事件复杂但效果好如果只是演示一段循环“闭嘴/微微张口”的动画也能应付。项目落地时建议先做“能说话”再优化“说得像”。7. 完整运行演示与预期结果7.1 启动顺序按以下步骤启动整个项目启动知识库服务cd knowledge-base uvicorn app:app --host 0.0.0.0 --port 8000用curl或浏览器访问接口确认返回正常curl http://localhost:8000/health打开Unity工程运行主场景在InputField输入问题点击发送观察控制台日志、UI文本显示、数字人动作响应。7.2 预期输出知识库返回的答案与上传文档内容一致不答非所问回答文本中如果提到来源Unity端Log可以打印sources列表数字人待机动画正常播放收到回答后触发TTS说话动作。7.3 核心链路梳理再强调一遍这个闭环用户输入问题 - Unity 发送 POST 请求 - FastAPI 接收 - 向量库检索相关片段 - LLM 生成回答 - FastAPI 返回 JSON - Unity 解析 reply - TTS 播放语音 - 数字人播放口型动画后续无论哪一环出问题都可以顺着这个链路逐层排查先看请求有没有到后端再看后端有没有返回最后看前端解析和播放是否有误。8. 常见问题与排查思路8.1 高频问题速查表问题现象常见原因解决思路Unity请求本地服务失败Player Settings未开启HTTP访问Player Settings - Allow downloads over HTTP 设为 Always allowedCORS跨域报错浏览器端调试时后端未开CORSFastAPI添加CORSMiddleware配置知识库回答与文档无关切片过大/过小、Embedding模型不匹配调整chunk_size换中文效果更好的Embedding检查检索召回内容模型导入Unity后材质丢失FBX导出时未嵌入贴图导出FBX时勾选Embed Textures统一材质路径规范数字人说话但嘴不动BlendShape权重未绑定确认模型包含BlendShape代码里正确设置SkinnedMeshRenderer权重TTS音频没有声音音频未赋值给AudioSource或输出设备异常检查AudioSource.clip、AudioListener位置、音量大模型回答缓慢模型推理速度慢改用更小模型/量化模型或升级GPU增加缓存向量库里文档更新不生效持久化目录已存在未重建索引删除chroma_db目录后重新build或实现增量更新8.2 关键排查案例案例1知识库回答“胡言乱语”先看是不是检索阶段就失败了。最简单的方法是打印出检索到的片段question 公司年假政策是什么 docs retriever.get_relevant_documents(question) for i, doc in enumerate(docs): print(f[{i}] {doc.page_content[:100]})如果打印出的片段本身就不相关问题出在切片或Embedding如果片段相关但大模型回答错误问题出在Prompt或模型选择。案例2Unity跨域/请求失败本地调试Unity时多数不是CORS问题而是HTTP访问限制。先在电脑浏览器里访问FastAPI接口确认服务可用再检查Unity日志中的具体错误码。如果是生产环境部署到服务器记得把IP更换为服务器公网地址并确认端口安全组放行。9. 最佳实践与工程建议9.1 3D资产侧建议统一单位与坐标轴建模时统一使用米制Y轴向上减少Unity导入后的旋转和缩放问题。LOD分级数字人模型在移动端和Web端要考虑LOD多级细节近景用高模远景用低模保证帧率。材质规范命名清晰贴图使用压缩格式同时保留原始贴图目录避免之后无法找回源资产。动画状态机设计待机、说话、点头、手势分开制作用Animator参数控制切换避免所有动画重叠播放。9.2 知识库侧建议文档质量决定回答质量RAG系统里脏数据比模型选型更致命。上线前清洗文档保留有价值、无矛盾、结构清晰的资料。切片参数要实验500字不是万能值。可以用多组chunk_size300/500/800做评测对比回答准确性后再定。记录并反馈检索日志每次问答都记录检索到的文档ID和来源便于回溯“为什么这么回答”。权限和敏感信息处理知识库可能涉及内部文档要控制服务访问权限不要直接暴露在公网。加一层API Token校验会更稳妥。版本管理知识库内容会变要建立数据版本概念方便回滚。比如每次导入前备份当前向量库目录。9.3 生产环境注意事项本地Ollama适合开发和内网公网高并发场景建议使用云端大模型API或GPU部署向量库和模型服务分离部署避免互相影响接口要做超时处理和降级策略大模型响应慢时前端要提示“正在思考”不要无反馈如果是数字人直播场景还需要考虑推流、帧率、丢帧这部分超出本文范围但架构要充分考虑延时要低。10. 总结与后续学习方向到这里你已经看完了一个完整的三层链路3D模型资产准备、AI知识库搭建、Unity前端接入。关键收获有三点第一数字人不只是模型而是“模型 动画 语音 文本响应”的组合每个环节都独立又互相依赖。第二知识库的核心不是调用大模型接口而是文档切片、向量检索和Prompt设计检索效果直接决定回答质量。第三各模块之间用HTTP接口解耦是成本和灵活性之间最好的平衡任何一端都能独立升级替换。后续你可以继续深入几个方向如果偏前端可以研究数字人口型同步、动作生成和情绪表达如果偏算法可以优化RAG的混合检索关键词向量、重排序和知识库增量更新如果偏工程可以把FastAPI改造成异步服务加上任务队列、缓存和监控指标。数字人和AI知识库都在快速演进但架构思想是稳定的。拿一个真实的业务场景从最小闭环开始做跑通一条“模型 - 知识 - 对话”的链路你就能在这个领域站稳脚跟。如果本文对你有帮助可以收藏备用也欢迎在评论区聊聊你搭建数字人过程中遇到的坑。