Unity 接入 DeepSeek 实现 AI 对话功能

Unity 接入 DeepSeek 实现 AI 对话功能

了解DeepSeek

要了解如何编写接入 DeepSeek 的代码和设置相关参数,首先要来学习它的文档。DeepSeek 的官方 API 文档地址是:https://api-docs.deepseek.com/zh-cn/我们希望使用它的对话 API,文档中给出了一些示例:
在这里插入图片描述
文档没有提供 C# 脚本示例,不过它提供了重要参数的信息,之后我们可以让 AI 协助补全脚本。在“对话补全”(https://api-docs.deepseek.com/zh-cn/api/create-chat-completion)中,提供了更详细的参数含义和示例。
在这里插入图片描述

重要准备

(1) API Key要在 Unity 中调用 DeepSeek,首先需要申请一个 API Key。申请地址为:https://platform.deepseek.com/api_keys注意:成功申请 Key 后,要在本地妥善保存。DeepSeek 后台只能查看 Key 的列表和 token 余额等信息,完整的 Key 只会在申请成功后显示一次。

(2) API 请求地址我们希望使用 DeepSeek 的对话补全功能,它的 API 请求地址是:https://api.deepseek.com/v1/chat/completions

(3) token每个 DeepSeek 账号有官方赠送的 10 元余额,可以用它来兑换 token。token 是模型用来表示自然语言文本的基本单位,也是我们的计费单元,可以直观的理解为“字”或“词”;通常 1 个中文词语、1 个英文单词、1 个数字或 1 个符号计为 1 个 token。一般情况下模型中 token 和字数的换算比例大致如下:1 个英文字符 ≈ 0.3 个 token。1 个中文字符 ≈ 0.6 个 token。DeepSeek 目前的收费标准如下:

在这里插入图片描述
建议大家根据自己的需求来估算消耗的 token 和费用,在控制台可以查看余额:https://platform.deepseek.com/
示例中,我们使用 Unity-Chan 模型作为对话角色,该模型可以从 Unity 资源商店中免费下载。同时,还需要准备简单的 UI,如图:
在这里插入图片描述
下方的提问框是用 InputField 组件制作的,右上方的回答框是用 ScrollView 组件搭配 Text 组件制作的。为了方便显示更多内容,要将 ScrollView 的窗口做大一些。美观起见,隐藏了滚动条。

我尝试了用 DeepSeek 来生成相关脚本,最初生成的脚本有可能报错,这时就要根据报错内容让 AI 来进行修改,要求它严格遵循官方文档来编写。首先是对话管理器脚本:

using UnityEngine;
using UnityEngine.Networking;
using System.Collections;
using System.Collections.Generic;

public class DeepSeekDialogueManager : MonoBehaviour
{
    // API配置
    [Header("API Settings")] [SerializeField]
    private string apiKey = "在此处填入你申请的API密钥"; // DeepSeek API密钥

    [SerializeField] private string modelName = "deepseek-chat"; // 使用的模型名称
    [SerializeField] private string apiUrl = "https://api.deepseek.com/v1/chat/completions"; // API请求地址

    // 对话参数
    [Header("Dialogue Settings")] [Range(0, 2)]
    public float temperature = 0.7f; //控制生成文本的随机性(0-2,值越高越随机)

    [Range(1, 1000)] public int maxTokens = 150; //生成的最大令牌数(控制回复长度)

    // 角色设定
    [System.Serializable]
    public class NPCCharacter
    {
        public string name;

        [TextArea(3, 10)]
        public string personalityPrompt = "你是虚拟人物Unity-Chan,是个性格活泼,聪明可爱的女生。擅长Unity和C#编程知识。"; // 角色设定提示词    }
    }

    [SerializeField] public NPCCharacter npcCharacter;

// 回调委托,用于异步处理API响应
    public delegate void DialogueCallback(string response, bool isSuccess);

    /// <summary>    /// 发送对话请求    /// </summary>
    /// <param name="userMessage">玩家的输入内容</param>
    /// <param name="callback">回调函数,用于处理API响应</param>
    public void SendDialogueRequest(string userMessage, DialogueCallback callback)
    {
        StartCoroutine(ProcessDialogueRequest(userMessage, callback));
    }

    /// <summary>    /// 处理对话请求的协程
    /// </summary>    /// <param name="userInput">玩家的输入内容</param>
    /// <param name="callback">回调函数,用于处理API响应</param>
    private IEnumerator ProcessDialogueRequest(string userInput, DialogueCallback callback)
    {
        // 构建消息列表,包含系统提示和用户输入
        List<Message> messages = new List<Message>
        {
            new Message { role = "system", content = npcCharacter.personalityPrompt },
            // 系统角色设定
            new Message { role = "user", content = userInput } // 用户输入
        };
// 构建请求体
        ChatRequest requestBody = new ChatRequest
        {
            model = modelName, // 模型名称
            messages = messages, // 消息列表
            temperature = temperature, // 温度参数
            max_tokens = maxTokens // 最大令牌数
        };
        string jsonBody = JsonUtility.ToJson(requestBody);
        Debug.Log("Sending JSON: " + jsonBody); // 调试用,打印发送的JSON数据
        // 创建UnityWebRequest并发送请求
        UnityWebRequest request = CreateWebRequest(jsonBody);
        yield return request.SendWebRequest();
        // 检查请求是否出错
        if (IsRequestError(request))
        {
            Debug.LogError($"API Error: {request.responseCode}\n{request.downloadHandler.text}");
            callback?.Invoke(null, false);
            yield break;
        }

        DeepSeekResponse response = ParseResponse(request.downloadHandler.text);
        if (response != null
            && response.choices.Length > 0)
        {
            string npcReply = response.choices[0].message.content;
            callback?.Invoke(npcReply, true);
        }

        else
        {
            callback?.Invoke(name + "(陷入沉默)", false);
        }
    }

    /// <summary>
    /// 创建UnityWebRequest对象
    /// </summary>
    /// <param name="jsonBody">请求体的JSON字符串</param>
    /// <returns>配置好的UnityWebRequest对象</returns>
    private UnityWebRequest CreateWebRequest(string jsonBody)
    {
        byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonBody);
        var request = new UnityWebRequest(apiUrl, "POST");
        request.uploadHandler = new UploadHandlerRaw(bodyRaw); // 设置上传处理器
        request.downloadHandler = new DownloadHandlerBuffer(); // 设置下载处理器
        request.SetRequestHeader("Content-Type", "application/json"); // 设置请求头
        request.SetRequestHeader("Authorization", $"Bearer {apiKey}"); // 设置认证头
        request.SetRequestHeader("Accept", "application/json"); // 设置接受类型
        return request;
    }

    /// <summary>
    /// 检查请求是否出错
    /// </summary>
    /// <param name="request">UnityWebRequest对象</param>
    /// <returns>如果请求出错返回true,否则返回false</returns>
    private bool IsRequestError(UnityWebRequest request)
    {
        return request.result == UnityWebRequest.Result.ConnectionError ||
               request.result == UnityWebRequest.Result.ProtocolError ||
               request.result == UnityWebRequest.Result.DataProcessingError;
    }

    /// <summary>
    /// 解析API响应
    /// </summary>
    /// <param name="jsonResponse">API响应的JSON字符串</param>
    /// <returns>解析后的DeepSeekResponse对象</returns>
    private DeepSeekResponse ParseResponse(string jsonResponse)
    {
        try
        {
            return JsonUtility.FromJson<DeepSeekResponse>(jsonResponse);
        }
        catch (System.Exception e)
        {
            Debug.LogError($"JSON解析失败: {e.Message}\n响应内容:{jsonResponse}");
            return null;
        }
    }

// 可序列化数据结构
    [System.Serializable]
    private class ChatRequest
    {
        public string model; // 模型名称
        public List<Message> messages; // 消息列表
        public float temperature; // 温度参数
        public int max_tokens; // 最大令牌数
    }

    [System.Serializable]
    public class Message
    {
        public string role; // 角色(system/user/assistant)
        public string content; // 消息内容
    }

    [System.Serializable]
    private class DeepSeekResponse
    {
        public Choice[] choices; // 生成的选择列表
    }

    [System.Serializable]
    private class Choice
    {
        public Message message; // 生成的消息
    }
}

API 设置

API Key:你申请的 API 密钥
Model Name:希望使用的模型。一般情况下用 deepseek-chat 即可,如需深度推理,可使用 deepseek-reasoner。
API Url:https://api.deepseek.com/v1/chat/completions

对话设置

Temperature:采样温度,介于 0 和 2 之间。更高的值,如 0.8,会使输出更随机,而更低的值,如 0.2,会使其更加集中和确定。可参考官方文档的推荐(https://api-docs.deepseek.com/zh-cn/quick_start/parameter_settings)进行设置,本例中设为 0.7。
Max Tokens:介于 1 到 8192 间的整数,限制一次请求中模型生成 completion 的最大 token 数。输入 token 和输出 token 的总长度受模型的上下文长度的限制。本例中设为 150。

NPC Character

Name:角色姓名,本例中设为 “Unity-Chan”。
Personality Prompt:性格描述。本例中设为“你是虚拟人物 Unity-Chan,一个性格活泼,聪明可爱的女生,擅长 Unity 和 C# 编程知识。
”第二个脚本是角色互动脚本,它用来显示 AI 生成的回复。

public class NPCInteraction : MonoBehaviour
{
    [Header("References")] [SerializeField]
    private DeepSeekDialogueManager dialogueManager; //对话管理器

    [SerializeField] private InputField inputField; //玩家问题输入框
    [SerializeField] private Text dialogueText; //角色回复的文本内容
    private string characterName;
    [Header("Settings")] [SerializeField] private float typingSpeed = 0.05f; // 打字机效果的字符显示速度

    void Start()
    {
        characterName = dialogueManager.npcCharacter.name; //角色姓名赋值
        //输入框提交后执行的回调函数
        inputField.onSubmit.AddListener((text) =>
        {
            dialogueManager.SendDialogueRequest(text, HandleAIResponse); //发送对话请求到DeepSeek AI
        });
    }
/// <summary>
/// 处理AI的响应
/// </summary>
/// <param name="response">AI的回复内容</param>
/// <param name="success">请求是否成功</param>
    private void HandleAIResponse(string response, bool success)
    {
        StartCoroutine(
            TypewriterEffect(success ? characterName + ":" + response : characterName + ":(通讯中断)")); //启动打字机效果协程
    }
/// <summary>
/// 打字机效果协程
/// </summary>
/// <param name="text">角色的回复内容</param>
/// <returns></returns>
    private IEnumerator TypewriterEffect(string text)
    {
        string currentText = ""; //当前显示的文本
        foreach (char c in text) //遍历每个字符
        {
            currentText += c; //添加字符到当前文本
            dialogueText.text = currentText; //更新显示文本
            yield return new WaitForSeconds(typingSpeed); //等待一定时间
        }
    }
}

将这个脚本挂在角色模型上,并给参数赋值。

Reference

Dialogue Manager:拖入挂载了对话控制器的物体。
Input Field:拖入提问对话框对应的 InputField 控件。
Dialogue Text:拖入回复对话框对应的 Text 控件。

Settings

Typing Speed:打字机效果的字符显示速度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值