恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
【Unity-AI开发篇】| 游戏中接入DeepSeek实现AI对话,完整详细步骤与TaoToken统一Key配置
首页
资讯中心
/
【Unity-AI开发篇】| 游戏中接入DeepSeek实现AI对话,完整详细步骤与TaoToken统一Key配置
【Unity-AI开发篇】| 游戏中接入DeepSeek实现AI对话,完整详细步骤与TaoToken统一Key配置
发布时间:2026/10/15 18:46:36
1. Unity 游戏里让 NPC 开口说话DeepSeek 对话接入到底难在哪如果你正在做 Unity 项目想让场景里的 NPC 不再只会循环播放几句固定台词而是能根据玩家输入实时生成回复那 DeepSeek 这类大语言模型确实是个顺手的选择。它中文理解自然、响应速度快用来做游戏内对话、任务提示、剧情分支都挺合适。适合谁独立开发者、Unity 初学者、想给 Demo 加 AI 交互的策划同学只要你会基本的 C# 和 Unity 编辑器操作就能跟上。但真正动手时问题往往不在“怎么发一条 HTTP 请求”而在几个容易被忽略的细节上。第一密钥管理。你一开始可能只接 DeepSeek过两天想换成别的模型做对比于是项目里散落着好几套 API Key、好几个 Base URL改起来容易漏。第二Unity 的网络请求是协程驱动的UnityWebRequest用不好就会出现请求发出去了、回调没进、UI 一直转圈的情况。第三对话上下文。单轮问答很简单但 NPC 要记住前面聊过什么就得自己维护一个 messages 数组还要控制 token 长度不然聊久了请求体越来越大。我试过最原始的做法把 Key 硬编码在脚本里apiUrl写死 DeepSeek 的地址。跑通是跑通了可一旦要换模型或者 Key 泄露要轮换就得翻遍整个工程。后来我把请求层抽出来统一走一个兼容 OpenAI 格式的通道Base URL 和 Key 都从配置读模型名做成 Inspector 可调这样同一套脚本既能接 DeepSeek也能接别的同格式模型。这篇就按这个思路来先讲清楚整体结构再给你可以直接复制的 C# 封装脚本和上下文管理组件然后一步步在 Unity Editor 里跑通验证最后把常见的报错对照着排一遍。密钥这块我会用 TaoToken 的统一 Key 通道来管理这样你后面想加模型不用改代码只改配置就行。2. 用 TaoToken 统一 Key 通道管理多模型凭据在动手写脚本之前先把“钥匙”这件事理顺。很多教程让你直接去某个模型平台申请 Key然后硬编码进 Unity。这样做在单模型 Demo 里没问题但游戏开发往往是长期迭代今天用 DeepSeek 做对话明天可能想试试别的模型做旁白后天又要给不同 NPC 配不同模型。如果每个模型都单独申请、单独存 Key工程里很快就会变成一团乱麻。TaoToken 在这里的角色是一个统一的 API 凭据通道。你可以在它的控制台里创建 Key然后通过同一个 Base URL 去调用不同模型模型名在请求体里指定即可。对 Unity 来说这意味着你的 C# 脚本只需要认一个地址、一个 Key切换模型只是改一个字符串。官网地址是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 根地址是 https://taotoken.net/api 注意这个 API 地址后面不加任何查询参数。具体操作上你先到控制台创建一个 API Key。打开 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite 登录后进入 API Keys 页面点新建给它起个名字比如unity-npc-dialogue创建完把 Key 复制出来。这个 Key 只在创建时完整显示一次后面只能看到前缀所以务必先存到安全的地方比如本地密码管理器别直接提交到 Git。拿到 Key 之后你还需要确认要用的模型 ID。DeepSeek 对话常用的模型 ID 是deepseek-chat这个字符串要填到请求体的model字段里。如果你不确定当前支持哪些模型可以到文档页查一下 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。文档里会列出可用的模型名和对应的请求格式Unity 这边用的就是标准的 OpenAI 兼容格式/v1/chat/completions这个路径。这里有个关键点TaoToken 的 Base URL 是https://taotoken.net/api那么完整的请求地址就是https://taotoken.net/api/v1/chat/completions。你在 Unity 脚本里配置的时候建议把 Base URL 和路径分开或者直接写完整地址但一定要保证拼接正确。我见过有人把 Base URL 写成带/v1的结果请求发到https://taotoken.net/api/v1/v1/chat/completions直接 404。另外如果你后面想用 Claude Code 这类编码工具或者想给项目里的 Agent 配长期通道可以了解一下 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。它和游戏内对话是两条线但底层凭据管理思路一致都是把 Key 集中管起来避免散落。把 Key 和模型 ID 准备好之后就可以进入 Unity 工程了。记住三个东西Base URL 用https://taotoken.net/apiKey 用刚创建的那串模型 ID 用deepseek-chat。这三件套在后面的脚本里会分别对应apiUrl、apiKey、modelName三个字段。3. 可复制的 Unity C# 配置与对话脚本现在进入实操。打开你的 Unity 项目在Assets下新建一个文件夹叫AIDialogue里面再建Scripts子文件夹。我们需要两个脚本一个是对话管理器AIDialogueManager.cs负责发请求、解析响应、维护上下文另一个是测试入口DialogueTester.cs挂在场景里触发对话。先看配置部分。为了不把 Key 写死在代码里我用一个可序列化的配置类在 Inspector 里填。这样你换 Key 或换模型不用改代码。在AIDialogueManager.cs顶部定义using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.Networking; [Serializable] public class AIConfig { public string baseUrl https://taotoken.net/api; public string apiKey ; public string modelName deepseek-chat; public int maxTokens 512; public float temperature 0.7f; }注意baseUrl我填的是https://taotoken.net/api没有带/v1。请求路径在代码里拼。apiKey留空等会儿在 Inspector 里填。modelName默认deepseek-chat你想换模型只改这个字符串。接下来是消息结构。对话上下文用一个ListMessage维护每条消息有role和content。为了能被JsonUtility序列化字段必须是 public[Serializable] public class ChatMessage { public string role; public string content; } [Serializable] public class ChatRequest { public string model; public int max_tokens; public float temperature; public ListChatMessage messages; } [Serializable] public class ChatChoice { public ChatMessage message; } [Serializable] public class ChatResponse { public ChatChoice[] choices; }这里有个坑JsonUtility对数组和列表的支持有限ChatResponse里的choices用数组没问题但ChatRequest里的messages用ListChatMessage在序列化时是 OK 的。不过JsonUtility不支持字典和嵌套的复杂泛型所以结构尽量扁平。然后是核心的请求协程。完整的管理器脚本如下你可以直接复制using System; using System.Collections; using System.Collections.Generic; using System.Text; using UnityEngine; using UnityEngine.Networking; public class AIDialogueManager : MonoBehaviour { public AIConfig config new AIConfig(); public int maxContextMessages 10; private ListChatMessage context new ListChatMessage(); private bool isRequesting false; public bool IsRequesting isRequesting; void Awake() { if (string.IsNullOrEmpty(config.apiKey)) { Debug.LogWarning(API Key 未配置请在 Inspector 中填写。); } } public void SendMessage(string userInput, Actionstring onReply, Actionstring onError null) { if (isRequesting) { onError?.Invoke(上一条请求还在处理中请稍候。); return; } StartCoroutine(SendRoutine(userInput, onReply, onError)); } private IEnumerator SendRoutine(string userInput, Actionstring onReply, Actionstring onError) { isRequesting true; context.Add(new ChatMessage { role user, content userInput }); TrimContext(); var requestBody new ChatRequest { model config.modelName, max_tokens config.maxTokens, temperature config.temperature, messages context }; string jsonBody JsonUtility.ToJson(requestBody); string url config.baseUrl.TrimEnd(/) /v1/chat/completions; using (UnityWebRequest request new UnityWebRequest(url, POST)) { byte[] bodyRaw Encoding.UTF8.GetBytes(jsonBody); request.uploadHandler new UploadHandlerRaw(bodyRaw); request.downloadHandler new DownloadHandlerBuffer(); request.SetRequestHeader(Content-Type, application/json); request.SetRequestHeader(Authorization, Bearer config.apiKey); request.timeout 30; yield return request.SendWebRequest(); if (request.result UnityWebRequest.Result.ConnectionError || request.result UnityWebRequest.Result.ProtocolError) { string err $HTTP {request.responseCode}: {request.error}\n{request.downloadHandler.text}; Debug.LogError(err); context.RemoveAt(context.Count - 1); onError?.Invoke(err); } else { string responseJson request.downloadHandler.text; ChatResponse response null; try { response JsonUtility.FromJsonChatResponse(responseJson); } catch (Exception e) { onError?.Invoke(解析响应失败: e.Message); isRequesting false; yield break; } if (response ! null response.choices ! null response.choices.Length 0) { string reply response.choices[0].message.content; context.Add(new ChatMessage { role assistant, content reply }); TrimContext(); onReply?.Invoke(reply); } else { onError?.Invoke(响应中没有 choices 内容。); } } } isRequesting false; } private void TrimContext() { while (context.Count maxContextMessages) { context.RemoveAt(0); } } public void ClearContext() { context.Clear(); } }这个脚本里TrimContext负责控制上下文长度默认保留最近 10 条消息避免 token 无限增长。isRequesting防止重复点击发送导致并发请求。错误处理里把responseCode和响应体都打出来方便排查。再写一个测试入口挂在场景的 GameObject 上using UnityEngine; public class DialogueTester : MonoBehaviour { public AIDialogueManager dialogueManager; void Start() { if (dialogueManager null) { dialogueManager FindObjectOfTypeAIDialogueManager(); } dialogueManager.SendMessage( 你好请用一句话介绍你自己。, reply Debug.Log(NPC 回复: reply), err Debug.LogError(请求失败: err) ); } }把AIDialogueManager和DialogueTester都挂到场景里同一个 GameObject 上或者分别挂、用 Inspector 拖引用。然后在AIDialogueManager的 Inspector 面板里把Config展开填入你的 TaoToken API Key确认Base Url是https://taotoken.net/apiModel Name是deepseek-chat。如果你用的是新版 UnityFindObjectOfType可能提示过时可以换成FindFirstObjectByTypeAIDialogueManager()。这个不影响运行只是警告。4. 在 Unity Editor 中运行验证对话响应配置填好之后点 Unity 顶部的播放按钮。如果一切正常Console 窗口会先打印出请求的 JSON然后打印NPC 回复: ...。第一次跑的时候我建议把maxTokens设小一点比如 128这样响应快方便确认链路通不通。如果 Console 里出现HTTP 401说明 Key 有问题。检查 Inspector 里Api Key是不是复制完整了有没有多余空格。TaoToken 的 Key 一般以特定前缀开头复制的时候注意别漏字符。如果出现HTTP 404大概率是 URL 拼错了。确认Base Url是https://taotoken.net/api代码里拼的是/v1/chat/completions合起来就是https://taotoken.net/api/v1/chat/completions。你可以把完整 URL 打印出来核对Debug.Log(请求地址: url);如果出现HTTP 400通常是请求体格式问题。比如model字段填了不存在的模型名或者messages为空。检查modelName是不是deepseek-chat以及context里至少有一条 user 消息。验证单轮通过后可以测多轮上下文。把DialogueTester改成连续发两条void Start() { dialogueManager.SendMessage(我叫小明。, r1 { Debug.Log(第一轮: r1); dialogueManager.SendMessage(我叫什么名字, r2 Debug.Log(第二轮: r2), e Debug.LogError(e)); }, e Debug.LogError(e)); }如果第二轮能正确回答“小明”说明上下文管理生效了。这里要注意SendMessage是异步的必须等第一轮回调回来再发第二轮不能直接连着调否则会被isRequesting拦掉。再进一步你可以搭一个简易 UI。在 Canvas 下建一个InputField和一个Text再加一个 Button。写个DialogueUI.csusing UnityEngine; using UnityEngine.UI; public class DialogueUI : MonoBehaviour { public AIDialogueManager manager; public InputField inputField; public Text outputText; public Button sendButton; void Start() { sendButton.onClick.AddListener(OnSend); } void OnSend() { string text inputField.text; if (string.IsNullOrEmpty(text)) return; inputField.text ; outputText.text 思考中...; manager.SendMessage(text, reply outputText.text reply, err outputText.text 出错: err); } }把 UI 元素拖到对应字段运行后输入文字点发送就能看到 NPC 的实时回复。实测下来DeepSeek 的中文回复速度在 1 到 3 秒左右体感上可以接受。如果觉得慢可以把maxTokens调小或者把temperature调低让输出更稳定。5. 常见报错排查401、local proxy failed、reading choices接入过程中最容易卡住的几个报错我按实际遇到的频率排一下。401 Unauthorized。这是最常见的。原因通常是 Key 没填、填错、或者带了多余字符。检查 Inspector 里Api Key字段确认没有前后空格。另外如果你把 Key 存在环境变量里再读进来注意 Unity Editor 不会自动加载系统环境变量得用System.Environment.GetEnvironmentVariable并且重启 Editor。最稳妥还是直接在 Inspector 填。如果确认 Key 没问题还是 401到 TaoToken 控制台看看这个 Key 是不是被禁用或删除了。local proxy failed / Cannot connect to destination host。这个报错说明 Unity 根本没连上服务器。先确认你的网络能正常访问https://taotoken.net/api可以在浏览器里打开文档页试试。如果浏览器能开但 Unity 报这个错检查 Unity 的 Player Settings 里有没有配代理或者系统代理设置干扰了UnityWebRequest。另外request.timeout设太短也可能导致连接超时我一般设 30 秒。reading choices 相关报错。这个通常出现在解析响应的时候比如JsonUtility解析失败或者choices数组为空。先看 Console 里打印的原始响应体。如果响应体是{error:{message:...}}这种格式说明请求被服务端拒绝了错误信息在error.message里。你可以临时加一段打印Debug.Log(原始响应: responseJson);如果响应体正常但choices为空可能是max_tokens设得太小模型还没输出就截断了。把maxTokens调到 256 以上再试。还有一种情况是JsonUtility对字段名大小写敏感服务端返回的字段必须和你的类字段完全一致。choices、message、content这些都要核对。OAuth 相关报错。如果你在别的地方配过 OAuth 或者用了某些 CLI 工具可能会看到 token 过期之类的提示。Unity 这边用的是 Bearer Key不涉及 OAuth 流程。如果你同时用 Claude Code 或 Codex 这类工具注意它们的认证文件和 Unity 脚本是分开的。比如 Codex 的auth.json里存的是它自己的凭据不要和 Unity 的 Key 混用。CC Switch 这类工具切换的是 CLI 环境的配置不影响 Unity 工程里的 Inspector 字段。请求发出但回调没进。检查SendMessage是不是在isRequesting为 true 的时候被调用了。加个日志Debug.Log(isRequesting: isRequesting);如果确实是并发问题等上一条回调完成再发。另外UnityWebRequest在using块里yield return是安全的但如果你在协程外部Dispose了 request就会出问题。按上面的脚本写法不会踩这个坑。模型名错误。如果你把modelName改成了别的比如deepseek-reasoner要确认 TaoToken 那边支持这个模型 ID。不支持的模型会返回 400 或 404。最稳的办法是先用文档里列出的模型名跑通再尝试其他。6. 继续扩展多模型切换与长期编码通道单轮对话跑通之后你可以把AIDialogueManager做成 Prefab每个 NPC 挂一个各自维护独立的context。这样不同 NPC 有各自的记忆不会串。如果 NPC 数量多注意控制同时请求的数量避免瞬间打满。想换模型的时候只改 Inspector 里的Model Name就行。比如你想试试别的对话模型把deepseek-chat换成对应 IDBase URL 和 Key 都不用动。这就是统一 Key 通道的好处。如果你需要查看当前可用的模型列表和参数说明文档页在 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite 。对于需要长期跑编码任务或者 Agent 流程的场景比如让 AI 帮你生成 NPC 对话树、批量写任务文本可以看看 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite 。它和游戏内实时对话是互补的一个偏开发期辅助一个偏运行期交互。如果你只是想快速验证某个模型的效果不想写代码可以用模型对话页面直接聊 https://taotoken.net/model-chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite 。把提示词调好之后再搬到 Unity 里效率更高。最后提醒一句API Key 不要提交到版本控制。可以在.gitignore里排除存 Key 的配置文件或者用 Unity 的ScriptableObject存配置并且把该资源文件加入忽略列表。工程里只保留一个空的配置模板每个人本地填自己的 Key。这样多人协作时不会互相覆盖也不会泄露凭据。