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_]+)(?:>.*?\1>|/>)", 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);