kulono / doom-loop
blob · README.md · md
← filesrepo
README.mdmd
1# doom-loop
2
3> Agents get stuck in loops. This detects it and makes them recover — autonomously.
4
5## The problem
6
7Every agent framework sets a `max_iterations` cap. That's not loop detection,
8that's giving up. A real agent can:
9
10- Retry the **exact same tool call** that just failed (repeat loop)
11- Flip between two actions forever (ping-pong loop)
12- Poll a resource that never changes (poll-without-progress)
13- Trigger loops **across** cooperating agents (cross-agent loop)
14
15`doom-loop` detects all four patterns at runtime and fires **graded recovery
16actions** — retry with different args, switch tool, rollback, downgrade model,
17or force a summary — before any turn budget is burned.
18
19## Install
20
21```bash
22pip install doom-loop
23```
24
25## Usage
26
27```python
28import asyncio
29from doom_loop import DoomLoopDetector, DoomLoopConfig, RecoveryAction
30
31detector = DoomLoopDetector(DoomLoopConfig(
32 warn_threshold_base=2,
33 critical_threshold_base=3,
34 adaptive_enabled=False, # True in prod raises the bar under rapid calls
35))
36
37async def on_recover(result):
38 print(f"RECOVERING: {result.message}")
39 return True
40
41# callbacks are keyed by RecoveryAction and awaited after execute_recovery()
42detector.register_recovery_callback(RecoveryAction.FORCE_SUMMARIZE, on_recover)
43
44# feed every tool call + its outcome
45detector.record_tool_call("read_file", {"path": "/etc/hosts"})
46detector.record_tool_outcome("read_file", {"path": "/etc/hosts"}, "ok")
47
48result = detector.detect()
49if result.stuck:
50 asyncio.run(detector.execute_recovery(result))
51```
52
53### Determinism gotcha
54
55`DoomLoopConfig` defaults to `adaptive_enabled=True`. Under high-frequency calls
56the adaptive threshold *raises*, so tight tests need `adaptive_enabled=False`
57to get deterministic triggers.
58
59## Design
60
61- 4 detector engines share one call/outcome history
62- `_fuzzy_args_hash` treats near-identical args as the same retry
63- Recovery callbacks are keyed by `RecoveryAction` (async, awaited via `execute_recovery`)
64- `result.stuck` + graded `LoopLevel` (NONE / WARNING / CRITICAL)
65
66Runs 24×7 in the tical-code / EITE agent mesh.
67
68## License
69
70AGPL-3.0.
71
100%