DEV Community

Stop Infinite ReAct Loops: Deterministic Cycle Detection in Spring AI with Java 21 Record Patterns

Stop Infinite ReAct Loops: Deterministic Cycle Detection in Spring AI with Java 21 Record Patterns Runaway ReAct agent loops are the single fastest way to blow through a enterprise LLM budget in a single deployment. If you rely on soft system prompt instructions or simple max-message limits to stop infinite agent execution, your production setup is a liability. Why Most Developers Get This Wrong - Relying on hard iteration caps: Setting max-iterations=10 in your orchestrator just defers the bill spike without solving deterministic tool loops. - Naive string matching: Checking raw LLM text output misses cycles when tool arguments contain dynamic values like timestamps or ephemeral tracing IDs. - Prompt-level guardrails: Pleading with models ("do not execute the same tool twice") fails probabilistically under heavy context windows. The Right Way Catch execution cycles at the runtime level by pairing a custom Spring AI CallAroundAdvisor with Java 21 record patterns across a rolling sliding window. - Intercept at the Advisor boundary: Intercept AdvisedRequest payloads before Spring AI dispatches state back to your model provider. - Deconstruct tool calls with Record Patterns: Extract tool signatures and arguments cleanly using Java 21 pattern matching ( case ToolCall(String name, Map args) ). - Sliding Window Fingerprinting: Hash sanitized tool signatures over a 5-step rolling window to instantly catch $A \rightarrow B \rightarrow A$ cyclic tool execution. - Short-circuit execution: Throw a typed runtime exception immediately to break the loop before incurring another API token charge. I built javalld.com while prepping for senior roles - complete LLD problems with execution traces, not just theory. Show Me The Code public record ToolCall(String name, Map args) {} public class CycleDetectionAdvisor implements CallAroundAdvisor { private final SlidingWindowCache window = new SlidingWindowCache<>(5); @Override public AdvisedResponse around(AdvisedRequest req, CallAroundAdvisorChain chain) { if (req.attributes().get("last_tool") instanceof ToolCall(String name, var args)) { int signatureHash = Objects.hash(name, sanitize(args)); if (window.containsAndAdd(signatureHash)) { throw new AgentCycleDetectedException("Loop detected for tool: " + name); } } return chain.nextAround(req); } } Key Takeaways - Enforce guardrails in code, not prompts: LLMs are probabilistic, but cycle detection in your backend pipeline must be strictly deterministic. - Leverage Java 21 Record Patterns: Cleanly destruct agent state without messy reflection or verbose instance checks. - Fail fast at the framework edge: Catch tool looping inside Spring AI's CallAroundAdvisor before firing unnecessary LLM token calls. Top comments (1) Deterministic loop detection is the right framing. A ReAct loop is not only a bad answer; it is a control-system failure where the agent keeps proving it cannot change state. I like that this treats the loop as something observable in the trace instead of something you hope the model notices.

Read on DEV Community ↗ ← Back to News

Comments

No comments yet. Start the discussion.