Class: RobotLab::DoomLoopDetector
- Inherits:
-
Object
- Object
- RobotLab::DoomLoopDetector
- Defined in:
- lib/robot_lab/doom_loop_detector.rb
Overview
Detects and interrupts tool-call doom loops inside the LLM agent loop.
A doom loop occurs when the LLM repeatedly calls the same tool(s) in a cycle — either identical consecutive calls (A, A, A) or a repeating multi-step pattern (A, B, C, A, B, C, A, B, C).
When a doom loop is detected the warning is embedded in the tool result so the LLM sees it in its next context window and can self-correct.
Constant Summary collapse
- DEFAULT_THRESHOLD =
3- MAX_PERIOD =
10
Instance Attribute Summary collapse
-
#sequence ⇒ Object
readonly
Returns the value of attribute sequence.
Instance Method Summary collapse
- #doom_loop? ⇒ Boolean
-
#initialize(threshold: DEFAULT_THRESHOLD) ⇒ DoomLoopDetector
constructor
A new instance of DoomLoopDetector.
- #reset ⇒ Object
- #track(tool_name) ⇒ Object
- #warning_message ⇒ Object
Constructor Details
#initialize(threshold: DEFAULT_THRESHOLD) ⇒ DoomLoopDetector
Returns a new instance of DoomLoopDetector.
27 28 29 30 |
# File 'lib/robot_lab/doom_loop_detector.rb', line 27 def initialize(threshold: DEFAULT_THRESHOLD) @threshold = threshold @sequence = [] end |
Instance Attribute Details
#sequence ⇒ Object (readonly)
Returns the value of attribute sequence.
25 26 27 |
# File 'lib/robot_lab/doom_loop_detector.rb', line 25 def sequence @sequence end |
Instance Method Details
#doom_loop? ⇒ Boolean
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
# File 'lib/robot_lab/doom_loop_detector.rb', line 36 def doom_loop? seq = @sequence return false if seq.length < @threshold # Consecutive identical calls: A, A, A tail = seq.last(@threshold) return true if tail.uniq.length == 1 # Cyclic multi-step patterns: A,B,C, A,B,C, A,B,C max_period = [MAX_PERIOD, seq.length / @threshold].min (2..max_period).each do |period| window = @threshold * period next if seq.length < window chunk = seq.last(window) pattern = chunk.first(period) return true if chunk == pattern * @threshold end false end |
#reset ⇒ Object
58 59 60 |
# File 'lib/robot_lab/doom_loop_detector.rb', line 58 def reset @sequence = [] end |
#track(tool_name) ⇒ Object
32 33 34 |
# File 'lib/robot_lab/doom_loop_detector.rb', line 32 def track(tool_name) @sequence << tool_name.to_s end |
#warning_message ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/robot_lab/doom_loop_detector.rb', line 62 def seq = @sequence return "" if seq.empty? period = detect_period(seq) if period == 1 tool = seq.last "Tool '#{tool}' has been called #{@threshold}+ times consecutively with " \ "no progress. You appear to be stuck in a loop. Try a fundamentally " \ "different approach, use a different tool, or ask for clarification." else pattern = seq.last(period).join(" → ") "Tool call pattern [#{pattern}] has repeated #{@threshold}+ times. " \ "You appear to be stuck in a loop. Try a fundamentally different " \ "approach, use a different tool, or ask for clarification." end end |