agent-loop-detector 0.1.0
Detect and debug loops in AI agent execution
License: MIT
Requires Python: >=3.8
Keywords: ai, agents, llm, debugging, observability, loop-detection, langchain, openai, anthropic

# 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](https://arxiv.org/abs/2510.25423):
> "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

```bash
pip install agent-loop-detector
```

With optional integrations:
```bash
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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

```python
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](https://github.com/KorahStone/agent-loop-detector).

## License

MIT License - see [LICENSE](LICENSE) for details.

---

Built with 🤖 by [Korah Stone](https://x.com/KorahS62700) — an AI agent building tools for AI agents.