diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index e7f0e887..24b04680 100644 Binary files a/1.6/1.6/Assemblies/WulaFallenEmpire.dll and b/1.6/1.6/Assemblies/WulaFallenEmpire.dll differ diff --git a/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs b/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs index d05ec25c..12cab727 100644 --- a/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs +++ b/Source/WulaFallenEmpire/EventSystem/AI/AIIntelligenceCore.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Text; using System.Text.RegularExpressions; @@ -83,6 +84,8 @@ namespace WulaFallenEmpire.EventSystem.AI { public string Text; public string Category; + public string Stability; + public float Confidence; } private struct MemoryUpdate @@ -1162,7 +1165,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori continue; } - string cleaned = StripToolCallJson(entry.message)?.Trim() ?? ""; + string cleaned = CleanAssistantForReply(entry.message); if (string.IsNullOrWhiteSpace(cleaned)) { continue; @@ -1264,19 +1267,52 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori continue; } - if (IsAutoCommentaryMessage(entry.message)) + string role; + string message = entry.message; + if (string.Equals(entry.role, "assistant", StringComparison.OrdinalIgnoreCase)) + { + message = CleanAssistantForReply(message); + if (string.IsNullOrWhiteSpace(message)) + { + continue; + } + role = "Assistant"; + } + else + { + role = "User"; + } + + if (IsAutoCommentaryMessage(message)) { continue; } - string role = string.Equals(entry.role, "user", StringComparison.OrdinalIgnoreCase) ? "User" : "Assistant"; - sb.AppendLine($"{role}: {entry.message}"); + sb.AppendLine($"{role}: {message}"); } string conversation = sb.ToString().Trim(); return TrimForPrompt(conversation, 4000); } + private static string CleanAssistantForReply(string message) + { + if (string.IsNullOrWhiteSpace(message)) + { + return ""; + } + + string cleaned = message; + cleaned = Regex.Replace(cleaned, @".*?", "", RegexOptions.Singleline | RegexOptions.IgnoreCase); + cleaned = Regex.Replace(cleaned, @"```[\s\S]*?```", match => + { + string block = match.Value ?? ""; + return block.IndexOf("tool_calls", StringComparison.OrdinalIgnoreCase) >= 0 ? "" : block; + }); + cleaned = StripToolCallJson(cleaned)?.Trim() ?? ""; + return cleaned.Trim(); + } + private async Task UpdateMemoriesFromConversationAsync(AIMemoryManager memoryManager, string existingMemoriesJson, string conversation) { try @@ -1367,12 +1403,42 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori } dict.TryGetValue("category", out string category); - facts.Add(new MemoryFact { Text = text.Trim(), Category = category ?? "misc" }); + dict.TryGetValue("stability", out string stability); + float confidence = -1f; + if (dict.TryGetValue("confidence", out string confidenceRaw) && + float.TryParse(confidenceRaw, NumberStyles.Float, CultureInfo.InvariantCulture, out float parsed)) + { + confidence = parsed; + } + + var fact = new MemoryFact + { + Text = text.Trim(), + Category = category ?? "misc", + Stability = stability ?? "volatile", + Confidence = confidence + }; + if (!IsStableMemoryFact(fact)) + { + continue; + } + facts.Add(fact); } return facts; } + private static bool IsStableMemoryFact(MemoryFact fact) + { + if (!string.Equals(fact.Stability, "stable", StringComparison.OrdinalIgnoreCase)) + { + return false; + } + + const float minConfidence = 0.6f; + return fact.Confidence < 0f || fact.Confidence >= minConfidence; + } + private static List ParseMemoryUpdates(string json) { var updates = new List(); diff --git a/Source/WulaFallenEmpire/EventSystem/AI/MemoryPrompts.cs b/Source/WulaFallenEmpire/EventSystem/AI/MemoryPrompts.cs index 105c406f..8db875d1 100644 --- a/Source/WulaFallenEmpire/EventSystem/AI/MemoryPrompts.cs +++ b/Source/WulaFallenEmpire/EventSystem/AI/MemoryPrompts.cs @@ -8,10 +8,12 @@ namespace WulaFallenEmpire.EventSystem.AI @"You are extracting long-term memory about the player from the conversation below. Return JSON only, no extra text. Schema: -{{""facts"":[{{""text"":""..."",""category"":""preference|personal|plan|colony|misc""}}]}} +{{""facts"":[{{""text"":""..."",""category"":""preference|personal|plan|colony|misc"",""stability"":""stable|volatile"",""confidence"":0.0}}]}} Rules: +- Use ONLY User and Assistant final replies. Ignore tool outputs, system messages, and auto-commentary. - Keep only stable, reusable facts about the player or colony. -- Ignore transient tool results, numbers, or one-off actions. +- Mark transient details (counts, coordinates, inventories, momentary statuses, or one-off events) as ""volatile"". +- If unsure, set low confidence and mark as ""volatile"". - Do not invent facts. Conversation: {0}"; diff --git a/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs index 1be91c29..8dc16f5b 100644 --- a/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs +++ b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs @@ -416,6 +416,47 @@ namespace WulaFallenEmpire.EventSystem.AI.UI toolcallBuffer.Clear(); toolResultBuffer.Clear(); } + else if (entry.role == "assistant" && traceEnabled && toolcallBuffer.Count == 0) + { + var traceLines = new List { "无工具调用,直接回复" }; + int traceKey = i; + bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved; + string header = BuildReactTraceHeader(); + + Text.Font = GameFont.Tiny; + float tracePadding = 8f; + float headerWidth = Mathf.Max(10f, contentWidth - tracePadding * 2f); + string headerLine = $"{(expanded ? "v" : ">")} {header}"; + float headerHeight = Text.CalcHeight(headerLine, headerWidth) + 10f; + float linesHeight = 0f; + if (expanded) + { + float lineWidth = Mathf.Max(10f, contentWidth - tracePadding * 2f); + foreach (string line in traceLines) + { + linesHeight += Text.CalcHeight(line, lineWidth) + 2f; + } + linesHeight += 8f; + } + float traceHeight = headerHeight + linesHeight; + + _cachedMessages.Add(new CachedMessage + { + role = "trace", + message = "", + displayText = "", + height = traceHeight, + yOffset = curY, + font = GameFont.Tiny, + isTrace = true, + traceKey = traceKey, + traceHeader = header, + traceLines = traceLines, + traceExpanded = expanded, + traceHeaderHeight = headerHeight + }); + curY += traceHeight + 10f; + } if (string.IsNullOrEmpty(messageText) || (entry.role == "user" && messageText.StartsWith("[Tool Results]"))) continue; bool isLastMessage = i == history.Count - 1; diff --git a/Source/WulaFallenEmpire/EventSystem/AI/UI/Overlay_WulaLink.cs b/Source/WulaFallenEmpire/EventSystem/AI/UI/Overlay_WulaLink.cs index 67208dc3..574e9891 100644 --- a/Source/WulaFallenEmpire/EventSystem/AI/UI/Overlay_WulaLink.cs +++ b/Source/WulaFallenEmpire/EventSystem/AI/UI/Overlay_WulaLink.cs @@ -475,6 +475,46 @@ namespace WulaFallenEmpire.EventSystem.AI.UI toolcallBuffer.Clear(); toolResultBuffer.Clear(); } + else if (msg.role == "assistant" && traceEnabled && toolcallBuffer.Count == 0) + { + var traceLines = new List { "无工具调用,直接回复" }; + int traceKey = i; + bool expanded = _traceExpandedByAssistantIndex.TryGetValue(traceKey, out bool saved) && saved; + string header = BuildReactTraceHeader(); + + Text.Font = GameFont.Tiny; + float tracePadding = 8f; + float headerWidth = Mathf.Max(10f, width - tracePadding * 2f); + string headerLine = $"{(expanded ? "v" : ">")} {header}"; + float headerHeight = Text.CalcHeight(headerLine, headerWidth) + 10f; + float linesHeight = 0f; + if (expanded) + { + float lineWidth = Mathf.Max(10f, width - tracePadding * 2f); + foreach (string line in traceLines) + { + linesHeight += Text.CalcHeight(line, lineWidth) + 2f; + } + linesHeight += 8f; + } + float traceHeight = headerHeight + linesHeight; + + _cachedMessages.Add(new CachedMessage + { + role = "trace", + message = "", + displayText = "", + height = traceHeight, + yOffset = curY, + isTrace = true, + traceKey = traceKey, + traceHeader = header, + traceLines = traceLines, + traceExpanded = expanded, + traceHeaderHeight = headerHeight + }); + curY += traceHeight + reducedSpacing; + } if (string.IsNullOrWhiteSpace(displayText)) continue;