【AI学习笔记】基于Unity+DeepSeek开发的一些BUG记录解决方案

news2024/11/28 12:44:57

【AI学习笔记】基于Unity+DeepSeek开发的一些BUG记录&解决方案

背景前摇:(省流可不看)

Unity是大学学的,AI是研究生学的,DeepSeek是第一份实习偷师的,三合一的梦是最近开始做的,BUG是今天遇到并且解决的。

关于Unity和大模型结合的教程网上并不多,正好符合Unity+DeepSeek的我目前只看到这一篇《【Unity+AI01】在Unity中调用DeepSeek大模型!实现AI对话功能!》:https://blog.csdn.net/leoysq/article/details/139547284
传送门
阅读全文后,发现这篇文章工程量不大,涉及的知识点(UGUI,C#编程,DeepSeek的API接入)都是我目前比较熟悉的,这个项目可以跟着做,于是就愉快地开始了尝试。
在复现工程的时候我遇到了一些奇奇怪怪的问题,查了CSDN和百度以后发现相关的解释帖子还很少,决定自己开个帖子记录下来。

————————————————————————————————

BUG集锦:

Unity版本:Unity6

1.Unity报错Error: HTTP/1.1 401 Unauthorized

解决方法:看看API有没有写对。
UnityEngine.Debug:LogError (object)
DeepSeekChat/d__8:MoveNext () (at Assets/Scripts/DeepSeekChat.cs:93)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr),
这种联网报错我处理经验不多,看红感叹号出来人都麻了,GPT查了一下,这个报错表示未授权
在这里插入图片描述
上网搜了一圈,没有我这种情境下报错的解决方案,我先在原作者的评论区下方留言等待好心人答复,也厚着脸皮通过DeepSeek官网的聊天渠道反馈了BUG。
很快我就收到了邮件和聊天界面双重回复——未授权一般是API没写对
在这里插入图片描述
我觉得DeepSeek的这一点做得特别好,聊天界面和邮件都可以看到过往记录并且回复,这样沟通就很方便自由。
在这里插入图片描述
我检查了一下我的代码,确实,API抄错了,写成隔壁智谱清言的了。
同时我也获得了教程原作者的友善回复:
在这里插入图片描述

2.base_url连接报错Error: HTTP/1.1 404 Not Found

解决方法:根据需要,换形如https://api.deepseek.com/chat/completions 这样的完整接口地址
UnityEngine.Debug:LogError (object)
DeepSeekChat/d__8:MoveNext () (at Assets/Scripts/DeepSeekChat.cs:94)
UnityEngine.SetupCoroutine:InvokeMoveNext (System.Collections.IEnumerator,intptr)
当时在跟着写代码的时候,我就发现一件很奇怪的事情——原教程作者写的url很特别。
我之前用Python+DeepSeek的时候,用的是官网的base_url,一切都很顺利。
https://platform.deepseek.com/api-docs/zh-cn/
DeepSeek官网传送门
在这里插入图片描述
但是我发现,如果Unity里面也用这个链接,Unity会报错。
在这里插入图片描述
但如果用教程作者老师的这个我没见过的“野生”链接,就没事。
在这里插入图片描述
在这里插入图片描述
这我就不明白了,因为我之前用python做过DeepSeek开发,一直照着官方文档这么用都没问题。
于是我又厚着脸皮请教了DeepSeek的老师们。
在这里插入图片描述
很快我得到了详细的答案:
在这里插入图片描述
我理解的一句话总结是:使用Openai python sdk最省事,base_url能应付所有问题。
在这里插入图片描述在这里插入图片描述
但如果是其他平台,就得根据功能需要换具体的链接了。
比如我现在用Unity做的功能可以归类为【对话补全】,那么就要需要这个页面。
在这里插入图片描述
https://platform.deepseek.com/api-docs/zh-cn/api/create-chat-completion/
传送门
复制下图红框所示的链接,也就是原教程的”野生“链接:
在这里插入图片描述
在这里插入图片描述
这样就可以在Unity里顺利调用DeepSeek的API啦!

3.TextMeshPro不显示AI的回复

解决方案:看看UGUI的排列、颜色
按代码逻辑,成功发送消息以后,AI应该回复,并且在背景板上显示出来。
但现在什么也没有,我首先排除了最不应该的UI层级错误(背景白色image把text盖住了),不是这个原因。
在这里插入图片描述
然后也Debug.Log了各个核心环节,发现AI其实回复了,但我的TextMeshPro没有正常显示内容。
在这里插入图片描述
所以大概BUG是从下面这一步开始的。
在这里插入图片描述
我按GPT给的办法加了个存在与否的判断,但我感觉不该是这种错误,因为确实把按钮拖进了脚本对应位置,要是没找到对象的话早ERROR了。
在这里插入图片描述
在这里插入图片描述
我觉得还是UI本身的问题,想着会不会还是我层级搞错了,于是就尝试把image隐藏看看是不是字在背后,结果——
破案了,原来是白色字体和背景融为一体了……
啥好玩意默认字体是白色啊,我以为默认黑色字来着,真是给我整的哭笑不得。
在这里插入图片描述
于是我给image换了个底色,问题解决了。
在这里插入图片描述
以及,因为要显示聊天记录,这个TextMeshPro不会在发信息后清空原来的内容,要美观的话记得把默认的New Text删除了。

4.安装Unity的可集成HTTP请求库(原作者的NuGet For Unity插件)

解决方法:从GitHub下载NuGet For Unity插件:https://github.com/GlitchEnzo/NuGetForUnity

Unity的HTTP请求库有UnityWebRequest,或第三方库如LitJson、Newtonsoft.Json等,可以将这三者类比为Python的Request、BeautifulSoup、Scrapy库,它们可以实现(指定数据格式)+(指定目标网页)的情况下爬取网站内容的任务
-UnityWebRequest:Unity内置的HTTP客户端库,不需要额外安装,集成在Unity引擎中。其他的第三方库需要自己安装,比如原作者使用的NuGet For Unity。
在这里插入图片描述
第一步的下载链接:https://github.com/GlitchEnzo/NuGetForUnity
传送门
这个下载下来是个Unitypackage,直接导入Unity就行。
第4步的详细操作如下:
在这里插入图片描述

其他可能的优化方案

已实现:

1.点击Send按钮以后自动清空InputField的内容。

2.对话界面也加上用户的输入。

在这里插入图片描述
未实现:

1.显示历史记录的TextMeshPro添加滚轮滑动条。

说来惭愧,学了这么久Unity,做个滑动查看的文本框还一直没有清晰的解决方案。
知道要加Scrollbar组件,并且把TextMeshPro这个要滑动查看的内容放在Content下面,但是经常不显示滑动效果。这历史遗留问题都很久了,我都还不是很确定该怎么做。
UGUI这个操作我会:在这里插入图片描述
今天在GPT的帮助下加了段代码,算是勉强实现了能滑动的功能,不过不是很美观,只能说将就。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
而且如果输入的文本太长了,还是会出现滑动条显示不全的问题,并且Unity会报错:Assertion failed on expression: ‘!(o->TestHideFlag(Object::kDontSaveInEditor) && (options & kAllowDontSaveObjectsToBePersistent) == 0)’
UnityEngine.GUIUtility:ProcessEvent (int,intptr,bool&)
在这里插入图片描述
在这里插入图片描述
这个问题我目前还不知道怎么解决。

2.加入中文字库允许中文交互。

中文字体这个网上有很多解决方案,我之前查了一下觉得很麻烦,暂时没精力(懒得)搞。
一句话我的理解就是要自己导入个字体,而且要尽可能包含多的中文字,不然遇到一个生字就会显示为下图这种方框。
在这里插入图片描述
第二份实习做过一些编辑工作,使用了两款商用字体:中文——阿里普惠体,英文——Monsterrate(拼写不确定),有精力的码友可以试试这两款字体。
————————————————————————————————————————————

添加注释&功能后的完整代码:

工程我发到了CSDN个人资源,有需要的朋友可以自行下载。
https://download.csdn.net/download/bailichen800/89822761
传送门

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using UnityEngine.Networking;
using Newtonsoft.Json;
using System.Text;
using TMPro;
using UnityEngine.UI;

public class DeepSeekChat : MonoBehaviour
{
    //参考链接:https://blog.csdn.net/leoysq/article/details/139547284

    //DeepSeek API配置
    private string apikey = "你的API"; //注意不要粘贴成别家api了
    private string apiURL = "https://api.deepseek.com/chat/completions";
    // private string apiURL = "https://api.deepseek.com";

    //Unity UI元素
    public TMP_InputField userInputField;
    public TextMeshProUGUI chatOutputText;
    public Button sendButton;
    public ScrollRect scrollRect; // 引用 ScrollRect 组件

    //存储对话历史
    private List<Dictionary<string, string>> messages = new List<Dictionary<string, string>>();

    // Start is called once before the first execution of Update after the MonoBehaviour is created
    void Start()
    {
        // 配置 ScrollRect
        scrollRect.content = chatOutputText.rectTransform; // 将 TextMeshProUGUI 的 RectTransform 设置为 Content
        scrollRect.vertical = true; // 启用垂直滚动
        if (sendButton != null)
        {
            sendButton.onClick.AddListener(OnSendButtonClicked);
            Debug.Log("Click Send Button!");
            
        }
        //初始化系统消息
        messages.Add(new Dictionary<string, string> { { "role", "system" }, { "content", "You are a helpful assistant." } });
    }


    public void OnSendButtonClicked()
    {
        //用户没说话,return
        string userMessage = userInputField.text;
        if (string.IsNullOrEmpty(userMessage))
        {
            Debug.Log("Empty Input!");
            return;
        }
        //显示响应
        if (chatOutputText != null)
        {
            chatOutputText.text += "\nUser: " + userMessage;//将AI的回复追加到聊天输出文本框中,以显示给用户。
        }
        else
        {
            Debug.LogError("TextMeshPro component not assigned.");
        }
        // 清空输入框
        userInputField.text = "";

        //添加用户消息到对话历史
        messages.Add(new Dictionary<string, string> { { "role", "user" }, { "content", userMessage } });

        //调用DeepSeek API
        StartCoroutine(CallDeepSeekAPI());
    }

    private IEnumerator CallDeepSeekAPI()
    {
        Debug.Log("CallDeepSeekAPI!");
        //创建请求数据
        var requestData = new  //定义一个匿名对象requestData
        {
            model = "deepseek-chat", //表示使用的模型,这里是"deepseek-chat"
            messages = messages, //一个变量,应该是一个包含消息的数组,用于与聊天模型交互
            stream = false //一个布尔值,表示是否以流的形式接收响应,这里设置为false
        };

        string jsonData = JsonConvert.SerializeObject(requestData); //将requestData对象序列化为JSON格式的字符串jsonData。

        //创建UnityWebRequest 向指定的API URL发送一个POST请求,请求体为JSON格式的数据,并设置必要的请求头
        UnityWebRequest request = new UnityWebRequest(apiURL, "POST"); //创建一个网络请求对象request,指定请求的URL和请求方法(POST)
        byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData); //将序列化后的JSON数据转换为字节数组bodyRaw
        request.uploadHandler = new UploadHandlerRaw(bodyRaw); //创建一个UploadHandlerRaw对象,并将其赋值给request的uploadHandler属性,这样就将JSON数据作为请求体发送
        request.downloadHandler = new DownloadHandlerBuffer(); //创建一个DownloadHandlerBuffer对象作为request的downloadHandler,用于接收服务器响应的数据
        request.SetRequestHeader("Content-Type", "application/json");  //通过SetRequestHeader方法设置请求的Content-Type为application/json,表明发送的是JSON数据
        request.SetRequestHeader("Authorization", "Bearer " + apikey); //同时设置Authorization请求头,包含一个API密钥apiKey,用于身份验证。

        //发送请求  发送一个网络请求到服务器,获取AI模型的回复,并将该回复显示在聊天界面中,同时更新对话历史。如果请求失败,则输出错误信息。
        yield return request.SendWebRequest(); //使用yield return来异步发送网络请求,而不会阻塞主线程

        if (request.result == UnityWebRequest.Result.Success) //检查网络请求是否成功
        {
            Debug.Log("Success!");
            //解析响应
            var response = JsonConvert.DeserializeObject<DeepSeekResponse>(request.downloadHandler.text); //将下载的响应文本(JSON格式)反序列化为一个DeepSeekResponse类型的对象
            string botMessage = response.choices[0].message.content; //从反序列化后的响应对象中提取AI回复的消息内容。

            Debug.Log("botMessage = " + botMessage);
            //显示响应
            if (chatOutputText != null)
            {
                chatOutputText.text += "\nAI: " + botMessage;//将AI的回复追加到聊天输出文本框中,以显示给用户。
            }
            else
            {
                Debug.LogError("TextMeshPro component not assigned.");
            }
            //添加AI消息到对话历史
            messages.Add(new Dictionary<string, string> { { "role", "assistant" }, { "content", botMessage } }); // 将AI的回复添加到消息列表中
        }
        else
        {
            Debug.LogError("Error: " + request.error);
        }
    }

    // 使用这些类,Unity将能够将JSON响应反序列化为一个DeepSeekResponse对象,你可以通过DeepSeekResponse对象访问choices数组,然后访问第一个Choice对象的message属性,最后得到content字符串,即AI的回复。
    [System.Serializable] //标记为[System.Serializable]的公共类,表示它可以被Unity的序列化系统序列化和反序列化。
    public class DeepSeekResponse
    {
        public Choice[] choices; //包含一个名为choices的数组,该数组应该包含多个Choice对象
    }

    [System.Serializable]
    public class Choice
    {
        public Message message; //包含一个名为message的属性,该属性是Message类型
    }

    [System.Serializable]
    public class Message
    {
        public string content; //包含一个名为content的字符串属性,该属性用于存储消息的实际内容
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2189501.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

VRRP协议个人理解+报文示例+典型配置-RFC2338/RFC3768/RFC5798/RFC9568

个人认为&#xff0c;理解报文就理解了协议。通过报文中的字段可以理解协议在交互过程中相关传递的信息&#xff0c;更加便于理解协议。 因此本文将在VRRP协议报文的基础上进行介绍。 VRRP协议发展 关于VRRPv2基本原理&#xff0c;可重点参考2004年发布的RFC3768-Virtual Ro…

【python实操】python小程序之函数的方法和赋值的区别

引言 python小程序之函数的方法和赋值 文章目录 引言一、函数的方法和赋值1.1 题目1.2 代码1.2.1 append方法1.2.2 赋值 1.3 代码解释1.3.1 append方法1.3.2 赋值 二、思考2.1 append方法和赋值的区别2.1.1 append方法2.1.2 赋值操作2.1.3 总结 一、函数的方法和赋值 1.1 题目…

通过freepbx搭建小型电话系统的过程

领导说公司的客服电话需要实现语音导航和非工作时间自动接听播放语音提示的功能。任务自然落到了伟大的程序员的头上&#xff0c;本着为公司节约成本原则遂百度了一番&#xff0c;找到了asterisk 和freeswitch两个比较流行的电话系统。经过对比和考虑公司的情况选择了asterisk系…

STM32 通用定时器

一、概述 STM32内部集成了多个定时/计数器&#xff0c;根据型号不同&#xff0c;STM32系列芯片最多包含8个定时/计数器。其中&#xff0c;TIM6、TIM7为基本定时器&#xff0c;TIM2~TIM5为通用定时器&#xff0c;TIM1、TIM8为高级控制定时器。 1.定时器的类型 基本定时器通用定…

C/C++ 中的未定义行为(Undefined Behavior, UB)

0. 简介 在 C/C 编程中&#xff0c;理解未定义行为&#xff08;UB&#xff09;及其相关概念至关重要。本文将对未定义行为进行详细解析&#xff0c;并通过实例展示其影响与处理方法。 1. 概念辨析 在 C/C 中&#xff0c;未定义行为容易与以下两个概念混淆&#xff1a; 1.1 …

【Spring】Spring MVC的项目准备和连接建立

文章目录 1. 什么是 Spring Web MVC1.1 MVC 定义1.2 什么是 Spring MVC 2. 学习 Spring MVC2.1 项目准备2.2 建立连接 1. 什么是 Spring Web MVC Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架&#xff0c;从已开是就包含在 Spring 框架中。它的正式名称“Spring We…

【pytorch】张量求导

笔者看到了这篇文章&#xff0c;可以很好的解释张量的求导问题&#xff1a; 看到了上面这张图&#xff0c;可以说很好的表示了前向和反向的过程了。 补充几个细节 之前看李沐的d2l&#xff0c;一直不懂为什么矩阵计算时的一些奇奇怪怪的规定&#xff0c;比如为什么一个行向量…

github项目——gpt-pilot自动创建应用

今天扯一扯在github上看到的一个项目gpt-pilot&#xff0c;声称“首个AI程序员”。本来打算玩一下&#xff0c;结果需要配置大语言模型的API&#xff0c;并且只支持OpenAI和claude&#xff08;Qwen呢&#xff09;。有没有玩过的老哥说一下好不好用&#xff01;&#xff01;(对了…

【Postman】接口测试工具使用

干就完啦 Postman发送get请求案例1&#xff1a; Postman发送post请求案例2 Postman发送其他请求 学习目标&#xff1a;能够使用Postman发送get/post/put/delete请求并获取响应结果 Postman发送get请求 首先postman是一款接口调试工具&#xff0c;支持win&#xff0c;mac以及l…

Python | Leetcode Python题解之第456题132模式

题目&#xff1a; 题解&#xff1a; class Solution:def find132pattern(self, nums: List[int]) -> bool:candidate_i, candidate_j [-nums[0]], [-nums[0]]for v in nums[1:]:idx_i bisect.bisect_right(candidate_i, -v)idx_j bisect.bisect_left(candidate_j, -v)if…

Pandas -----------------------基础知识(六)

目录 数据类型 查看类型 类型转换 无法转换的值返回NaN 无法转换的值返回原值 datetime类型 datetime类型数据列作为df索引 Python中的timedelta类型 Pandas中的timedelta类型 pd.to_timedelta函数转换timedelta类型 timedelta类型数据作为df索引 分组groupby 分箱…

开发环境简单介绍

目录 开发环境keil的安装和使用 keil的介绍 keil的安装 keil的简单使用 STC-ISP的安装 STC-ISP简单介绍 开发环境测试 总结 开发环境keil的安装和使用 keil的介绍 Keil uVision5是一个集成开发环境&#xff08;IDE&#xff09;&#xff0c;用于对嵌入式系统中的微控制器…

vue-scrollto实现页面组件锚点定位

文章目录 前言背景操作指南安装及配置步骤vue组件中使用 参考文章 前言 博主介绍&#xff1a;✌目前全网粉丝3W&#xff0c;csdn博客专家、Java领域优质创作者&#xff0c;博客之星、阿里云平台优质作者、专注于Java后端技术领域。 涵盖技术内容&#xff1a;Java后端、大数据…

Java | Leetcode Java题解之第454题四数相加II

题目&#xff1a; 题解&#xff1a; class Solution {public int fourSumCount(int[] A, int[] B, int[] C, int[] D) {Map<Integer, Integer> countAB new HashMap<Integer, Integer>();for (int u : A) {for (int v : B) {countAB.put(u v, countAB.getOrDefa…

多模态—文字生成图片

DALL-E是一个用于文字生成图片的模型&#xff0c;这也是一个很好思路的模型。该模型的训练分为两个阶段&#xff1a; 第一阶段&#xff1a;图片经过编码器编码为图片向量&#xff0c;当然我们应该注意这个过程存在无损压缩&#xff08;图片假设200*200&#xff0c;如果用one-h…

VBA中类的解读及应用第十六讲:让文本框在激活时改变颜色(中)

《VBA中类的解读及应用》教程【10165646】是我推出的第五套教程&#xff0c;目前已经是第一版修订了。这套教程定位于最高级&#xff0c;是学完初级&#xff0c;中级后的教程。 类&#xff0c;是非常抽象的&#xff0c;更具研究的价值。随着我们学习、应用VBA的深入&#xff0…

数据链路层(以太网简介)

一.以太网数据帧结构&#xff1a; 目的地址&#xff0c;源地址&#xff0c;类型这三个被称为帧头&#xff0c;数据则被称为载荷&#xff0c;CRC则被称为帧尾&#xff08;校验和&#xff09; 二.数据帧结构分析 1.目的地址和源地址 i.地址解释 这两个地址指的是mac地址&#x…

【AIGC】2022-NIPS-视频扩散模型

2022-NIPS-Video Diffusion Models 视频扩散模型摘要1. 引言2. 背景3. 视频扩散模型3.1. 重建引导采样以改进条件生成 4. 实验4.1. 无条件视频建模4.2. 视频预测4.3. 文本条件视频生成4.3.1 视频与图像建模的联合训练4.3.2 无分类器指导的效果4.3.3 更长序列的自回归视频扩展 5…

数通 2

一 网络层 数据传输中最大支持1518字节&#xff0c;所以超过这个一次传不过去&#xff0c;就要分开传&#xff0c;就像快递标记1/2, 2/2 。说明你有两包 下图例子解释了 identification 用于标识一台设备发送的数据 片偏移&#xff0c;就是 你 好 吗 三个分片谁先到达不一定&…

C语言 | Leetcode C语言题解之第457题环形数组是否存在循环

题目&#xff1a; 题解&#xff1a; int next(int* nums, int numsSize, int cur) {return ((cur nums[cur]) % numsSize numsSize) % numsSize; // 保证返回值在 [0,n) 中 }bool circularArrayLoop(int* nums, int numsSize) {for (int i 0; i < numsSize; i) {if (!n…