MandAPI
English ▾
Build application workflows

Stop repeated tool calls and runaway agent loops

Enforce a step limit and detect identical calls using application trace data.

Use this recipe with your coding assistant

Read the prompt
Add a local loop guard around this application's model-requested operations. Validate each call before dispatch, hash canonical action-and-argument data, enforce a per-run step limit, and stop excessive identical consecutive calls. Keep transient timestamps out of fingerprints. Record a stop reason and offer a human handoff. Test with local simulated operations only; do not claim a MandAPI Decisions API or semantic loop detector.
TXT

Define a stopping rule

A model can repeatedly propose the same tool call when a result is missing or misunderstood. Put a limit in the application that runs the loop. Do not rely solely on the next model reply deciding to stop.

Start with two clear rules: a maximum number of steps for one run, and a maximum number of identical consecutive calls. Tune these limits to the workflow. Repeating a read may be useful; repeating a write can have consequences. This local example stops before dispatching a repeated operation and never calls MandAPI or a tool.

Check calls before dispatch

Canonicalize the action and arguments, then hash the serialized value. Only include fields that define the operation. Request timestamps or random identifiers would make equivalent operations look different.

python
import hashlib
import json

class LoopGuard:
    def __init__(self, max_steps=8, max_identical=2):
        self.max_steps = max_steps
        self.max_identical = max_identical
        self.steps = 0
        self.last = None
        self.repeated = 0

    def check(self, name, arguments):
        if self.steps >= self.max_steps:
            raise RuntimeError("Step limit reached")
        raw = json.dumps(
            {"name": name, "arguments": arguments},
            sort_keys=True, separators=(",", ":"),
        )
        fingerprint = hashlib.sha256(raw.encode()).hexdigest()
        self.repeated = self.repeated + 1 if fingerprint == self.last else 1
        self.last = fingerprint
        if self.repeated > self.max_identical:
            raise RuntimeError("Repeated operation stopped")
        self.steps += 1
        return {"step": self.steps, "fingerprint": fingerprint}

guard = LoopGuard()
print(guard.check("lookup_order", {"order_id": "demo-001"}))
print(guard.check("lookup_order", {"order_id": "demo-001"}))
try:
    guard.check("lookup_order", {"order_id": "demo-001"})
except RuntimeError as error:
    print(error)

Integrate with a model loop

Run the guard after validating a proposed call and before executing it. Store its trace with the run identifier, operation result, and stop reason. On a stop, return a useful explanation or route the task to a person. The rules live in your application; there is no special Decisions API or model confidence score involved.

Check the result

The demonstration accepts two identical lookups and stops the third. Also test distinct calls until the step limit is reached. This fingerprint only catches exact normalized repetition, not semantic equivalence; retain the step limit as a separate boundary.

References

Topics inspired by public documentation; instructions and examples adapted for MandAPI.