diff options
Diffstat (limited to 'src/Agent.Core/Runtime/AgentRuntime.cs')
| -rw-r--r-- | src/Agent.Core/Runtime/AgentRuntime.cs | 25 |
1 files changed, 15 insertions, 10 deletions
diff --git a/src/Agent.Core/Runtime/AgentRuntime.cs b/src/Agent.Core/Runtime/AgentRuntime.cs index 9c051e9..4aaf646 100644 --- a/src/Agent.Core/Runtime/AgentRuntime.cs +++ b/src/Agent.Core/Runtime/AgentRuntime.cs @@ -228,6 +228,7 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag var guard = new ToolUseGuard(settings.SameFailureLimit); bool requiresExternalEffect = plannerTask.RequiresExternalEffect; bool externalEffectEvidence = false; + var successfulRequiredTools = new HashSet<string>(StringComparer.Ordinal); var unresolvedToolFailures = new Dictionary<string, string>(StringComparer.Ordinal); string? finalContent = null; @@ -266,11 +267,11 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag foreach (OllamaToolCall call in calls) { ToolExecutionResult toolResult; - bool outOfScopeEffect = IsOutOfScopeEffectCall(call, plannerTask.RequiredEffectTools); + bool outOfScopeEffect = IsOutOfScopeEffectCall(call, requiresExternalEffect, plannerTask.RequiredTools); if (outOfScopeEffect) { toolResult = new(false, - $"The '{call.Function.Name}' external effect belongs to another Planner Task. Perform only the current Task; required effect Tool(s): {string.Join(", ", plannerTask.RequiredEffectTools.Order(StringComparer.Ordinal))}.", + $"The '{call.Function.Name}' external effect belongs to another Planner Task. Perform only the current Task; required Tool(s): {string.Join(", ", plannerTask.RequiredTools.Order(StringComparer.Ordinal))}.", false, $"task_scope:{plannerTask.Id}:{call.Function.Name}"); } else try @@ -286,14 +287,16 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag toolResult = new(false, ex.Message, false, $"{call.Function.Name}:{ex.GetType().Name}:{ex.Message}"); } - externalEffectEvidence |= toolResult.Success && toolResult.ExternalEffect && plannerTask.RequiredEffectTools.Contains(call.Function.Name); + if (toolResult.Success && plannerTask.RequiredTools.Contains(call.Function.Name)) successfulRequiredTools.Add(call.Function.Name); + externalEffectEvidence |= toolResult.Success && toolResult.ExternalEffect && plannerTask.RequiredTools.Contains(call.Function.Name); // task_update is a metadata tool for graph bookkeeping; its failures should not block completion. bool isMetadataTool = call.Function.Name == "task_update"; if (toolResult.Success) unresolvedToolFailures.Remove(call.Function.Name); else if (!isMetadataTool && !outOfScopeEffect) unresolvedToolFailures[call.Function.Name] = toolResult.FailureFingerprint ?? toolResult.Output; // Record ExecutionResult in session for context summary. - session.AppendExecutionResult(new ExecutionResult(taskId, call.Function.Name, toolResult.Success, toolResult.Output)); + if (!outOfScopeEffect) + session.AppendExecutionResult(new ExecutionResult(taskId, call.Function.Name, toolResult.Success, toolResult.Output)); _observer.ToolResult(callContext, call.Function.Name, toolResult); if (!toolResult.Success && !toolResult.RetryAllowed && !isMetadataTool) @@ -303,11 +306,12 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag failureFingerprint = toolResult.FailureFingerprint ?? failureFingerprint; messages.Add(new OllamaMessage("tool", toolResult.Output, ToolName: call.Function.Name)); } - ToolUseGuardResult callGuard = guard.Evaluate(requiresExternalEffect, true, externalEffectEvidence, failureFingerprint); + bool verified = plannerTask.RequiredTools.All(successfulRequiredTools.Contains) && (!requiresExternalEffect || externalEffectEvidence); + ToolUseGuardResult callGuard = guard.Evaluate(plannerTask.RequiredTools.Count > 0 || requiresExternalEffect, true, verified, failureFingerprint); if (callGuard.Decision == ToolUseGuardDecision.Fail) throw new InvalidOperationException(callGuard.Observation); if (callGuard.Decision == ToolUseGuardDecision.Retry) - messages.Add(new OllamaMessage("system", callGuard.Observation + " Required effect Tool(s) for the current Task: " + - string.Join(", ", plannerTask.RequiredEffectTools.Order(StringComparer.Ordinal)) + ".")); + messages.Add(new OllamaMessage("system", callGuard.Observation + " Required Tool(s) for the current Task: " + + string.Join(", ", plannerTask.RequiredTools.Order(StringComparer.Ordinal)) + ".")); continue; } @@ -322,7 +326,8 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag continue; } - ToolUseGuardResult completion = guard.Evaluate(requiresExternalEffect, false, externalEffectEvidence); + bool completionVerified = plannerTask.RequiredTools.All(successfulRequiredTools.Contains) && (!requiresExternalEffect || externalEffectEvidence); + ToolUseGuardResult completion = guard.Evaluate(plannerTask.RequiredTools.Count > 0 || requiresExternalEffect, false, completionVerified); if (completion.Decision == ToolUseGuardDecision.Retry) { messages.Add(new OllamaMessage("system", completion.Observation)); @@ -337,12 +342,12 @@ public sealed class AgentRuntime(TaskRegistry registry, IOllamaClient ollama, Ag return finalContent; } - private static bool IsOutOfScopeEffectCall(OllamaToolCall call, IReadOnlySet<string> requiredEffectTools) + private static bool IsOutOfScopeEffectCall(OllamaToolCall call, bool requiresExternalEffect, IReadOnlySet<string> requiredTools) { bool createsExternalEffect = call.Function.Name is "file_edit" or "process_run" || call.Function.Name == "dir" && call.Function.Arguments.TryGetProperty("operation", out JsonElement operation) && operation.GetString() is "mkdir" or "rmdir"; - return createsExternalEffect && !requiredEffectTools.Contains(call.Function.Name); + return createsExternalEffect && (!requiresExternalEffect || !requiredTools.Contains(call.Function.Name)); } /// <summary> |
