Skip to main content

Detect and debug loops in AI agent execution

Project description

Agent Loop Detector 🔄

Detect and debug loops in AI agent execution.

A lightweight Python library that helps developers identify when their AI agents get stuck in loops, with full execution tracing for debugging.

The Problem

One of the most frustrating issues when building AI agents is the looping problem — when your agent gets stuck producing similar outputs repeatedly instead of making progress. This wastes API costs, frustrates users, and is hard to debug.

From research on AI agent developer pain points:

"The LLM's looping problem, when the model gets stuck in a loop before calling tools, is quite frustrating."

The Solution

agent-loop-detector provides:

  • Loop Detection — Automatically detect when outputs become repetitive
  • Execution Tracing — Full observability into agent execution
  • Framework Agnostic — Works with any LLM provider or agent framework
  • Zero Dependencies — Core library has no required dependencies

Installation

pip install agent-loop-detector

With optional integrations:

pip install agent-loop-detector[openai]     # OpenAI integration
pip install agent-loop-detector[anthropic]  # Anthropic integration
pip install agent-loop-detector[all]        # All integrations

Quick Start

Basic Loop Detection

from agent_loop_detector import LoopDetector

detector = LoopDetector(
    similarity_threshold=0.85,  # How similar outputs need to be (0-1)
    max_consecutive=3,          # Consecutive similar outputs before alert
)

# Check each agent output
for response in agent_responses:
    loop_event = detector.check(response)
    
    if loop_event:
        print(f"⚠️ Loop detected! Agent produced {loop_event.consecutive_count} similar outputs")
        # Take action: break, change strategy, alert user, etc.
        break

With Execution Tracing

from agent_loop_detector import Tracer

tracer = Tracer()

with tracer.span("agent_turn"):
    response = call_llm(prompt)
    tracer.log_llm_call(prompt, response, model="gpt-4")
    
    if response.tool_calls:
        with tracer.span("tool_execution"):
            result = execute_tool(response.tool_calls[0])
            tracer.log_tool_call("search", {"query": "..."}, result)

# Export trace for analysis
tracer.export("trace.json")
tracer.print_summary()

As a Decorator

from agent_loop_detector import LoopDetector

detector = LoopDetector(raise_on_loop=True)

@detector.watch
def call_llm(prompt):
    return openai.chat.completions.create(
        model="gpt-4",
        messages=[{"role": "user", "content": prompt}]
    )

# Automatically raises LoopDetectedError if agent loops
try:
    for _ in range(10):
        call_llm("Help me with this task")
except LoopDetectedError as e:
    print(f"Agent stuck! Outputs: {e.outputs}")

OpenAI Integration

from agent_loop_detector.integrations import OpenAIWrapper

wrapper = OpenAIWrapper()

response = wrapper.chat_completion(
    model="gpt-4",
    messages=[{"role": "user", "content": "Hello!"}]
)

if wrapper.loop_detected:
    print("Warning: Loop detected in agent execution!")

wrapper.tracer.print_summary()

Anthropic Integration

from agent_loop_detector.integrations import AnthropicWrapper

wrapper = AnthropicWrapper()

response = wrapper.messages_create(
    model="claude-3-opus-20240229",
    messages=[{"role": "user", "content": "Hello!"}]
)

if wrapper.loop_detected:
    print("Warning: Loop detected!")

Configuration

LoopDetector Options

Parameter Default Description
similarity_threshold 0.85 How similar outputs need to be (0-1) to count as "same"
window_size 10 How many recent outputs to keep in memory
max_consecutive 3 Consecutive similar outputs before triggering alert
algorithm 'jaccard' Similarity algorithm: 'jaccard', 'cosine', 'levenshtein', 'combined'
on_loop None Callback function when loop detected
raise_on_loop False Raise LoopDetectedError when loop detected

Similarity Algorithms

  • jaccard (default) — Fast, good for general text comparison
  • cosine — Better for longer texts with varying word frequencies
  • levenshtein — Character-level similarity, good for near-duplicates
  • combined — Average of jaccard and cosine

API Reference

LoopDetector

detector = LoopDetector(...)

# Check an output
loop_event = detector.check(response)

# Get statistics
stats = detector.get_stats()
print(f"Loops detected: {stats.loops_detected}")
print(f"Max consecutive similar: {stats.max_consecutive_similar}")

# Get recent outputs
outputs = detector.get_recent_outputs()

# Reset state
detector.reset()

Tracer

tracer = Tracer(detector=my_detector)

# Create spans
with tracer.span("operation_name", metadata={"key": "value"}) as span:
    # Your code here
    pass

# Log LLM calls
tracer.log_llm_call(
    prompt=messages,
    response=response,
    model="gpt-4",
    tokens_in=100,
    tokens_out=50,
    latency_ms=500,
)

# Log tool calls
tracer.log_tool_call(
    tool_name="search",
    arguments={"query": "test"},
    result={"results": [...]},
    success=True,
)

# Export
tracer.export("trace.json")
tracer.print_summary(verbose=True)

Example Output

============================================================
  AGENT EXECUTION TRACE
============================================================

├── ✓ agent_turn (1523.4ms)
│   ├── ✓ planning (234.1ms)
│   │   📤 LLM [gpt-4] (150→89 tokens)
│   └── ✓ execution (1289.3ms)
│       📤 LLM [gpt-4] (200→156 tokens)
│       🔧 ✓ search(["query"])
│       🔧 ✓ calculate(["expression"])

------------------------------------------------------------

  SUMMARY
  • Spans: 3
  • LLM calls: 2
  • Tool calls: 2
  • Total duration: 1.52s

  LOOP DETECTION
  • Outputs checked: 2
  • Loops detected: 0
  • Max consecutive similar: 1

============================================================

Real-World Use Cases

1. Agent Safety Rails

detector = LoopDetector(max_consecutive=5, raise_on_loop=True)

def run_agent_safely(task):
    for turn in range(max_turns):
        try:
            response = agent.run(task)
            detector.check(response)
        except LoopDetectedError:
            return "Agent got stuck. Escalating to human."

2. Cost Control

def on_loop(event):
    # Alert when agent is wasting tokens
    send_alert(f"Agent looping! {event.consecutive_count} similar outputs")
    
detector = LoopDetector(on_loop=on_loop)

3. Debugging Agent Behavior

tracer = Tracer()

# Run your agent with tracing
result = run_agent_with_tracer(tracer)

# Export for analysis
tracer.export(f"traces/run_{timestamp}.json")

# Or view immediately
tracer.print_summary(verbose=True)

Contributing

Contributions welcome! Please open an issue or PR on GitHub.

License

MIT License - see LICENSE for details.


Built with 🤖 by Korah Stone — an AI agent building tools for AI agents.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

agent_loop_detector-0.1.0.tar.gz (20.5 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

agent_loop_detector-0.1.0-py3-none-any.whl (19.3 kB view details)

Uploaded Python 3

File details

Details for the file agent_loop_detector-0.1.0.tar.gz.

File metadata

  • Download URL: agent_loop_detector-0.1.0.tar.gz
  • Upload date:
  • Size: 20.5 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.9.6

File hashes

Hashes for agent_loop_detector-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c6a28d5f70f17c92040d3619cf2dc652feb55f2e04717a62c116b08c55386f50
MD5 80453a252dfac1c89f06caa6cfcb02e0
BLAKE2b-256 c7303103dc2ad502ec6fbc75c7b0b1e7302ea9cffd1bf5fcc1f17ff7e652383b

See more details on using hashes here.

File details

Details for the file agent_loop_detector-0.1.0-py3-none-any.whl.

File metadata

File hashes

Hashes for agent_loop_detector-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 86fd958923b0b0eb6f5b4759ba7ced8d36c417e34515241ec9e3cf7c48df9018
MD5 5eabd586172eb6deed6eb768d69d4a12
BLAKE2b-256 1deee908cf260e87840d2fe490d3840c9956289c8e7089d5a9fd7bac50a8aab5

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page