using Newtonsoft.Json; using System; using System.Collections; using System.Collections.Generic; using System.Text; using System.Text.RegularExpressions; using TMPro; using UnityEngine; using UnityEngine.Networking; using UnityEngine.UI; public class DeepSeekReasonerStreamManager : MonoBehaviour { // API 配置 [Header("API Settings")] [SerializeField] private string apiKey = "sk-c89fd3342dda4a8eb53cb533607e2e89"; // 替换成你的 API Key [SerializeField] private string modelName = "deepseek-reasoner"; [SerializeField] private string apiUrl = "https://api.deepseek.com/v1/chat/completions"; // 推理参数 [Header("Inference Settings")] [Range(0, 2)] public float temperature = 0.7f; [Range(1, 1000)] public int maxTokens = 150; // UI 控件(请在 Inspector 中指定,只需一个 Text 节点) [Header("UI Elements")] public TextMeshProUGUI dialogueText; // 显示推理过程及最终回复 public InfoShow infoShow; // 内部缓冲区 private StringBuilder reasoningBuffer = new StringBuilder(); private StringBuilder contentBuffer = new StringBuilder(); private bool streamingCompleted = false; /// /// 开始流式推理请求,传入对话消息列表(例如第一轮对话) /// /// 对话消息列表,格式参见 DeepSeekMessage public void SendStreamReasonerRequest(List messages) { // 清空之前的缓冲区及 UI 显示 reasoningBuffer.Clear(); contentBuffer.Clear(); dialogueText.text = ""; streamingCompleted = false; StartCoroutine(ProcessStreamReasonerRequest(messages)); // 开启打字机效果协程,先显示推理过程 StartCoroutine(AnimateReasoningText()); } /// /// 流式请求的协程:发送请求并通过自定义下载处理器实时处理返回块 /// private IEnumerator ProcessStreamReasonerRequest(List messages) { ChatStreamRequest requestBody = new ChatStreamRequest { model = modelName, messages = messages, stream = true }; GameManager.Instance.infoTGo.text = "智慧正在酝酿中,请稍候…"; string jsonBody = JsonUtility.ToJson(requestBody); Debug.Log("Sending streaming JSON: " + jsonBody); // 使用自定义 StreamingDownloadHandler 逐块处理响应 StreamingDownloadHandler downloadHandler = new StreamingDownloadHandler(); downloadHandler.onChunkReceived = ProcessChunk; using (UnityWebRequest request = new UnityWebRequest(apiUrl, "POST")) { byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonBody); request.uploadHandler = new UploadHandlerRaw(bodyRaw); request.downloadHandler = downloadHandler; request.SetRequestHeader("Content-Type", "application/json"); request.SetRequestHeader("Authorization", $"Bearer {apiKey}"); request.SetRequestHeader("Accept", "application/json"); yield return request.SendWebRequest(); if (IsRequestError(request)) { Debug.LogError($"API Error: {request.responseCode}\n{request.downloadHandler.text}"); yield break; } } streamingCompleted = true; } /// /// 对每个返回块进行解析,如果存在推理内容则追加到 reasoningBuffer, /// 否则将最终结果追加到 contentBuffer(不直接更新 UI)。 /// private void ProcessChunk(string chunk) { chunk = chunk.Trim(); if (string.IsNullOrEmpty(chunk)) return; if (chunk.Contains("keep-alive")) return; if (chunk.StartsWith("data:")) { chunk = chunk.Substring("data:".Length).Trim(); if (chunk == "[DONE]") return; } try { ChatStreamResponseChunk chunkData = JsonUtility.FromJson(chunk); if (chunkData != null && chunkData.choices != null && chunkData.choices.Length > 0) { StreamingDelta delta = chunkData.choices[0].delta; if (!string.IsNullOrEmpty(delta.reasoning_content)) { reasoningBuffer.Append(delta.reasoning_content); } else if (!string.IsNullOrEmpty(delta.content)) { contentBuffer.Append(delta.content); // 此处不直接更新 dialogueText,待流式完成后统一显示最终结果 } } } catch (Exception e) { Debug.LogError("解析返回块失败: " + e.Message + "\nChunk content: " + chunk); } } /// /// 实现打字机效果:不断检查 reasoningBuffer 中的新字符,并逐个显示到 dialogueText, /// 流式加载结束后,替换显示最终回复内容。 /// private IEnumerator AnimateReasoningText() { string displayed = ""; GameManager.Instance.stopAnimation = false; // 允许执行 while (!streamingCompleted || displayed.Length < reasoningBuffer.Length) { if (GameManager.Instance.stopAnimation) break; // 立即停止 if (displayed.Length < reasoningBuffer.Length) { displayed = reasoningBuffer.ToString().Substring(0, displayed.Length + 1); dialogueText.text = displayed; } yield return new WaitForSeconds(0.05f); } // 推理过程已显示完毕,等待片刻后替换为最终结果 yield return new WaitForSeconds(0.5f); if (!GameManager.Instance.stopAnimation) { yield return new WaitForSeconds(0.5f); linkidResponse(contentBuffer.ToString()); } //dialogueText.text = contentBuffer.ToString(); // linkidResponse(contentBuffer.ToString()); } private void linkidResponse(string response) { try { Dictionary dictionary = JsonConvert.DeserializeObject>(response); infoShow.ShowInfo(dictionary); } catch (System.Exception) { infoShow.ShowInfo( new Dictionary { {"什么是潜艇?","潜艇是一种能够在水下航行的特种舰艇,主要用于军事侦察、战斗巡逻、秘密运输等任务。潜艇利用内部的压载舱调整自身浮力,实现浮出水面或下潜水中的能力。现代潜艇通常配备柴油发动机或核动力系统,能够长时间潜伏在水下执行任务。军事潜艇通常携带鱼雷、导弹或水雷等武器,能够对敌方舰船实施打击。此外,潜艇还可用于海洋科学研究、深海探测和搜救任务。在历史上,潜艇在第一次和第二次世界大战中都发挥了重要作用,尤其是德国的“U型潜艇”对海战产生了深远影响。如今,各国海军普遍装备不同类型的潜艇,如战略核潜艇、攻击型潜艇和常规动力潜艇,以执行各种军事和非军事任务。"}, {"什么是航母?","航空母舰,简称航母,是一种专门用于搭载和起降战斗机的大型水面舰艇。航母通常配备巨大的甲板作为飞机的跑道,并设有升降机、机库等辅助设施,使舰载机能够高效起降和维护。现代航母一般由核动力或常规动力提供动力,拥有强大的续航能力。航母不仅是一种海上军事基地,还是国家海军力量的象征,能够在远离本土的海域实施空中作战任务。其舰载机编队能够执行制空、对海打击、反潜作战、电子战等多种任务。航母战斗群通常由驱逐舰、护卫舰、补给舰和潜艇等护航舰艇组成,形成强大的作战体系。世界主要海军强国,如美国、中国、英国、法国等,都装备有不同级别的航母,并持续发展新型舰载机和航母战斗体系。"} } ); } } // 解析 [] 内的内容,转换成 形式 public string ParseClickableText(string rawText) { return Regex.Replace(rawText, @"\[(.*?)\]", "$1"); } /// /// 检查请求是否出现错误 /// private bool IsRequestError(UnityWebRequest request) { return request.result == UnityWebRequest.Result.ConnectionError || request.result == UnityWebRequest.Result.ProtocolError || request.result == UnityWebRequest.Result.DataProcessingError; } // 可序列化的请求和响应数据结构 [Serializable] public class DeepSeekMessage { public string role; // "user", "assistant", "system" 等 public string content; // 消息内容 } [Serializable] private class ChatStreamRequest { public string model; public List messages; public bool stream; } [Serializable] private class StreamingDelta { public string reasoning_content; public string content; } [Serializable] private class StreamingChoice { public StreamingDelta delta; } [Serializable] private class ChatStreamResponseChunk { public StreamingChoice[] choices; } } /// /// 自定义流式下载处理器:将接收到的数据按行分割,并调用回调处理每一行 JSON 数据 /// public class StreamingDownloadHandler : DownloadHandlerScript { public Action onChunkReceived; private StringBuilder buffer = new StringBuilder(); public StreamingDownloadHandler() : base(new byte[1024]) { } protected override bool ReceiveData(byte[] data, int dataLength) { if (data == null || dataLength <= 0) return false; string chunk = Encoding.UTF8.GetString(data, 0, dataLength); buffer.Append(chunk); // 假设每个 JSON 块以换行符结尾 string fullText = buffer.ToString(); string[] lines = fullText.Split(new[] { "\n" }, StringSplitOptions.None); // 处理除最后一行外的所有完整行 for (int i = 0; i < lines.Length - 1; i++) { string line = lines[i].Trim(); if (!string.IsNullOrEmpty(line)) { onChunkReceived?.Invoke(line); } } // 保留最后可能不完整的那一行 buffer.Clear(); buffer.Append(lines[lines.Length - 1]); return true; } protected override void CompleteContent() { string remaining = buffer.ToString().Trim(); if (!string.IsNullOrEmpty(remaining)) { onChunkReceived?.Invoke(remaining); } base.CompleteContent(); } }