summaryrefslogtreecommitdiff
path: root/src/Agent.Core/Tools/ToolUseGuard.cs
blob: eeb815b949b086ada3ee546bb7025c0bc6caa3c9 (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, "The required operation was not verified after repeated identical failures.", count);
        return new(ToolUseGuardDecision.Retry, "Required Tool evidence is missing; perform the current operation using the required Internal Tool.", count);
    }
}