zc
This commit is contained in:
Binary file not shown.
@@ -270,13 +270,19 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
"- call_bombardment\n" +
|
"- call_bombardment\n" +
|
||||||
"- modify_goodwill\n" +
|
"- modify_goodwill\n" +
|
||||||
"If no action is required, output exactly: <no_action/>.\n" +
|
"If no action is required, output exactly: <no_action/>.\n" +
|
||||||
"Other tools are still available if needed.\n"
|
"Query tools exist but are disabled in this phase (not listed here).\n"
|
||||||
|
: string.Empty;
|
||||||
|
string actionWhitelist = phase == RequestPhase.ActionTools
|
||||||
|
? "ACTION PHASE VALID TAGS ONLY:\n" +
|
||||||
|
"<spawn_resources>, <send_reinforcement>, <call_bombardment>, <modify_goodwill>, <no_action/>\n" +
|
||||||
|
"INVALID EXAMPLES (do NOT use now): <get_map_resources/>, <search_thing_def/>\n"
|
||||||
: string.Empty;
|
: string.Empty;
|
||||||
|
|
||||||
return string.Join("\n\n", new[]
|
return string.Join("\n\n", new[]
|
||||||
{
|
{
|
||||||
phaseInstruction,
|
phaseInstruction,
|
||||||
string.IsNullOrWhiteSpace(actionPriority) ? null : actionPriority.TrimEnd(),
|
string.IsNullOrWhiteSpace(actionPriority) ? null : actionPriority.TrimEnd(),
|
||||||
|
string.IsNullOrWhiteSpace(actionWhitelist) ? null : actionWhitelist.TrimEnd(),
|
||||||
ToolRulesInstruction.TrimEnd(),
|
ToolRulesInstruction.TrimEnd(),
|
||||||
toolsForThisPhase
|
toolsForThisPhase
|
||||||
}.Where(part => !string.IsNullOrWhiteSpace(part)));
|
}.Where(part => !string.IsNullOrWhiteSpace(part)));
|
||||||
@@ -288,6 +294,11 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
|
|
||||||
var available = _tools
|
var available = _tools
|
||||||
.Where(t => t != null)
|
.Where(t => t != null)
|
||||||
|
.Where(t => phase == RequestPhase.QueryTools
|
||||||
|
? IsQueryToolName(t.Name)
|
||||||
|
: phase == RequestPhase.ActionTools
|
||||||
|
? IsActionToolName(t.Name)
|
||||||
|
: true)
|
||||||
.OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase)
|
.OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -328,6 +339,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
"- Prefer query tools (get_*/search_*).\n" +
|
"- Prefer query tools (get_*/search_*).\n" +
|
||||||
"- You MAY call multiple tools in one response, but keep it concise.\n" +
|
"- You MAY call multiple tools in one response, but keep it concise.\n" +
|
||||||
"- If the user requests multiple items or information, you MUST output ALL required tool calls in this SAME response.\n" +
|
"- If the user requests multiple items or information, you MUST output ALL required tool calls in this SAME response.\n" +
|
||||||
|
"- Action tools are available in PHASE 2 only; do NOT use them here.\n" +
|
||||||
"After this phase, the game will automatically proceed to PHASE 2.\n" +
|
"After this phase, the game will automatically proceed to PHASE 2.\n" +
|
||||||
"Output: XML only.\n",
|
"Output: XML only.\n",
|
||||||
RequestPhase.ActionTools =>
|
RequestPhase.ActionTools =>
|
||||||
@@ -379,7 +391,38 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<(string role, string message)> BuildToolContext(int maxToolResults = 2)
|
private static bool IsActionToolName(string toolName)
|
||||||
|
{
|
||||||
|
return toolName == "spawn_resources" ||
|
||||||
|
toolName == "send_reinforcement" ||
|
||||||
|
toolName == "call_bombardment" ||
|
||||||
|
toolName == "modify_goodwill";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsQueryToolName(string toolName)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(toolName)) return false;
|
||||||
|
return toolName.StartsWith("get_", StringComparison.OrdinalIgnoreCase) ||
|
||||||
|
toolName.StartsWith("search_", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string SanitizeToolResultForActionPhase(string message)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(message)) return message;
|
||||||
|
string sanitized = message;
|
||||||
|
sanitized = Regex.Replace(sanitized, @"Tool\s+'[^']+'\s+Result(?:\s+\(Invisible\))?:", "Query Result:");
|
||||||
|
sanitized = Regex.Replace(sanitized, @"Tool\s+'[^']+'\s+Result\s+\(Invisible\):", "Query Result:");
|
||||||
|
return sanitized;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string TrimForPrompt(string text, int maxChars)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) return "";
|
||||||
|
if (text.Length <= maxChars) return text;
|
||||||
|
return text.Substring(0, maxChars) + "...(truncated)";
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<(string role, string message)> BuildToolContext(RequestPhase phase, int maxToolResults = 2, bool includeUser = true)
|
||||||
{
|
{
|
||||||
if (_history == null || _history.Count == 0) return new List<(string role, string message)>();
|
if (_history == null || _history.Count == 0) return new List<(string role, string message)>();
|
||||||
|
|
||||||
@@ -400,7 +443,12 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
{
|
{
|
||||||
if (string.Equals(_history[i].role, "tool", StringComparison.OrdinalIgnoreCase))
|
if (string.Equals(_history[i].role, "tool", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
toolEntries.Add(_history[i]);
|
string msg = _history[i].message;
|
||||||
|
if (phase == RequestPhase.ActionTools)
|
||||||
|
{
|
||||||
|
msg = SanitizeToolResultForActionPhase(msg);
|
||||||
|
}
|
||||||
|
toolEntries.Add((_history[i].role, msg));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -409,10 +457,12 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
toolEntries = toolEntries.Skip(toolEntries.Count - maxToolResults).ToList();
|
toolEntries = toolEntries.Skip(toolEntries.Count - maxToolResults).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
var context = new List<(string role, string message)>
|
bool includeUserFallback = includeUser || toolEntries.Count == 0;
|
||||||
|
var context = new List<(string role, string message)>();
|
||||||
|
if (includeUserFallback)
|
||||||
{
|
{
|
||||||
_history[lastUserIndex]
|
context.Add(_history[lastUserIndex]);
|
||||||
};
|
}
|
||||||
context.AddRange(toolEntries);
|
context.AddRange(toolEntries);
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
@@ -459,7 +509,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
}
|
}
|
||||||
|
|
||||||
string queryInstruction = GetToolSystemInstruction(queryPhase);
|
string queryInstruction = GetToolSystemInstruction(queryPhase);
|
||||||
string queryResponse = await client.GetChatCompletionAsync(queryInstruction, BuildToolContext(), maxTokens: 128, temperature: 0.1f);
|
string queryResponse = await client.GetChatCompletionAsync(queryInstruction, BuildToolContext(queryPhase), maxTokens: 128, temperature: 0.1f);
|
||||||
if (string.IsNullOrEmpty(queryResponse))
|
if (string.IsNullOrEmpty(queryResponse))
|
||||||
{
|
{
|
||||||
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
||||||
@@ -501,7 +551,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
SetThinkingPhase(1, true);
|
SetThinkingPhase(1, true);
|
||||||
string retryQueryInstruction = GetToolSystemInstruction(queryPhase) +
|
string retryQueryInstruction = GetToolSystemInstruction(queryPhase) +
|
||||||
"\n\n# RETRY\nYou chose to retry. Output XML tool calls only (or <no_action/>).";
|
"\n\n# RETRY\nYou chose to retry. Output XML tool calls only (or <no_action/>).";
|
||||||
string retryQueryResponse = await client.GetChatCompletionAsync(retryQueryInstruction, BuildToolContext(), maxTokens: 128, temperature: 0.1f);
|
string retryQueryResponse = await client.GetChatCompletionAsync(retryQueryInstruction, BuildToolContext(queryPhase), maxTokens: 128, temperature: 0.1f);
|
||||||
if (string.IsNullOrEmpty(retryQueryResponse))
|
if (string.IsNullOrEmpty(retryQueryResponse))
|
||||||
{
|
{
|
||||||
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
||||||
@@ -529,7 +579,8 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
|
|
||||||
SetThinkingPhase(2, false);
|
SetThinkingPhase(2, false);
|
||||||
string actionInstruction = GetToolSystemInstruction(actionPhase);
|
string actionInstruction = GetToolSystemInstruction(actionPhase);
|
||||||
string actionResponse = await client.GetChatCompletionAsync(actionInstruction, BuildToolContext(), maxTokens: 128, temperature: 0.1f);
|
var actionContext = BuildToolContext(actionPhase, includeUser: true);
|
||||||
|
string actionResponse = await client.GetChatCompletionAsync(actionInstruction, actionContext, maxTokens: 128, temperature: 0.1f);
|
||||||
if (string.IsNullOrEmpty(actionResponse))
|
if (string.IsNullOrEmpty(actionResponse))
|
||||||
{
|
{
|
||||||
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
||||||
@@ -540,10 +591,29 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
{
|
{
|
||||||
if (Prefs.DevMode)
|
if (Prefs.DevMode)
|
||||||
{
|
{
|
||||||
WulaLog.Debug("[WulaAI] Turn 2/3 missing XML; treating as <no_action/>");
|
WulaLog.Debug("[WulaAI] Turn 2/3 missing XML; attempting XML-only conversion.");
|
||||||
|
}
|
||||||
|
string fixInstruction = actionInstruction +
|
||||||
|
"\n\n# FORMAT FIX\n" +
|
||||||
|
"Your last output was not valid XML.\n" +
|
||||||
|
"Convert the intended action into VALID XML tool calls or <no_action/>.\n" +
|
||||||
|
"Output XML only. No commentary.\n" +
|
||||||
|
"You MUST use only action tools.\n" +
|
||||||
|
"\nPrevious output:\n" + TrimForPrompt(actionResponse, 600);
|
||||||
|
string fixedResponse = await client.GetChatCompletionAsync(fixInstruction, actionContext, maxTokens: 128, temperature: 0.1f);
|
||||||
|
if (!string.IsNullOrEmpty(fixedResponse) && IsXmlToolCall(fixedResponse))
|
||||||
|
{
|
||||||
|
actionResponse = fixedResponse;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Prefs.DevMode)
|
||||||
|
{
|
||||||
|
WulaLog.Debug("[WulaAI] Turn 2/3 conversion failed; treating as <no_action/>");
|
||||||
}
|
}
|
||||||
actionResponse = "<no_action/>";
|
actionResponse = "<no_action/>";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
PhaseExecutionResult actionResult = await ExecuteXmlToolsForPhase(actionResponse, actionPhase);
|
PhaseExecutionResult actionResult = await ExecuteXmlToolsForPhase(actionResponse, actionPhase);
|
||||||
if (!actionResult.AnyActionSuccess && !_actionRetryUsed)
|
if (!actionResult.AnyActionSuccess && !_actionRetryUsed)
|
||||||
@@ -570,7 +640,8 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
SetThinkingPhase(2, true);
|
SetThinkingPhase(2, true);
|
||||||
string retryActionInstruction = GetToolSystemInstruction(actionPhase) +
|
string retryActionInstruction = GetToolSystemInstruction(actionPhase) +
|
||||||
"\n\n# RETRY\nYou chose to retry. Output XML tool calls only (or <no_action/>).";
|
"\n\n# RETRY\nYou chose to retry. Output XML tool calls only (or <no_action/>).";
|
||||||
string retryActionResponse = await client.GetChatCompletionAsync(retryActionInstruction, BuildToolContext(), maxTokens: 128, temperature: 0.1f);
|
var retryActionContext = BuildToolContext(actionPhase, includeUser: true);
|
||||||
|
string retryActionResponse = await client.GetChatCompletionAsync(retryActionInstruction, retryActionContext, maxTokens: 128, temperature: 0.1f);
|
||||||
if (string.IsNullOrEmpty(retryActionResponse))
|
if (string.IsNullOrEmpty(retryActionResponse))
|
||||||
{
|
{
|
||||||
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
||||||
@@ -581,10 +652,29 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
{
|
{
|
||||||
if (Prefs.DevMode)
|
if (Prefs.DevMode)
|
||||||
{
|
{
|
||||||
WulaLog.Debug("[WulaAI] Retry action phase missing XML; treating as <no_action/>");
|
WulaLog.Debug("[WulaAI] Retry action phase missing XML; attempting XML-only conversion.");
|
||||||
|
}
|
||||||
|
string retryFixInstruction = retryActionInstruction +
|
||||||
|
"\n\n# FORMAT FIX\n" +
|
||||||
|
"Your last output was not valid XML.\n" +
|
||||||
|
"Convert the intended action into VALID XML tool calls or <no_action/>.\n" +
|
||||||
|
"Output XML only. No commentary.\n" +
|
||||||
|
"You MUST use only action tools.\n" +
|
||||||
|
"\nPrevious output:\n" + TrimForPrompt(retryActionResponse, 600);
|
||||||
|
string retryFixedResponse = await client.GetChatCompletionAsync(retryFixInstruction, retryActionContext, maxTokens: 128, temperature: 0.1f);
|
||||||
|
if (!string.IsNullOrEmpty(retryFixedResponse) && IsXmlToolCall(retryFixedResponse))
|
||||||
|
{
|
||||||
|
retryActionResponse = retryFixedResponse;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (Prefs.DevMode)
|
||||||
|
{
|
||||||
|
WulaLog.Debug("[WulaAI] Retry action conversion failed; treating as <no_action/>");
|
||||||
}
|
}
|
||||||
retryActionResponse = "<no_action/>";
|
retryActionResponse = "<no_action/>";
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
actionResult = await ExecuteXmlToolsForPhase(retryActionResponse, actionPhase);
|
actionResult = await ExecuteXmlToolsForPhase(retryActionResponse, actionPhase);
|
||||||
}
|
}
|
||||||
@@ -622,14 +712,31 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
replyInstruction += "\nIMPORTANT: An action tool failed. You MUST acknowledge the failure and MUST NOT claim success.";
|
replyInstruction += "\nIMPORTANT: An action tool failed. You MUST acknowledge the failure and MUST NOT claim success.";
|
||||||
}
|
}
|
||||||
|
|
||||||
string reply = await client.GetChatCompletionAsync(replyInstruction, _history);
|
string reply = await client.GetChatCompletionAsync(replyInstruction, BuildReplyHistory());
|
||||||
if (string.IsNullOrEmpty(reply))
|
if (string.IsNullOrEmpty(reply))
|
||||||
{
|
{
|
||||||
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
_currentResponse = "Wula_AI_Error_ConnectionLost".Translate();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (IsXmlToolCall(reply))
|
bool replyHadXml = IsXmlToolCall(reply);
|
||||||
|
string strippedReply = StripXmlTags(reply)?.Trim() ?? "";
|
||||||
|
if (replyHadXml || string.IsNullOrWhiteSpace(strippedReply))
|
||||||
|
{
|
||||||
|
string retryReplyInstruction = replyInstruction +
|
||||||
|
"\n\n# RETRY (REPLY OUTPUT)\n" +
|
||||||
|
"Your last reply included XML or was empty. Tool calls are DISABLED.\n" +
|
||||||
|
"You MUST reply in natural language only. Do NOT output any XML.\n";
|
||||||
|
string retryReply = await client.GetChatCompletionAsync(retryReplyInstruction, BuildReplyHistory(), maxTokens: 256, temperature: 0.3f);
|
||||||
|
if (!string.IsNullOrEmpty(retryReply))
|
||||||
|
{
|
||||||
|
reply = retryReply;
|
||||||
|
replyHadXml = IsXmlToolCall(reply);
|
||||||
|
strippedReply = StripXmlTags(reply)?.Trim() ?? "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (replyHadXml)
|
||||||
{
|
{
|
||||||
string cleaned = StripXmlTags(reply)?.Trim() ?? "";
|
string cleaned = StripXmlTags(reply)?.Trim() ?? "";
|
||||||
if (string.IsNullOrWhiteSpace(cleaned))
|
if (string.IsNullOrWhiteSpace(cleaned))
|
||||||
@@ -682,21 +789,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
return default;
|
return default;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool IsActionToolName(string toolName)
|
|
||||||
{
|
|
||||||
return toolName == "spawn_resources" ||
|
|
||||||
toolName == "send_reinforcement" ||
|
|
||||||
toolName == "call_bombardment" ||
|
|
||||||
toolName == "modify_goodwill";
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool IsQueryToolName(string toolName)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(toolName)) return false;
|
|
||||||
return toolName.StartsWith("get_", StringComparison.OrdinalIgnoreCase) ||
|
|
||||||
toolName.StartsWith("search_", StringComparison.OrdinalIgnoreCase);
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxTools = MaxToolsPerPhase(phase);
|
int maxTools = MaxToolsPerPhase(phase);
|
||||||
int executed = 0;
|
int executed = 0;
|
||||||
bool executedActionTool = false;
|
bool executedActionTool = false;
|
||||||
@@ -920,6 +1012,49 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
return stripped;
|
return stripped;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<(string role, string message)> BuildReplyHistory()
|
||||||
|
{
|
||||||
|
if (_history == null || _history.Count == 0) return new List<(string role, string message)>();
|
||||||
|
|
||||||
|
int lastUserIndex = -1;
|
||||||
|
for (int i = _history.Count - 1; i >= 0; i--)
|
||||||
|
{
|
||||||
|
if (string.Equals(_history[i].role, "user", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
lastUserIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var filtered = new List<(string role, string message)>();
|
||||||
|
for (int i = 0; i < _history.Count; i++)
|
||||||
|
{
|
||||||
|
var entry = _history[i];
|
||||||
|
if (string.Equals(entry.role, "tool", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
if (lastUserIndex != -1 && i > lastUserIndex)
|
||||||
|
{
|
||||||
|
filtered.Add(entry);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.Equals(entry.role, "assistant", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
filtered.Add(entry);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
string stripped = StripXmlTags(entry.message)?.Trim() ?? "";
|
||||||
|
if (!string.IsNullOrWhiteSpace(stripped))
|
||||||
|
{
|
||||||
|
filtered.Add(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered;
|
||||||
|
}
|
||||||
|
|
||||||
private string StripExpressionTags(string text)
|
private string StripExpressionTags(string text)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(text)) return text;
|
if (string.IsNullOrEmpty(text)) return text;
|
||||||
|
|||||||
Reference in New Issue
Block a user