Unity接入AI—Deepseek,数据流式传输

csharp 复制代码
using System;
using Newtonsoft.Json;
using QFramework;
using System.Collections;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using UnityEngine.UI;

public class AIDeepSeekManager : MonoSingleton<AIDeepSeekManager>
{
    private AIDeepSeekManager()
    {
    }


    private UnityWebRequest currentRequest;
    private string apiKey = "替换为自己的apikey"; // 替换为实际的API密钥
    private string apiEndpoint = "https://api.deepseek.com/v1/chat/completions";

    [TextArea]
    public string systemPrompt = "你的姓名叫小白,性别女生,是一个有感情的,有幽默感的,有创造力的,有思考力的,有同情心的,有爱心的,有责任心,说话温柔的智能体";

    // 使用的模型的 ID :[deepseek-chat, deepseek-reasoner]
    //直接回答
    private string modelNoThink = "deepseek-chat";
    //思考模式,更准确
    private string modelThink = "deepseek-reasoner";


    //是否开启思考模式
    public bool IsOpenThinking = false;

    //是否正在AI生成

    public BindableProperty<bool> IsGenerating = new BindableProperty<bool>(false);


    //AI回复消息回调,完整消息内容
    private Action<string> mCallBack;

    private object CreateRequestData(string strChat)
    {
        StringBuilder sb = new StringBuilder();

        sb.AppendLine($"你的身份:{systemPrompt}");
        sb.AppendLine("请根据用户提问的相关问题进行回答");
        return new
        {
            messages = new[]
            {
                new
                {
                    role = "system",
                    //content = systemPrompt,
                    content = $"{sb.ToString()}",
                },
                new
                {
                    role = "user",
                    content = strChat,
                },
            },
            model = IsOpenThinking?modelThink:modelNoThink,
            thinking = new
            {
                type = IsOpenThinking?"enabled": "disabled",
            },
            frequency_penalty = 0,
            max_tokens = 4096,
            presence_penalty = 0,
            response_format = new
            {
                type = "text",
            },
            stream = true,
            temperature = 0.7,
        };
    }



    public void SendMessage(string message,Action<string> callBack )
    {
        IsGenerating.Value = true;
        mStringBuilder.Clear();
        mCallBack = callBack;

        var requestData = CreateRequestData(message);

        string jsonPayload = JsonConvert.SerializeObject(requestData);
        Debug.Log($"发送的请求内容: {jsonPayload}");

        byte[] rawData = System.Text.Encoding.UTF8.GetBytes(jsonPayload);

        currentRequest = new UnityWebRequest(apiEndpoint, "POST");
        currentRequest.uploadHandler = new UploadHandlerRaw(rawData);
        currentRequest.downloadHandler = new CustomDownloadHandler(ProcessStreamResponse);

        currentRequest.SetRequestHeader("Content-Type", "application/json");
        currentRequest.SetRequestHeader("Accept", "text/event-stream");
        currentRequest.SetRequestHeader("Authorization", $"Bearer {apiKey}");

        currentRequest.timeout = 120; // 120秒超时

        // 启用协程来处理流式响应
        StartCoroutine(SendRequest());
    }

    private IEnumerator SendRequest()
    {
        yield return currentRequest.SendWebRequest();
        if (currentRequest != null)
        {
            if (currentRequest.result == UnityWebRequest.Result.ConnectionError ||
                currentRequest.result == UnityWebRequest.Result.ProtocolError)
            {
                Debug.LogError($"请求错误: {currentRequest.error}");
                Debug.LogError($"状态码: {currentRequest.responseCode}");
                Debug.LogError($"响应内容: {currentRequest.downloadHandler.text}");

                if (currentRequest.responseCode == (int)EnumDeepSeekCode.InsufficientBalance)
                {
                    Debug.LogError("API需要付费,请检查账户余额和API密钥状态");
                }

                IsGenerating.Value = false;
                mCallBack = null;
            }
            else
            {
                ProcessStreamResponse(currentRequest.downloadHandler.text);
            }
        }
    }


    private void ProcessStreamResponse(string responseText)
    {
        string[] lines = responseText.Split('\n');
        foreach (string line in lines)
        {
            if (string.IsNullOrWhiteSpace(line))
                continue;

            if (line.StartsWith("data: "))
            {
                string json = line.Substring(6).Trim();
                if (json == "[DONE]")
                {
                    Debug.Log("流式传输结束");
                    IsGenerating.Value = false;
                    mCallBack = null;
                    continue;
                }

                try
                {
                    var responseData = JsonConvert.DeserializeObject<DeepSeekResponse>(json);
                    if (responseData?.choices?.Length > 0 && !string.IsNullOrEmpty(responseData.choices[0].delta?.content))
                    {
                        ProcessStreamContent(responseData.choices[0].delta.content);
                    }
                }
                catch (JsonException e)
                {
                    Debug.LogError($"解析流式数据时出错: {e.Message}");
                    Debug.LogError($"原始数据: {json}");
                    IsGenerating.Value = false;
                    mCallBack = null;
                }
            }
        }
    }


    private StringBuilder mStringBuilder = new StringBuilder();

    private void ProcessStreamContent(string content)
    {
        mStringBuilder.Append(content);
        Debug.Log($"接收到的内容: {content}");
        mCallBack?.Invoke(mStringBuilder.ToString());
    }


    public void DisposeAI()
    {
        if (currentRequest!=null)
        {
            currentRequest.Abort();
            currentRequest = null;
            IsGenerating.Value = false;
            mCallBack = null;
        }
    }

    private Action<string> mStopCallBack;
    public void StopGenerate(Action<string> callBack=null)
    {
        mStopCallBack = callBack;
        DisposeAI();
        mStopCallBack?.Invoke("当前对话已中断");
        mCallBack = null;
    }
}


public class CustomDownloadHandler : DownloadHandlerScript
{
    private System.Action<string> onReceiveData;
    private System.Text.StringBuilder buffer = new System.Text.StringBuilder();

    public CustomDownloadHandler(System.Action<string> callback) : base()
    {
        onReceiveData = callback;
    }

    protected override bool ReceiveData(byte[] data, int dataLength)
    {
        if (data == null || data.Length < 1 || dataLength < 1)
        {
            return false;
        }

        string content = System.Text.Encoding.UTF8.GetString(data, 0, dataLength);
        buffer.Append(content);

        // 处理缓冲区中的完整行
        ProcessBuffer();

        return true;
    }

    private void ProcessBuffer()
    {
        string content = buffer.ToString();
        int lastNewLine = content.LastIndexOf('\n');

        if (lastNewLine >= 0)
        {
            string completeLines = content.Substring(0, lastNewLine + 1);
            string remaining = content.Substring(lastNewLine + 1);

            string[] lines = completeLines.Split('\n');
            foreach (string line in lines)
            {
                if (!string.IsNullOrWhiteSpace(line))
                {
                    onReceiveData?.Invoke(line + "\n");
                }
            }

            buffer.Clear();
            buffer.Append(remaining);
        }
    }

    protected override void CompleteContent()
    {
        // 处理剩余的数据
        if (buffer.Length > 0)
        {
            onReceiveData?.Invoke(buffer.ToString());
        }
        buffer.Clear();
    }
}
csharp 复制代码
public class DeepSeekResponse
{
    //该对话的唯一标识符。
    public string id { get; set; }
    //模型生成的 completion 的选择列表。
    public Choice[] choices { get; set; }
    //创建聊天完成时的 Unix 时间戳(以秒为单位)。
    public int created { get; set; }

    public string @object { get; set; }
    //该对话补全请求的用量信息。
    public Usage usage { get; set; }
}

public class Choice
{
    //该 completion 在模型生成的 completion 的选择列表中的索引。
    public int index { get; set; }
    //public Message message { get; set; }

    public Message delta { get; set; }

    //模型停止生成 token 的原因。
    public string finish_reason { get; set; }
}

public class Message
{
    //该 completion 的内容。
    public string content { get; set; }
    //生成这条消息的角色。
    public string role { get; set; }
    
}

public class Usage
{
    public int prompt_tokens { get; set; }
    public int completion_tokens { get; set; }
    public int total_tokens { get; set; }
}

//返回码
public enum EnumDeepSeekCode
{
    /// <summary>
    /// 格式错误
    /// </summary>
    FormatError = 400,
    /// <summary>
    /// 认证失败
    /// </summary>
    AuthenticationFailed = 401,
    /// <summary>
    /// 余额不足
    /// </summary>
    InsufficientBalance = 402,
    /// <summary>
    /// 参数错误
    /// </summary>
    ParameterError = 422,
    /// <summary>
    /// 请求速率达到上限
    /// </summary>
    MaximumLimit = 429,
    /// <summary>
    /// 服务器故障
    /// </summary>
    ServerFailure = 500,
    /// <summary>
    /// 服务器繁忙
    /// </summary>
    ServerBusy = 503,
}
相关推荐
美酒没故事°18 小时前
Open WebUI安装指南。搭建自己的自托管 AI 平台
人工智能·windows·ai
鸿乃江边鸟19 小时前
Nanobot 从onboard启动命令来看个人助理Agent的实现
人工智能·ai
本旺19 小时前
【Openclaw 】完美解决 Codex 认证失败
ai·codex·openclaw·小龙虾·gpt5.4
张張40819 小时前
(域格)环境搭建和编译
c语言·开发语言·python·ai
黄思搏20 小时前
基于标注平台数据的 Unity UI 自动化构建工作流设计与工程实践
ui·unity·蓝湖·vectoui
乐鑫科技 Espressif20 小时前
使用 MCP 服务器,把乐鑫文档接入 AI 工作流
人工智能·ai·esp32·乐鑫科技
语戚20 小时前
Stable Diffusion 入门:架构、空间与生成流程概览
人工智能·ai·stable diffusion·aigc·模型
俊哥V20 小时前
每日 AI 研究简报 · 2026-04-08
人工智能·ai
rrrjqy21 小时前
什么是RAG?
ai
Flittly21 小时前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot