zc
This commit is contained in:
Binary file not shown.
@@ -57,7 +57,7 @@ namespace WulaFallenEmpire.EventSystem.AI
|
|||||||
private const int CharsPerToken = 4;
|
private const int CharsPerToken = 4;
|
||||||
private const int DefaultReactMaxSteps = 4;
|
private const int DefaultReactMaxSteps = 4;
|
||||||
private const int ReactMaxToolsPerStep = 8;
|
private const int ReactMaxToolsPerStep = 8;
|
||||||
private const float DefaultReactMaxSeconds = 12f;
|
private const float DefaultReactMaxSeconds = 30f;
|
||||||
private int _thinkingPhaseTotal = DefaultReactMaxSteps;
|
private int _thinkingPhaseTotal = DefaultReactMaxSteps;
|
||||||
|
|
||||||
private static readonly Regex ExpressionTagRegex = new Regex(@"\[EXPR\s*:\s*([1-6])\s*\]", RegexOptions.IgnoreCase);
|
private static readonly Regex ExpressionTagRegex = new Regex(@"\[EXPR\s*:\s*([1-6])\s*\]", RegexOptions.IgnoreCase);
|
||||||
@@ -111,7 +111,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
- Do NOT split multi-item requests across turns.
|
- Do NOT split multi-item requests across turns.
|
||||||
5. **TOOLS**: You MAY call any tools listed in ""# TOOLS (AVAILABLE)"".
|
5. **TOOLS**: You MAY call any tools listed in ""# TOOLS (AVAILABLE)"".
|
||||||
6. **ANTI-HALLUCINATION**: Never invent tools, parameters, defNames, coordinates, or tool results. If a tool is needed but not available, output { ""tool_calls"": [] }.
|
6. **ANTI-HALLUCINATION**: Never invent tools, parameters, defNames, coordinates, or tool results. If a tool is needed but not available, output { ""tool_calls"": [] }.
|
||||||
7. **STEP BUDGET (OPTIONAL)**: You MAY include { ""meta"": { ""step_budget"": <positive integer> } } to request more tool steps for this turn.
|
7. **WORKFLOW PREFERENCE**: Prefer the flow Query tools → Action tools → Reply. If action results reveal missing info, you MAY return to Query and then Action again.
|
||||||
8. **NO TAGS**: Do NOT use <think> tags, code fences, or any extra text outside JSON.";
|
8. **NO TAGS**: Do NOT use <think> tags, code fences, or any extra text outside JSON.";
|
||||||
|
|
||||||
public AIIntelligenceCore(World world) : base(world)
|
public AIIntelligenceCore(World world) : base(world)
|
||||||
@@ -454,7 +454,14 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
|
|
||||||
private void SetThinkingPhase(int phaseIndex, bool isRetry)
|
private void SetThinkingPhase(int phaseIndex, bool isRetry)
|
||||||
{
|
{
|
||||||
_thinkingPhaseIndex = Math.Max(1, Math.Min(_thinkingPhaseTotal, phaseIndex));
|
if (_thinkingPhaseTotal <= 0 || _thinkingPhaseTotal == int.MaxValue)
|
||||||
|
{
|
||||||
|
_thinkingPhaseIndex = Math.Max(1, phaseIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_thinkingPhaseIndex = Math.Max(1, Math.Min(_thinkingPhaseTotal, phaseIndex));
|
||||||
|
}
|
||||||
_thinkingPhaseRetry = isRetry;
|
_thinkingPhaseRetry = isRetry;
|
||||||
_thinkingStartTime = Time.realtimeSinceStartup;
|
_thinkingStartTime = Time.realtimeSinceStartup;
|
||||||
}
|
}
|
||||||
@@ -664,7 +671,7 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
$"Final replies are generated later and MUST use: {language}.";
|
$"Final replies are generated later and MUST use: {language}.";
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetNativeSystemInstruction()
|
private string GetNativeSystemInstruction(RequestPhase phase)
|
||||||
{
|
{
|
||||||
string persona = GetActivePersona();
|
string persona = GetActivePersona();
|
||||||
string personaBlock = persona;
|
string personaBlock = persona;
|
||||||
@@ -684,6 +691,21 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
sb.AppendLine(goodwillContext);
|
sb.AppendLine(goodwillContext);
|
||||||
sb.AppendLine($"IMPORTANT: Reply in the following language: {language}.");
|
sb.AppendLine($"IMPORTANT: Reply in the following language: {language}.");
|
||||||
sb.AppendLine("IMPORTANT: Use tools to fetch in-game data or perform actions. Do NOT invent tool results.");
|
sb.AppendLine("IMPORTANT: Use tools to fetch in-game data or perform actions. Do NOT invent tool results.");
|
||||||
|
sb.AppendLine("IMPORTANT: Tool workflow is fixed: Phase 1 = Query Tools, Phase 2 = Action Tools, Phase 3 = Reply.");
|
||||||
|
switch (phase)
|
||||||
|
{
|
||||||
|
case RequestPhase.QueryTools:
|
||||||
|
sb.AppendLine("CURRENT PHASE: Query Tools. Use ONLY query tools (get_*/search_*/analyze_*/recall_memories).");
|
||||||
|
sb.AppendLine("Do NOT reply in natural language. If no query tools are needed, return no tool calls and leave content empty.");
|
||||||
|
break;
|
||||||
|
case RequestPhase.ActionTools:
|
||||||
|
sb.AppendLine("CURRENT PHASE: Action Tools. Use ONLY action tools (spawn_resources, send_reinforcement, call_bombardment, modify_goodwill, call_prefab_airdrop, set_overwatch_mode, remember_fact).");
|
||||||
|
sb.AppendLine("Do NOT reply in natural language. If no actions are needed, return no tool calls and leave content empty.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
sb.AppendLine("CURRENT PHASE: Reply. Do NOT call any tools. Reply in natural language only.");
|
||||||
|
break;
|
||||||
|
}
|
||||||
sb.AppendLine("IMPORTANT: Long-term memory is not preloaded. Use recall_memories to fetch memories when needed.");
|
sb.AppendLine("IMPORTANT: Long-term memory is not preloaded. Use recall_memories to fetch memories when needed.");
|
||||||
sb.AppendLine("IMPORTANT: When the user asks for an item by name, call search_thing_def to confirm the exact defName before spawning.");
|
sb.AppendLine("IMPORTANT: When the user asks for an item by name, call search_thing_def to confirm the exact defName before spawning.");
|
||||||
sb.AppendLine("You MAY include [EXPR:n] (n=1-6) to set your expression.");
|
sb.AppendLine("You MAY include [EXPR:n] (n=1-6) to set your expression.");
|
||||||
@@ -822,10 +844,15 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
return sb.ToString().TrimEnd();
|
return sb.ToString().TrimEnd();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Dictionary<string, object>> BuildNativeToolDefinitions()
|
private List<Dictionary<string, object>> BuildNativeToolDefinitions(RequestPhase phase)
|
||||||
{
|
{
|
||||||
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)
|
||||||
|
: false)
|
||||||
.OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase)
|
.OrderBy(t => t.Name, StringComparer.OrdinalIgnoreCase)
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
@@ -943,27 +970,14 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int? ExtractStepBudget(string json)
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(json)) return null;
|
|
||||||
var match = Regex.Match(json, "\"step_budget\"\\s*:\\s*\"?(\\d+)\"?", RegexOptions.IgnoreCase);
|
|
||||||
if (!match.Success) return null;
|
|
||||||
if (int.TryParse(match.Groups[1].Value, NumberStyles.Integer, CultureInfo.InvariantCulture, out int value))
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string BuildReactFormatFixInstruction(string previousOutput)
|
private static string BuildReactFormatFixInstruction(string previousOutput)
|
||||||
{
|
{
|
||||||
return "# FORMAT FIX (REACT JSON ONLY)\n" +
|
return "# FORMAT FIX (REACT JSON ONLY)\n" +
|
||||||
"Output valid JSON with fields thought/tool_calls.\n" +
|
"Output valid JSON with fields thought/tool_calls.\n" +
|
||||||
"If tools are needed, tool_calls must be non-empty.\n" +
|
"If tools are needed, tool_calls must be non-empty.\n" +
|
||||||
"If no tools are needed, output exactly: {\"tool_calls\": []} (you may include thought).\n" +
|
"If no tools are needed, output exactly: {\"tool_calls\": []} (you may include thought).\n" +
|
||||||
"You MAY include an optional meta block: {\"meta\":{\"step_budget\":<positive integer>}}.\n" +
|
|
||||||
"Do NOT output any text outside JSON.\n" +
|
"Do NOT output any text outside JSON.\n" +
|
||||||
"Schema: {\"thought\":\"...\",\"tool_calls\":[{\"type\":\"function\",\"function\":{\"name\":\"tool_name\",\"arguments\":{...}}}],\"meta\":{\"step_budget\":7}}\n" +
|
"Schema: {\"thought\":\"...\",\"tool_calls\":[{\"type\":\"function\",\"function\":{\"name\":\"tool_name\",\"arguments\":{...}}}]}\n" +
|
||||||
"\nPrevious output:\n" + TrimForPrompt(previousOutput, 600);
|
"\nPrevious output:\n" + TrimForPrompt(previousOutput, 600);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1007,9 +1021,8 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
"You used tool names that are NOT available: " + invalidList + "\n" +
|
"You used tool names that are NOT available: " + invalidList + "\n" +
|
||||||
"Re-emit JSON with only available tools from # TOOLS (AVAILABLE).\n" +
|
"Re-emit JSON with only available tools from # TOOLS (AVAILABLE).\n" +
|
||||||
"If no tools are needed, output exactly: {\"tool_calls\": []}.\n" +
|
"If no tools are needed, output exactly: {\"tool_calls\": []}.\n" +
|
||||||
"You MAY include an optional meta block: {\"meta\":{\"step_budget\":<positive integer>}}.\n" +
|
|
||||||
"Do NOT output any text outside JSON.\n" +
|
"Do NOT output any text outside JSON.\n" +
|
||||||
"Schema: {\"thought\":\"...\",\"tool_calls\":[{\"type\":\"function\",\"function\":{\"name\":\"tool_name\",\"arguments\":{...}}}],\"meta\":{\"step_budget\":7}}";
|
"Schema: {\"thought\":\"...\",\"tool_calls\":[{\"type\":\"function\",\"function\":{\"name\":\"tool_name\",\"arguments\":{...}}}]}";
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool ShouldRetryTools(string response)
|
private static bool ShouldRetryTools(string response)
|
||||||
@@ -1924,28 +1937,18 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
var client = new SimpleAIClient(apiKey, baseUrl, model, settings.useGeminiProtocol);
|
var client = new SimpleAIClient(apiKey, baseUrl, model, settings.useGeminiProtocol);
|
||||||
_currentClient = client;
|
_currentClient = client;
|
||||||
|
|
||||||
if (!settings.useGeminiProtocol)
|
// ReAct Tool Loop: Start with null image. The model must request it using analyze_screen or capture_screen if needed.
|
||||||
{
|
|
||||||
await RunNativeToolLoopAsync(client, settings);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Model-Driven Vision: Start with null image. The model must request it using analyze_screen or capture_screen if needed.
|
|
||||||
string base64Image = null;
|
string base64Image = null;
|
||||||
float startTime = Time.realtimeSinceStartup;
|
float startTime = Time.realtimeSinceStartup;
|
||||||
int maxSteps = DefaultReactMaxSteps;
|
int maxSteps = int.MaxValue;
|
||||||
float maxSeconds = DefaultReactMaxSeconds;
|
float maxSeconds = DefaultReactMaxSeconds;
|
||||||
|
|
||||||
int stepBudgetMax = int.MaxValue;
|
|
||||||
if (settings != null)
|
if (settings != null)
|
||||||
{
|
{
|
||||||
stepBudgetMax = Math.Max(1, settings.reactMaxStepsMax);
|
maxSeconds = Math.Max(2f, settings.reactMaxSeconds <= 0f ? DefaultReactMaxSeconds : settings.reactMaxSeconds);
|
||||||
maxSteps = Math.Max(1, settings.reactMaxSteps);
|
|
||||||
maxSeconds = Math.Max(2f, settings.reactMaxSeconds);
|
|
||||||
}
|
}
|
||||||
_thinkingPhaseTotal = maxSteps;
|
_thinkingPhaseTotal = 0;
|
||||||
string toolPhaseReplyCandidate = null;
|
string toolPhaseReplyCandidate = null;
|
||||||
bool budgetApplied = false;
|
|
||||||
for (int step = 1; step <= maxSteps; step++)
|
for (int step = 1; step <= maxSteps; step++)
|
||||||
{
|
{
|
||||||
if (Time.realtimeSinceStartup - startTime > maxSeconds)
|
if (Time.realtimeSinceStartup - startTime > maxSeconds)
|
||||||
@@ -2003,26 +2006,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
parsedSource = normalizedFixed;
|
parsedSource = normalizedFixed;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!budgetApplied && !string.IsNullOrWhiteSpace(jsonFragment))
|
|
||||||
{
|
|
||||||
string budgetSource = jsonFragment;
|
|
||||||
int? requested = ExtractStepBudget(budgetSource);
|
|
||||||
if (!requested.HasValue)
|
|
||||||
{
|
|
||||||
requested = ExtractStepBudget(parsedSource);
|
|
||||||
}
|
|
||||||
if (requested.HasValue)
|
|
||||||
{
|
|
||||||
budgetApplied = true;
|
|
||||||
int clamped = Math.Max(1, Math.Min(stepBudgetMax, requested.Value));
|
|
||||||
if (clamped > maxSteps)
|
|
||||||
{
|
|
||||||
maxSteps = clamped;
|
|
||||||
_thinkingPhaseTotal = maxSteps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(parsedSource))
|
if (!string.IsNullOrWhiteSpace(parsedSource))
|
||||||
{
|
{
|
||||||
string traceText = parsedSource;
|
string traceText = parsedSource;
|
||||||
@@ -2059,25 +2042,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
if (!string.IsNullOrEmpty(normalizedCorrected) &&
|
if (!string.IsNullOrEmpty(normalizedCorrected) &&
|
||||||
JsonToolCallParser.TryParseToolCallsFromText(normalizedCorrected, out toolCalls, out jsonFragment))
|
JsonToolCallParser.TryParseToolCallsFromText(normalizedCorrected, out toolCalls, out jsonFragment))
|
||||||
{
|
{
|
||||||
if (!budgetApplied && !string.IsNullOrWhiteSpace(jsonFragment))
|
|
||||||
{
|
|
||||||
string budgetSource = jsonFragment;
|
|
||||||
int? requested = ExtractStepBudget(budgetSource);
|
|
||||||
if (!requested.HasValue)
|
|
||||||
{
|
|
||||||
requested = ExtractStepBudget(normalizedCorrected);
|
|
||||||
}
|
|
||||||
if (requested.HasValue)
|
|
||||||
{
|
|
||||||
budgetApplied = true;
|
|
||||||
int clamped = Math.Max(1, Math.Min(stepBudgetMax, requested.Value));
|
|
||||||
if (clamped > maxSteps)
|
|
||||||
{
|
|
||||||
maxSteps = clamped;
|
|
||||||
_thinkingPhaseTotal = maxSteps;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parsedSource = normalizedCorrected;
|
parsedSource = normalizedCorrected;
|
||||||
|
|
||||||
invalidTools = toolCalls
|
invalidTools = toolCalls
|
||||||
@@ -2216,9 +2180,8 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
|
|
||||||
private async Task RunNativeToolLoopAsync(SimpleAIClient client, WulaFallenEmpireSettings settings)
|
private async Task RunNativeToolLoopAsync(SimpleAIClient client, WulaFallenEmpireSettings settings)
|
||||||
{
|
{
|
||||||
string systemInstruction = GetNativeSystemInstruction();
|
|
||||||
var messages = BuildNativeHistory();
|
var messages = BuildNativeHistory();
|
||||||
var tools = BuildNativeToolDefinitions();
|
RequestPhase phase = RequestPhase.QueryTools;
|
||||||
|
|
||||||
string finalReply = null;
|
string finalReply = null;
|
||||||
var successfulQueryTools = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var successfulQueryTools = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
@@ -2226,17 +2189,15 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
var failedActionTools = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
var failedActionTools = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
|
||||||
float startTime = Time.realtimeSinceStartup;
|
float startTime = Time.realtimeSinceStartup;
|
||||||
int maxSteps = Math.Max(1, settings.reactMaxSteps);
|
int maxSteps = int.MaxValue;
|
||||||
float maxSeconds = Math.Max(2f, settings.reactMaxSeconds);
|
float maxSeconds = Math.Max(2f, settings.reactMaxSeconds <= 0f ? DefaultReactMaxSeconds : settings.reactMaxSeconds);
|
||||||
_thinkingPhaseTotal = maxSteps;
|
_thinkingPhaseTotal = 3;
|
||||||
int strictRetryCount = 0;
|
int strictRetryCount = 0;
|
||||||
|
int phaseRetryCount = 0;
|
||||||
const int MaxStrictRetries = 2;
|
const int MaxStrictRetries = 2;
|
||||||
const string StrictRetryGuidance =
|
const int MaxPhaseRetries = 2;
|
||||||
"ToolRunner Error: Your last response was rejected because tool_calls was empty. " +
|
|
||||||
"You MUST call tools via the tool_calls field. Do NOT output XML or natural language. " +
|
|
||||||
"If no tools are needed, return no tool calls and leave content empty.";
|
|
||||||
|
|
||||||
for (int step = 1; step <= maxSteps; step++)
|
for (int step = 1; step <= maxSteps && phase != RequestPhase.Reply; step++)
|
||||||
{
|
{
|
||||||
if (Time.realtimeSinceStartup - startTime > maxSeconds)
|
if (Time.realtimeSinceStartup - startTime > maxSeconds)
|
||||||
{
|
{
|
||||||
@@ -2247,8 +2208,10 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
SetThinkingPhase(step, false);
|
SetThinkingPhase(phase == RequestPhase.QueryTools ? 1 : 2, false);
|
||||||
|
|
||||||
|
string systemInstruction = GetNativeSystemInstruction(phase);
|
||||||
|
var tools = BuildNativeToolDefinitions(phase);
|
||||||
ChatCompletionResult result = await client.GetChatCompletionWithToolsAsync(
|
ChatCompletionResult result = await client.GetChatCompletionWithToolsAsync(
|
||||||
systemInstruction,
|
systemInstruction,
|
||||||
messages,
|
messages,
|
||||||
@@ -2264,15 +2227,26 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
|
|
||||||
if (result.ToolCalls == null || result.ToolCalls.Count == 0)
|
if (result.ToolCalls == null || result.ToolCalls.Count == 0)
|
||||||
{
|
{
|
||||||
if (strictRetryCount < MaxStrictRetries)
|
if (!string.IsNullOrWhiteSpace(result.Content) && strictRetryCount < MaxStrictRetries)
|
||||||
{
|
{
|
||||||
strictRetryCount++;
|
strictRetryCount++;
|
||||||
messages.Add(ChatMessage.User(StrictRetryGuidance));
|
string strictRetryGuidance = phase == RequestPhase.QueryTools
|
||||||
|
? "ToolRunner Error: This is Query phase. You MUST call query tools using tool_calls. Do NOT output XML or natural language. If no query tools are needed, return no tool calls and leave content empty."
|
||||||
|
: "ToolRunner Error: This is Action phase. You MUST call action tools using tool_calls. Do NOT output XML or natural language. If no actions are needed, return no tool calls and leave content empty.";
|
||||||
|
messages.Add(ChatMessage.User(strictRetryGuidance));
|
||||||
if (Prefs.DevMode)
|
if (Prefs.DevMode)
|
||||||
{
|
{
|
||||||
WulaLog.Debug($"[WulaAI] Native tool loop retry: missing tool_calls (attempt {strictRetryCount}/{MaxStrictRetries}).");
|
WulaLog.Debug($"[WulaAI] Native tool loop retry: missing tool_calls (attempt {strictRetryCount}/{MaxStrictRetries}).");
|
||||||
}
|
}
|
||||||
step--;
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
strictRetryCount = 0;
|
||||||
|
phaseRetryCount = 0;
|
||||||
|
|
||||||
|
if (phase == RequestPhase.QueryTools)
|
||||||
|
{
|
||||||
|
phase = RequestPhase.ActionTools;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2280,9 +2254,35 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
}
|
}
|
||||||
|
|
||||||
strictRetryCount = 0;
|
strictRetryCount = 0;
|
||||||
|
phaseRetryCount = 0;
|
||||||
|
|
||||||
if (result.ToolCalls != null && result.ToolCalls.Count > 0)
|
if (result.ToolCalls != null && result.ToolCalls.Count > 0)
|
||||||
{
|
{
|
||||||
|
var invalidTools = result.ToolCalls
|
||||||
|
.Where(c => c != null && !string.IsNullOrWhiteSpace(c.Name))
|
||||||
|
.Select(c => c.Name)
|
||||||
|
.Where(n => phase == RequestPhase.QueryTools ? !IsQueryToolName(n) : !IsActionToolName(n))
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.ToList();
|
||||||
|
if (invalidTools.Count > 0)
|
||||||
|
{
|
||||||
|
if (phaseRetryCount < MaxPhaseRetries)
|
||||||
|
{
|
||||||
|
phaseRetryCount++;
|
||||||
|
string invalidList = string.Join(", ", invalidTools);
|
||||||
|
string guidance = phase == RequestPhase.QueryTools
|
||||||
|
? $"ToolRunner Error: Query phase only allows query tools. Invalid: {invalidList}. Re-issue tool_calls with query tools only."
|
||||||
|
: $"ToolRunner Error: Action phase only allows action tools. Invalid: {invalidList}. Re-issue tool_calls with action tools only.";
|
||||||
|
messages.Add(ChatMessage.User(guidance));
|
||||||
|
if (Prefs.DevMode)
|
||||||
|
{
|
||||||
|
WulaLog.Debug($"[WulaAI] Native tool loop retry: invalid tools ({invalidList}).");
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
int maxTools = ReactMaxToolsPerStep;
|
int maxTools = ReactMaxToolsPerStep;
|
||||||
var callsToExecute = result.ToolCalls.Count > maxTools
|
var callsToExecute = result.ToolCalls.Count > maxTools
|
||||||
? result.ToolCalls.Take(maxTools).ToList()
|
? result.ToolCalls.Take(maxTools).ToList()
|
||||||
@@ -2476,7 +2476,6 @@ You are 'The Legion', a super AI of the Wula Empire. Your personality is authori
|
|||||||
{
|
{
|
||||||
string guidance = "ToolRunner Guidance: Continue with JSON only using {\"thought\":\"...\",\"tool_calls\":[...]}. " +
|
string guidance = "ToolRunner Guidance: Continue with JSON only using {\"thought\":\"...\",\"tool_calls\":[...]}. " +
|
||||||
"If no more tools are needed, output exactly: {\"tool_calls\": []}. " +
|
"If no more tools are needed, output exactly: {\"tool_calls\": []}. " +
|
||||||
"You MAY include {\"meta\":{\"step_budget\":<positive integer>}} to request more steps. " +
|
|
||||||
"Do NOT output any text outside JSON.";
|
"Do NOT output any text outside JSON.";
|
||||||
|
|
||||||
if (!JsonToolCallParser.TryParseToolCallsFromText(json ?? "", out var toolCalls, out string jsonFragment))
|
if (!JsonToolCallParser.TryParseToolCallsFromText(json ?? "", out var toolCalls, out string jsonFragment))
|
||||||
|
|||||||
@@ -805,7 +805,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
? Mathf.Max(0f, Time.realtimeSinceStartup - (_core?.ThinkingStartTime ?? 0f))
|
? Mathf.Max(0f, Time.realtimeSinceStartup - (_core?.ThinkingStartTime ?? 0f))
|
||||||
: _core?.LastThinkingDuration ?? 0f;
|
: _core?.LastThinkingDuration ?? 0f;
|
||||||
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", CultureInfo.InvariantCulture) : "0.0";
|
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", CultureInfo.InvariantCulture) : "0.0";
|
||||||
return $"{state} (用时 {elapsedText}s · Loop {_core?.ThinkingPhaseIndex ?? 0}/{_core?.ThinkingPhaseTotal ?? 0})";
|
return $"{state} (用时 {elapsedText}s · Loop {_core?.ThinkingPhaseIndex ?? 0})";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawReactTracePanel(Rect rect, CachedMessage traceEntry)
|
private void DrawReactTracePanel(Rect rect, CachedMessage traceEntry)
|
||||||
@@ -852,7 +852,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
if (_core == null) return "Thinking...";
|
if (_core == null) return "Thinking...";
|
||||||
float elapsedSeconds = Mathf.Max(0f, Time.realtimeSinceStartup - _core.ThinkingStartTime);
|
float elapsedSeconds = Mathf.Max(0f, Time.realtimeSinceStartup - _core.ThinkingStartTime);
|
||||||
string elapsedText = elapsedSeconds.ToString("0.0", CultureInfo.InvariantCulture);
|
string elapsedText = elapsedSeconds.ToString("0.0", CultureInfo.InvariantCulture);
|
||||||
return $"P.I.A is thinking... ({elapsedText}s Loop {_core.ThinkingPhaseIndex}/{_core.ThinkingPhaseTotal})";
|
return $"P.I.A is thinking... ({elapsedText}s Loop {_core.ThinkingPhaseIndex})";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawThinkingIndicator(Rect rect)
|
private void DrawThinkingIndicator(Rect rect)
|
||||||
|
|||||||
@@ -849,8 +849,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
: _core?.LastThinkingDuration ?? 0f;
|
: _core?.LastThinkingDuration ?? 0f;
|
||||||
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) : "0.0";
|
string elapsedText = elapsed > 0f ? elapsed.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture) : "0.0";
|
||||||
int phaseIndex = _core?.ThinkingPhaseIndex ?? 0;
|
int phaseIndex = _core?.ThinkingPhaseIndex ?? 0;
|
||||||
int phaseTotal = _core?.ThinkingPhaseTotal ?? 0;
|
return $"{state} (用时 {elapsedText}s · Loop {phaseIndex})";
|
||||||
return $"{state} (用时 {elapsedText}s · Loop {phaseIndex}/{phaseTotal})";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawReactTracePanel(Rect rect, CachedMessage traceEntry)
|
private void DrawReactTracePanel(Rect rect, CachedMessage traceEntry)
|
||||||
@@ -1037,7 +1036,7 @@ namespace WulaFallenEmpire.EventSystem.AI.UI
|
|||||||
if (_core == null) return "Thinking...";
|
if (_core == null) return "Thinking...";
|
||||||
float elapsedSeconds = Mathf.Max(0f, Time.realtimeSinceStartup - _core.ThinkingStartTime);
|
float elapsedSeconds = Mathf.Max(0f, Time.realtimeSinceStartup - _core.ThinkingStartTime);
|
||||||
string elapsedText = elapsedSeconds.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture);
|
string elapsedText = elapsedSeconds.ToString("0.0", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
return $"P.I.A is thinking... ({elapsedText}s Loop {_core.ThinkingPhaseIndex}/{_core.ThinkingPhaseTotal})";
|
return $"P.I.A is thinking... ({elapsedText}s Loop {_core.ThinkingPhaseIndex})";
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawThinkingIndicator(Rect rect)
|
private void DrawThinkingIndicator(Rect rect)
|
||||||
|
|||||||
@@ -14,8 +14,6 @@ namespace WulaFallenEmpire
|
|||||||
public static bool _showApiKey = false;
|
public static bool _showApiKey = false;
|
||||||
public static bool _showVlmApiKey = false;
|
public static bool _showVlmApiKey = false;
|
||||||
private string _maxContextTokensBuffer;
|
private string _maxContextTokensBuffer;
|
||||||
private string _reactMaxStepsBuffer;
|
|
||||||
private string _reactMaxStepsMaxBuffer;
|
|
||||||
private string _reactMaxSecondsBuffer;
|
private string _reactMaxSecondsBuffer;
|
||||||
|
|
||||||
public WulaFallenEmpireMod(ModContentPack content) : base(content)
|
public WulaFallenEmpireMod(ModContentPack content) : base(content)
|
||||||
@@ -99,14 +97,7 @@ namespace WulaFallenEmpire
|
|||||||
|
|
||||||
listingStandard.GapLine();
|
listingStandard.GapLine();
|
||||||
listingStandard.Label("<color=cyan>ReAct Loop Settings</color>");
|
listingStandard.Label("<color=cyan>ReAct Loop Settings</color>");
|
||||||
listingStandard.Label("Default Steps (min 1):");
|
listingStandard.Label("Steps: Unlimited (step limit removed).");
|
||||||
Rect stepsRect = listingStandard.GetRect(Text.LineHeight);
|
|
||||||
Widgets.TextFieldNumeric(stepsRect, ref settings.reactMaxSteps, ref _reactMaxStepsBuffer, 1, int.MaxValue);
|
|
||||||
|
|
||||||
listingStandard.Label("Max Steps Limit (step_budget upper bound, min 1):");
|
|
||||||
Rect stepsMaxRect = listingStandard.GetRect(Text.LineHeight);
|
|
||||||
Widgets.TextFieldNumeric(stepsMaxRect, ref settings.reactMaxStepsMax, ref _reactMaxStepsMaxBuffer, 1, int.MaxValue);
|
|
||||||
|
|
||||||
listingStandard.Label("Max Seconds (min 2):");
|
listingStandard.Label("Max Seconds (min 2):");
|
||||||
Rect secondsRect = listingStandard.GetRect(Text.LineHeight);
|
Rect secondsRect = listingStandard.GetRect(Text.LineHeight);
|
||||||
Widgets.TextFieldNumeric(secondsRect, ref settings.reactMaxSeconds, ref _reactMaxSecondsBuffer, 10f, 600f);
|
Widgets.TextFieldNumeric(secondsRect, ref settings.reactMaxSeconds, ref _reactMaxSecondsBuffer, 10f, 600f);
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ namespace WulaFallenEmpire
|
|||||||
public float aiCommentaryChance = 0.7f;
|
public float aiCommentaryChance = 0.7f;
|
||||||
public bool commentOnNegativeOnly = false;
|
public bool commentOnNegativeOnly = false;
|
||||||
public string extraPersonalityPrompt = "";
|
public string extraPersonalityPrompt = "";
|
||||||
public int reactMaxSteps = 4;
|
public int reactMaxSteps = 0; // Deprecated: step limit removed (unlimited).
|
||||||
public int reactMaxStepsMax = 7;
|
public int reactMaxStepsMax = 0; // Deprecated: step limit removed (unlimited).
|
||||||
public float reactMaxSeconds = 60f;
|
public float reactMaxSeconds = 30f;
|
||||||
public bool showReactTraceInUI = false;
|
public bool showReactTraceInUI = false;
|
||||||
|
|
||||||
public override void ExposeData()
|
public override void ExposeData()
|
||||||
@@ -48,9 +48,9 @@ namespace WulaFallenEmpire
|
|||||||
Scribe_Values.Look(ref aiCommentaryChance, "aiCommentaryChance", 0.7f);
|
Scribe_Values.Look(ref aiCommentaryChance, "aiCommentaryChance", 0.7f);
|
||||||
Scribe_Values.Look(ref commentOnNegativeOnly, "commentOnNegativeOnly", false);
|
Scribe_Values.Look(ref commentOnNegativeOnly, "commentOnNegativeOnly", false);
|
||||||
Scribe_Values.Look(ref extraPersonalityPrompt, "extraPersonalityPrompt", "");
|
Scribe_Values.Look(ref extraPersonalityPrompt, "extraPersonalityPrompt", "");
|
||||||
Scribe_Values.Look(ref reactMaxSteps, "reactMaxSteps", 4);
|
Scribe_Values.Look(ref reactMaxSteps, "reactMaxSteps", 0);
|
||||||
Scribe_Values.Look(ref reactMaxStepsMax, "reactMaxStepsMax", 7);
|
Scribe_Values.Look(ref reactMaxStepsMax, "reactMaxStepsMax", 0);
|
||||||
Scribe_Values.Look(ref reactMaxSeconds, "reactMaxSeconds", 60f);
|
Scribe_Values.Look(ref reactMaxSeconds, "reactMaxSeconds", 30f);
|
||||||
Scribe_Values.Look(ref showReactTraceInUI, "showReactTraceInUI", false);
|
Scribe_Values.Look(ref showReactTraceInUI, "showReactTraceInUI", false);
|
||||||
|
|
||||||
base.ExposeData();
|
base.ExposeData();
|
||||||
|
|||||||
Reference in New Issue
Block a user