summaryrefslogtreecommitdiff
path: root/src/Agent.Core/Tools/ToolUseGuard.cs
blob: 633d34c8b24f88d887369414fbea2a46bf39fce6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
namespace Agent.Core.Tools;

public enum ToolUseGuardDecision { Complete, Retry, Fail }

public sealed record ToolUseGuardResult(ToolUseGuardDecision Decision, string Observation, int IdenticalFailureCount);

public sealed class ToolUseGuard(int sameFailureLimit = 5)
{
    private readonly Dictionary<string, int> _failures = new(StringComparer.Ordinal);
    public int SameFailureLimit { get; } = sameFailureLimit > 0 ? sameFailureLimit : throw new ArgumentOutOfRangeException(nameof(sameFailureLimit));

    public ToolUseGuardResult Evaluate(bool requiresExternalEffect, bool hadToolCall, bool hasExternalEffectEvidence, string? failureFingerprint = null)
    {
        if (!requiresExternalEffect) return new(ToolUseGuardDecision.Complete, string.Empty, 0);
        if (hasExternalEffectEvidence) return new(ToolUseGuardDecision.Complete, string.Empty, 0);
        string fingerprint = string.IsNullOrWhiteSpace(failureFingerprint) ? "missing-external-effect" : failureFingerprint;
        int count = _failures.TryGetValue(fingerprint, out int prior) ? prior + 1 : 1;
        _failures[fingerprint] = count;
        if (count >= SameFailureLimit) return new(ToolUseGuardDecision.Fail, "Required external effect was not completed after repeated identical failures.", count);
        return new(ToolUseGuardDecision.Retry, "No verified external effect occurred; perform the required operation using an internal tool.", count);
    }
}