This commit is contained in:
2025-12-28 12:03:33 +08:00
parent 42427f28df
commit addb6bbd08
6 changed files with 181 additions and 391 deletions

View File

@@ -248,7 +248,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
return;
}
// 附加选中对象的上下文信息
// 附加选中对象的上下文信息
string messageWithContext = BuildUserMessageWithContext(text);
_history.Add(("user", messageWithContext));
PersistHistory();
@@ -317,23 +317,18 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
_tools.Add(new Tool_SendReinforcement());
_tools.Add(new Tool_GetColonistStatus());
_tools.Add(new Tool_GetMapResources());
_tools.Add(new Tool_GetAvailablePrefabs());
_tools.Add(new Tool_GetMapPawns());
_tools.Add(new Tool_GetRecentNotifications());
_tools.Add(new Tool_CallBombardment());
_tools.Add(new Tool_SearchThingDef());
_tools.Add(new Tool_SearchPawnKind());
_tools.Add(new Tool_CallPrefabAirdrop());
// Agent 工具 - 纯视觉操作 (移除了 GetGameState, DesignateMine, DraftPawn)
// Agent 工具 - 保留画面分析截图能力,移除所有模拟操作工具
if (WulaFallenEmpireMod.settings?.enableVlmFeatures == true)
{
_tools.Add(new Tool_AnalyzeScreen());
_tools.Add(new Tool_VisualClick());
_tools.Add(new Tool_VisualScroll());
_tools.Add(new Tool_VisualTypeText());
_tools.Add(new Tool_VisualDrag());
_tools.Add(new Tool_VisualHotkey());
_tools.Add(new Tool_VisualWait());
_tools.Add(new Tool_VisualDeleteText());
}
}
@@ -482,6 +477,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"- send_reinforcement\n" +
"- call_bombardment\n" +
"- modify_goodwill\n" +
"- call_prefab_airdrop\n" +
"If no action is required, output exactly: <no_action/>.\n" +
"Query tools exist but are disabled in this phase (not listed here).\n"
: string.Empty;
@@ -497,8 +493,8 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
string actionWhitelist = phase == RequestPhase.ActionTools
? "ACTION PHASE VALID TAGS ONLY:\n" +
"<spawn_resources>, <send_reinforcement>, <call_bombardment>, <modify_goodwill>, <visual_click>, <visual_scroll>, <visual_type_text>, <visual_drag>, <visual_hotkey>, <visual_wait>, <visual_delete_text>, <no_action/>\n" +
"INVALID EXAMPLES (do NOT use now): <get_map_resources/>, JSON, Markdown Code Blocks\n"
"<spawn_resources>, <send_reinforcement>, <call_bombardment>, <modify_goodwill>, <call_prefab_airdrop>, <no_action/>\n" +
"INVALID EXAMPLES (do NOT use now): <get_map_resources/>, <analyze_screen/>\n"
: string.Empty;
return string.Join("\n\n", new[]
@@ -571,7 +567,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"Rules:\n" +
"- You MUST NOT write any natural language to the user in this phase.\n" +
"- Output XML tool calls only, or exactly: <no_action/>.\n" +
"- ONLY action tools are accepted in this phase (spawn_resources, send_reinforcement, call_bombardment, modify_goodwill, visual_click, visual_scroll, visual_type_text, visual_drag, visual_hotkey, visual_wait, visual_delete_text).\n" +
"- ONLY action tools are accepted in this phase (spawn_resources, send_reinforcement, call_bombardment, modify_goodwill, call_prefab_airdrop).\n" +
"- Query tools (get_*/search_*) will be ignored.\n" +
"- Prefer action tools (spawn_resources, send_reinforcement, call_bombardment, modify_goodwill).\n" +
"- Avoid queries unless absolutely required.\n" +
@@ -643,13 +639,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
toolName == "send_reinforcement" ||
toolName == "call_bombardment" ||
toolName == "modify_goodwill" ||
toolName == "visual_click" ||
toolName == "visual_scroll" ||
toolName == "visual_type_text" ||
toolName == "visual_drag" ||
toolName == "visual_hotkey" ||
toolName == "visual_wait" ||
toolName == "visual_delete_text";
toolName == "call_prefab_airdrop";
}
private static bool IsQueryToolName(string toolName)
@@ -839,15 +829,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
}
}
private bool CheckVisualIntent(string message)
{
if (string.IsNullOrEmpty(message)) return false;
string[] keywords = new string[] {
"屏幕", "画面", "截图", "看", "找", "显示", // CN
"screen", "screenshot", "image", "view", "look", "see", "find", "visual", "scan" // EN
};
return keywords.Any(k => message.IndexOf(k, StringComparison.OrdinalIgnoreCase) >= 0);
}
private async Task RunPhasedRequestAsync()
{
if (_isThinking) return;
@@ -880,7 +861,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
if (settings.enableVlmFeatures && settings.showThinkingProcess)
{
// Optional: We can still say "Analyzing data link..."
AddAssistantMessage("<i>[P.I.A] 正在分析数据链路...</i>");
AddAssistantMessage("<i>[P.I.A] 正在分析数据链路...</i>");
}
var queryPhase = RequestPhase.QueryTools;
@@ -914,7 +895,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
base64Image = queryResult.CapturedImage;
if (settings.showThinkingProcess)
{
AddAssistantMessage("<i>[P.I.A] 视觉传感器已激活,图像已捕获...</i>");
AddAssistantMessage("<i>[P.I.A] 视觉传感器已激活,图像已捕获...</i>");
}
}
@@ -965,7 +946,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
if (settings.showThinkingProcess)
{
AddAssistantMessage("<i>[P.I.A] 正在计算最优战术方案...</i>");
AddAssistantMessage("<i>[P.I.A] 正在计算最优战术方案...</i>");
}
var actionPhase = RequestPhase.ActionTools;
@@ -998,19 +979,14 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"Preserve the intent of the previous output.\n" +
"If the previous output indicates no action is needed or refuses action, output exactly: <no_action/>.\n" +
"Do NOT invent new actions.\n" +
"Output VALID XML tool calls only. No natural language, no commentary.\nIf the previous output contains JSON (including JSON code blocks or inline {\"point\": [x,y]}), convert it to XML.\n- For a point coordinate, output: <visual_click><x>...</x><y>...</y></visual_click>.\n- If coordinates are larger than 1 (e.g., 0-1000), normalize by dividing by 1000.\nIgnore any non-XML text.\n" +
"Allowed tags: <spawn_resources>, <send_reinforcement>, <call_bombardment>, <modify_goodwill>, <visual_click>, <visual_scroll>, <visual_type_text>, <no_action/>.\n" +
"Output VALID XML tool calls only. No natural language, no commentary.\nIgnore any non-XML text.\n" +
"Allowed tags: <spawn_resources>, <send_reinforcement>, <call_bombardment>, <modify_goodwill>, <call_prefab_airdrop>, <no_action/>.\n" +
"\nAction tool XML formats:\n" +
"- <spawn_resources><items><item><name>DefName</name><count>Int</count></item></items></spawn_resources>\n" +
"- <send_reinforcement><units>PawnKindDef: Count, ...</units></send_reinforcement>\n" +
"- <call_bombardment><abilityDef>DefName</abilityDef><x>Int</x><z>Int</z></call_bombardment>\n" +
"- <modify_goodwill><amount>Int</amount></modify_goodwill>\n" +
"- <visual_click><x>Float</x><y>Float</y></visual_click>\n" +
"- <visual_scroll><delta>Int</delta></visual_scroll>\n" +
"- <visual_type_text><text>String</text></visual_type_text>\n" +
"- <visual_drag><start_x>0-1</start_x><start_y>0-1</start_y><end_x>0-1</end_x><end_y>0-1</end_y></visual_drag>\n" +
"- <visual_hotkey><key>String (e.g. 'enter', 'esc', 'space')</key></visual_hotkey>\n" +
"- <visual_wait><seconds>Float</seconds></visual_wait>\n" +
"- <call_prefab_airdrop><prefabDefName>DefName</prefabDefName><x>Int</x><z>Int</z></call_prefab_airdrop>\n" +
"\nPrevious output:\n" + TrimForPrompt(actionResponse, 600);
string fixedResponse = await client.GetChatCompletionAsync(fixInstruction, actionContext, maxTokens: 2048, temperature: 0.1f);
bool fixedHasXml = !string.IsNullOrEmpty(fixedResponse) && IsXmlToolCall(fixedResponse);
@@ -1073,19 +1049,14 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
"Preserve the intent of the previous output.\n" +
"If the previous output indicates no action is needed or refuses action, output exactly: <no_action/>.\n" +
"Do NOT invent new actions.\n" +
"Output VALID XML tool calls only. No natural language, no commentary.\nIf the previous output contains JSON (including JSON code blocks or inline {\"point\": [x,y]}), convert it to XML.\n- For a point coordinate, output: <visual_click><x>...</x><y>...</y></visual_click>.\n- If coordinates are larger than 1 (e.g., 0-1000), normalize by dividing by 1000.\nIgnore any non-XML text.\n" +
"Allowed tags: <spawn_resources>, <send_reinforcement>, <call_bombardment>, <modify_goodwill>, <visual_click>, <visual_scroll>, <visual_type_text>, <no_action/>.\n" +
"Output VALID XML tool calls only. No natural language, no commentary.\nIgnore any non-XML text.\n" +
"Allowed tags: <spawn_resources>, <send_reinforcement>, <call_bombardment>, <modify_goodwill>, <call_prefab_airdrop>, <no_action/>.\n" +
"\nAction tool XML formats:\n" +
"- <spawn_resources><items><item><name>DefName</name><count>Int</count></item></items></spawn_resources>\n" +
"- <send_reinforcement><units>PawnKindDef: Count, ...</units></send_reinforcement>\n" +
"- <call_bombardment><abilityDef>DefName</abilityDef><x>Int</x><z>Int</z></call_bombardment>\n" +
"- <modify_goodwill><amount>Int</amount></modify_goodwill>\n" +
"- <visual_click><x>Float</x><y>Float</y></visual_click>\n" +
"- <visual_scroll><delta>Int</delta></visual_scroll>\n" +
"- <visual_type_text><text>String</text></visual_type_text>\n" +
"- <visual_drag><start_x>0-1</start_x><start_y>0-1</start_y><end_x>0-1</end_x><end_y>0-1</end_y></visual_drag>\n" +
"- <visual_hotkey><key>String</key></visual_hotkey>\n" +
"- <visual_wait><seconds>Float</seconds></visual_wait>\n" +
"- <call_prefab_airdrop><prefabDefName>DefName</prefabDefName><x>Int</x><z>Int</z></call_prefab_airdrop>\n" +
"\nPrevious output:\n" + TrimForPrompt(retryActionResponse, 600);
string retryFixedResponse = await client.GetChatCompletionAsync(retryFixInstruction, retryActionContext, maxTokens: 2048, temperature: 0.1f);
bool retryFixedHasXml = !string.IsNullOrEmpty(retryFixedResponse) && IsXmlToolCall(retryFixedResponse);
@@ -1151,7 +1122,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
if (settings.showThinkingProcess)
{
AddAssistantMessage("<i>[P.I.A] 正在汇总战报并建立通讯记录...</i>");
AddAssistantMessage("<i>[P.I.A] 正在汇总战报并建立通讯记录...</i>");
}
// VISUAL CONTEXT FOR REPLY: Pass the image so the AI can describe what it sees.
@@ -1212,52 +1183,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
string guidance = "ToolRunner Guidance: Reply to the player in natural language only. Do NOT output any XML. You may include [EXPR:n] to set expression (n=1-6).";
var matches = Regex.Matches(xml ?? "", @"<([a-zA-Z0-9_]+)(?:>.*?</\1>|/>)", RegexOptions.Singleline);
// GEMINI 3 JSON FALLBACK (Restored & Fixed)
// If no XML found, try to parse JSON markdown block commonly output by Gemini 3 Flash Preview
// Uses a 0-1000 coordinate system typically.
if (matches.Count == 0 && (xml ?? "").Contains("```json"))
{
try
{
var jsonMatch = Regex.Match(xml, @"```json\s*(\[.*?\])\s*```", RegexOptions.Singleline);
if (jsonMatch.Success)
{
string jsonArr = jsonMatch.Groups[1].Value;
// Regex to extract objects with "point" (and optional "action")
// Matches: { "point": [123, 456] }
// Note: In verbatim string @"""", double quotes are escaped as "". Not \".
var pointMatches = Regex.Matches(jsonArr, @"\{.*?\""point\""\s*:\s*\[\s*([\d\.]+)\s*,\s*([\d\.]+)\s*\].*?\}", RegexOptions.Singleline);
if (pointMatches.Count > 0)
{
StringBuilder synthesizedXml = new StringBuilder();
foreach (Match pm in pointMatches)
{
if (float.TryParse(pm.Groups[1].Value, out float xVal) && float.TryParse(pm.Groups[2].Value, out float yVal))
{
// Gemini uses 0-1000 scale usually
if (xVal > 1 || yVal > 1)
{
xVal /= 1000.0f;
yVal /= 1000.0f;
}
synthesizedXml.Append($"<visual_click><x>{xVal}</x><y>{yVal}</y></visual_click>");
}
}
if (synthesizedXml.Length > 0)
{
xml = synthesizedXml.ToString() + "\n" + xml; // Prepend to process downstream
matches = Regex.Matches(xml, @"<([a-zA-Z0-9_]+)(?:>.*?</\1>|/>)", RegexOptions.Singleline);
}
}
}
}
catch (Exception ex)
{
WulaLog.Debug($"[JSON Fallback Error] {ex.Message}");
}
}
if (matches.Count == 0 || (matches.Count == 1 && matches[0].Groups[1].Value.Equals("no_action", StringComparison.OrdinalIgnoreCase)))
{