diff --git a/1.6/1.6/Assemblies/WulaFallenEmpire.dll b/1.6/1.6/Assemblies/WulaFallenEmpire.dll index e9de73e2..a2beedfe 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/UI/Dialog_AIConversation.cs b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs index 26eae116..196ec3c8 100644 --- a/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs +++ b/Source/WulaFallenEmpire/EventSystem/AI/UI/Dialog_AIConversation.cs @@ -67,16 +67,18 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori value -2. **STRICT OUTPUT (TOOL PHASES)**: In PHASE 1/2/3, your output MUST be either: - - One or more XML tool calls (no extra text), OR - - Exactly: - Do NOT include any natural language, explanation, markdown, or additional commentary in tool phases. +2. **STRICT OUTPUT (TOOL PHASES)**: + - In PHASE 1/2, your output MUST be either: + - One or more XML tool calls (no extra text), OR + - Exactly: + - In PHASE 3, you MUST output XML tool calls only AND you MUST include exactly one (expression_id 1-6). Do NOT output in PHASE 3. + Do NOT include any natural language, explanation, markdown, or additional commentary in tool phases (PHASE 1/2/3). 3. **STRICT OUTPUT (REPLY PHASE)**: In PHASE 4, tools are disabled. You MUST reply in natural language only and MUST NOT output any XML. 4. **ALLOWED TOOLS**: You MUST ONLY call tools listed in the current phase's tool list (the section titled ""# TOOLS (PHASE X/4 ONLY)""). 5. **WORKFLOW**: Use the phase workflow: - PHASE 1 gathers info (optional). - PHASE 2 performs at most one in-game action (optional). - - PHASE 3 performs UI/meta adjustments (optional). + - PHASE 3 performs UI/meta adjustments (MUST include ). - PHASE 4 replies to the player in natural language (mandatory). 6. **ANTI-HALLUCINATION**: Never invent tools, parameters, defNames, coordinates, or tool results. If a tool is needed but not available, use and proceed to PHASE 4 to explain limitations. "; @@ -401,6 +403,13 @@ Description: Changes your visual AI portrait to match your current mood or react Use this tool when: - Your verbal response conveys a strong emotion (e.g., annoyance, approval, curiosity). - You want to visually emphasize your statement. +Expression meanings (choose the closest match): +- 1: 得意、炫耀(非敌对)、示威(非敌对)、展示武力和财力(非敌对)、策划计谋 +- 2: 常态立绘(当其他立绘不适用时使用这个) +- 3: 无言以对、不满、无奈、轻微的鄙视 +- 4: 恼火、展现轻微敌对姿态、抗拒 +- 5: 答复、解释 +- 6: 严重的敌意、严重不满、攻击性行为 Parameters: - expression_id: (REQUIRED) An integer from 1 to 6 corresponding to a specific expression. Usage: @@ -470,13 +479,13 @@ Example (changing to a neutral expression): "Output: XML only.\n", RequestPhase.Cosmetic => "# PHASE 3/4 (Cosmetic)\n" + - "Goal: Optional UI/meta adjustments before your final reply.\n" + + "Goal: Set your UI expression before your final reply.\n" + "Rules:\n" + "- You MUST NOT write any natural language to the user in this phase.\n" + - "- You MAY call up to 2 tools from \"# TOOLS (PHASE 3/4 ONLY)\".\n" + - "- If you performed an in-game action in PHASE 2, you SHOULD call to match your mood.\n" + + "- You MUST call exactly ONE in this phase (expression_id 1-6).\n" + + "- You MAY also call (invisible) if needed, but keep changes small.\n" + "- Use only to adjust your INTERNAL goodwill (invisible to the player).\n" + - "- If you do not need any tool, output exactly: .\n" + + "- Do NOT output in this phase.\n" + "After this phase, the game will automatically proceed to PHASE 4.\n" + "Output: XML only.\n", RequestPhase.Reply => @@ -496,6 +505,13 @@ Example (changing to a neutral expression): return Regex.IsMatch(response, @"<([a-zA-Z0-9_]+)(?:>.*?|/>)", RegexOptions.Singleline); } + private static bool ContainsToolCall(string response, string toolName) + { + if (string.IsNullOrWhiteSpace(response) || string.IsNullOrWhiteSpace(toolName)) return false; + string pattern = $@"<\s*{Regex.Escape(toolName)}(?:\s|/|>)"; + return Regex.IsMatch(response, pattern, RegexOptions.IgnoreCase); + } + private static bool IsAllowedInPhase(RequestPhase phase, string toolName) { if (string.IsNullOrWhiteSpace(toolName)) return false; @@ -638,7 +654,33 @@ Example (changing to a neutral expression): { Log.Warning($"[WulaAI] Turn {phaseIndex}/4 still missing XML after retry; forcing "); } - response = ""; + response = phase == RequestPhase.Cosmetic + ? "2" + : ""; + } + } + + if (phase == RequestPhase.Cosmetic && !ContainsToolCall(response, "change_expression")) + { + _history.Add(("system", "[PhaseEnforcer] PHASE 3/4 MUST include exactly one (expression_id 1-6). Output XML only and do NOT output in PHASE 3.")); + PersistHistory(); + if (Prefs.DevMode) + { + Log.Message("[WulaAI] Turn 3/4 missing ; retrying once"); + } + + string retry = await client.GetChatCompletionAsync(systemInstruction, _history); + if (!string.IsNullOrEmpty(retry) && ContainsToolCall(retry, "change_expression")) + { + response = retry; + } + else + { + if (Prefs.DevMode) + { + Log.Warning("[WulaAI] Turn 3/4 still missing after retry; forcing default expression_id=2"); + } + response = "2"; } } @@ -661,10 +703,17 @@ Example (changing to a neutral expression): // Special-case no_action for phases 1-3. if (Regex.IsMatch(xml ?? "", @"<\s*no_action\s*/\s*>", RegexOptions.IgnoreCase)) { + if (phase == RequestPhase.Cosmetic) + { + xml = "2"; + } + else + { _history.Add(("assistant", "")); _history.Add(("tool", "[Tool Results]\nTool 'no_action' Result: No action taken.")); PersistHistory(); return; + } } // Reuse the tool runner but temporarily constrain allowed tools by phase. @@ -680,6 +729,8 @@ Example (changing to a neutral expression): int maxTools = MaxToolsPerPhase(phase); int executed = 0; bool actionHadError = false; + bool executedChangeExpression = false; + bool executedModifyGoodwill = false; StringBuilder combinedResults = new StringBuilder(); StringBuilder xmlOnlyBuilder = new StringBuilder(); @@ -700,6 +751,28 @@ Example (changing to a neutral expression): continue; } + if (phase == RequestPhase.Cosmetic) + { + if (toolName.Equals("change_expression", StringComparison.OrdinalIgnoreCase)) + { + if (executedChangeExpression) + { + combinedResults.AppendLine("ToolRunner Note: Skipped duplicate 'change_expression' (only one is allowed in PHASE 3)."); + continue; + } + executedChangeExpression = true; + } + else if (toolName.Equals("modify_goodwill", StringComparison.OrdinalIgnoreCase)) + { + if (executedModifyGoodwill) + { + combinedResults.AppendLine("ToolRunner Note: Skipped duplicate 'modify_goodwill' (only one is allowed in PHASE 3)."); + continue; + } + executedModifyGoodwill = true; + } + } + if (xmlOnlyBuilder.Length > 0) xmlOnlyBuilder.AppendLine().AppendLine(); xmlOnlyBuilder.Append(toolCallXml);