Chat AI聊天助手

直接在输入框输入或者下载桌面版解压即可与AI聊天啦

 


public class DeepSeekDialogueManager : MonoBehaviour
{
    /// API配置
    [Header("API设置")]
    [SerializeField] private string apiKey = "apiKey";// DeepSeek API密钥
    [SerializeField] private string modelName = "deepseek-v3-250324";// 使用的模型名称
    [SerializeField] private string apiUrl = "https://ark.cn-beijing.volces.com/api/v3/chat/completions";// API请求地址

    // 对话参数
    [Header("对话参数")]
    /*
    技术问答 / 代码生成	0.1~0.3	答案精准,避免歧义
    客服对话 / 邮件撰写	0.4~0.6	平衡专业性和自然度
    创意写作 / 营销文案	0.7~0.9	输出更生动,但需人工审核
    探索性场景(如诗歌生成) ≥1.0 高风险,可能产生无意义内容
    */
    [Range(0, 2)] public float temperature = 0.5f;// 控制生成文本的随机性(0-1,值越高越随机)
    [Range(1, 1000)] public int maxTokens = 150;// 生成的最大令牌数(控制回复长度)

    // 角色设定
    [System.Serializable]
    public class NPCCharacter
    {
        public string name;
        public string personalityPrompt = "你是一个喜欢怼人的智能机器人";// 角色设定提示词
    }

    [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 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;
            npcReply = npcReply.TrimStart();
            //Debug.Log($"NPC回复:{npcReply}");
            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>
    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;// 生成的消息
    }
}

public class NPCInteraction : MonoBehaviour
{
    //引用和配置
    [Header("References")]
    [SerializeField] private DeepSeekDialogueManager dialogueManager;//对话管理器
    [SerializeField] private InputField inputField;//玩家问题输入框
    [SerializeField] private Text dialogueText;//角色回复的文本内容
    [SerializeField] private GameObject delayText;//等待
    [SerializeField] private Transform content;//对话内容的父物体
    private string characterName;

    [Header("Settings")]
    [SerializeField] private float typingSpeed = 0.05f; // 打字机效果的字符显示速度


    void Start()
    {
        characterName = dialogueManager.npcCharacter.name;//角色姓名赋值
        //输入框提交后执行的回调函数
        inputField.onSubmit.AddListener((text) =>
        {
            string currentText = "<color=red><b>小主</b></color>:";//当前显示的文本
            Text contentText = Instantiate(dialogueText, content);
            contentText.text = currentText + inputField.text;//更新显示文本
            inputField.text = "";//清空输入框

            delayText.SetActive(true);//显示等待提示

            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 = "<color=blue><b>Blue</b></color>";//当前显示的文本
        delayText.SetActive(false);//隐藏等待提示
        inputField.Select();//选中输入框
        inputField.ActivateInputField();
        Text contentText = Instantiate(dialogueText, content);
        foreach (char c in text)//遍历每个字符
        {
            currentText += c;//添加字符到当前文本
            contentText.text = currentText;//更新显示文本
            yield return new WaitForSeconds(typingSpeed);//等待一定时间
        }
    }
}