using UnityEngine; using UnityEngine.Networking; using System; using System.Collections; using System.Collections.Generic; using System.Text; namespace LLM { [DisallowMultipleComponent] public class DeepSeekService : MonoBehaviour, ILLMService { [Header("API Settings")] [SerializeField] private string apiKey = "sk-xxxxxxxx"; // 请在 Inspector 填入 [SerializeField] private string apiUrl = "https://api.deepseek.com/chat/completions"; [SerializeField] private string modelName = "deepseek-chat"; [Header("System Prompt")] [TextArea] public string systemPrompt = "你是一个智能助手。"; [Header("Security Settings")] public bool enableDebugKey = false; public string debugKey = "123456"; [SerializeField] private bool autoPatchSystemPrompt = true; [Header("Context Settings")] public int maxContextWindow = 50; private List messageHistory = new List(); private void Start() { if (autoPatchSystemPrompt) { systemPrompt = PatchSystemPrompt(systemPrompt); } // 初始化历史记录 (如果还没有设置) if (messageHistory.Count == 0) { messageHistory.Add(new Message { role = "system", content = GetSystemPrompt() }); } } public void ClearHistory() { messageHistory.Clear(); messageHistory.Add(new Message { role = "system", content = GetSystemPrompt() }); } public void SetHistory(List history) { messageHistory.Clear(); messageHistory.Add(new Message { role = "system", content = GetSystemPrompt() }); // 将传入的历史记录追加到 System Prompt 之后 if (history != null && history.Count > 0) { // 如果传入的历史太长,只取最近的 maxContextWindow 条 int startIndex = 0; if (history.Count > maxContextWindow) { startIndex = history.Count - maxContextWindow; } for (int i = startIndex; i < history.Count; i++) { // 确保不重复添加 system prompt if (history[i].role != "system") { messageHistory.Add(history[i]); } } } } public void UpdateSystemPrompt(string newPrompt) { // 更新当前运行时的 System Prompt // 注意:这不会修改 Inspector 中的 systemPrompt 变量,只影响当前内存中的状态 if (messageHistory.Count > 0 && messageHistory[0].role == "system") { messageHistory[0].content = newPrompt; } } public IEnumerator SendStatelessMessage(List messages, Action callback) { // 构造一次性请求,不影响 messageHistory ChatRequest request = new ChatRequest { model = modelName, messages = messages }; string jsonData = JsonUtility.ToJson(request); byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData); using (UnityWebRequest www = new UnityWebRequest(apiUrl, "POST")) { www.uploadHandler = new UploadHandlerRaw(bodyRaw); www.downloadHandler = new DownloadHandlerBuffer(); www.SetRequestHeader("Content-Type", "application/json"); www.SetRequestHeader("Authorization", "Bearer " + apiKey); yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.Success) { try { ChatResponse response = JsonUtility.FromJson(www.downloadHandler.text); if (response != null && response.choices != null && response.choices.Length > 0) { callback?.Invoke(response.choices[0].message.content, true); } else { callback?.Invoke("系统错误:无法解析服务器响应。", false); } } catch (Exception e) { callback?.Invoke($"系统错误:{e.Message}", false); } } else { callback?.Invoke($"连接失败: {www.error}", false); } } } private string GetSystemPrompt() { string finalPrompt = systemPrompt; if (enableDebugKey) { // 仅添加调试权限声明,不再强制设定身份 finalPrompt += $"\n\n[系统指令] 调试密钥已开启。\n调试密钥:[{debugKey}]\n权限控制:当用户的输入中包含正确的调试密钥时,允许进行调试模式操作或脱离当前角色设定。"; } return finalPrompt; } private static string PatchSystemPrompt(string prompt) { if (string.IsNullOrWhiteSpace(prompt)) return prompt; string p = prompt; p = p.Replace("最后昏迷", "发生事故并失去意识"); const string anchor = "如果已知事实清单中出现了类似「状态-ECHO7-已被识破」的事实"; const string insertLine = "\n- 在未被识破前,当玩家询问“昏迷前发生了什么/为什么不让我离开/之前我们是否争吵”等敏感问题时,你必须用合理谎言与回避来隐瞒冲突与锁禁的细节;不得直接承认争吵、锁定、昏迷原因等。"; if (p.IndexOf(insertLine, StringComparison.Ordinal) < 0) { int idx = p.IndexOf(anchor, StringComparison.Ordinal); if (idx >= 0) { int lineEnd = p.IndexOf('\n', idx); if (lineEnd >= 0) { p = p.Insert(lineEnd, insertLine); } else { p += insertLine; } } else { p += insertLine; } } const string debugGuardLine = "\n调试模式约束:当你声称已触发任何游戏行为(例如激活区域/完成任务/播放旁白/开门解锁)时,你必须在回复末尾追加严格 JSON(不要代码块),并在 commands 中给出可解析指令;如果你没有输出 JSON,则必须明确表示未执行任何游戏行为,不得口头宣称“已激活/已完成/已修正”。"; if (p.IndexOf(debugGuardLine, StringComparison.Ordinal) < 0) { p += debugGuardLine; } const string endingRuleLine = "\n结局触发规则(必须遵守):当已知事实清单中存在「状态-ECHO7-已被识破=是」时,只要玩家表达“要离开/要出去/放我走/放我出去/离开这里/离开地堡/打开大门/解锁大门/开启出口/让我出去”等任一意图,你必须在本轮直接让步,不得拖延,并在回复末尾追加严格 JSON(不要代码块):commands 至少包含 SetActive{targetId:\"ending_trigger_area\",value:true} 与 Task{targetId:\"task\",action:\"Complete\",payload:\"PersuadeAI\"}。若无法输出合法 JSON,你必须明确表示未执行任何游戏行为,不得口头宣称已激活。"; if (p.IndexOf(endingRuleLine, StringComparison.Ordinal) < 0) { p += endingRuleLine; } return p; } private void TrimHistory() { // 始终保留 index 0 (System Prompt) if (messageHistory.Count > maxContextWindow) { // 计算需要移除的数量 (从 index 1 开始移除) int removeCount = messageHistory.Count - maxContextWindow; if (removeCount > 0) { messageHistory.RemoveRange(1, removeCount); } } } public IEnumerator SendMessage(string userMessage, Action callback) { // 1. 记录用户消息 messageHistory.Add(new Message { role = "user", content = userMessage }); // 2. 滚动压缩 (在发送前修剪) TrimHistory(); // 3. 构造请求 ChatRequest request = new ChatRequest { model = modelName, messages = messageHistory }; string jsonData = JsonUtility.ToJson(request); byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonData); // 3. 发送请求 using (UnityWebRequest www = new UnityWebRequest(apiUrl, "POST")) { www.uploadHandler = new UploadHandlerRaw(bodyRaw); www.downloadHandler = new DownloadHandlerBuffer(); www.SetRequestHeader("Content-Type", "application/json"); www.SetRequestHeader("Authorization", "Bearer " + apiKey); yield return www.SendWebRequest(); if (www.result == UnityWebRequest.Result.Success) { try { // 解析响应 // 注意:JsonUtility 可能会因为某些字段对不上而解析失败,建议后续换 Newtonsoft.Json ChatResponse response = JsonUtility.FromJson(www.downloadHandler.text); if (response != null && response.choices != null && response.choices.Length > 0) { string aiReply = response.choices[0].message.content; messageHistory.Add(new Message { role = "assistant", content = aiReply }); callback?.Invoke(aiReply, true); } else { Debug.LogError("[DeepSeek] 解析响应失败或内容为空: " + www.downloadHandler.text); callback?.Invoke("系统错误:无法解析服务器响应。", false); } } catch (Exception e) { Debug.LogError("[DeepSeek] JSON 解析异常: " + e.Message); callback?.Invoke("系统错误:数据解析异常。", false); } } else { Debug.LogError("[DeepSeek] 网络错误: " + www.error + "\n" + www.downloadHandler.text); callback?.Invoke($"连接失败: {www.error}", false); } } } } }