feat: add loop detection to prevent agents from getting stuck

- Track repetition count when same tool call is repeated
- After 3 repetitions: inject warning message to break the loop
- After 5 repetitions: force-complete with error message
- Log warnings when loop is detected for debugging
This commit is contained in:
Thomas Marchand
2025-12-19 19:10:06 +00:00
parent 594a401888
commit 0ad2366a7e

View File

@@ -369,6 +369,9 @@ Use `search_memory` when you encounter a problem you might have solved before or
let mut files_modified = false;
let mut last_tool_calls: Vec<String> = Vec::new();
let mut repetitive_actions = false;
let mut repetition_count: u32 = 0;
const LOOP_WARNING_THRESHOLD: u32 = 3;
const LOOP_FORCE_COMPLETE_THRESHOLD: u32 = 5;
let mut has_error_messages = false;
let mut iterations_completed = 0u32;
@@ -576,13 +579,58 @@ Use `search_memory` when you encounter a problem you might have solved before or
reasoning: response.reasoning.clone(),
});
// Check for repetitive actions
// Check for repetitive actions (loop detection)
let current_calls: Vec<String> = tool_calls
.iter()
.map(|tc| format!("{}:{}", tc.function.name, tc.function.arguments))
.collect();
if current_calls == last_tool_calls && !current_calls.is_empty() {
repetitive_actions = true;
repetition_count += 1;
tracing::warn!(
"Loop detected: same tool call repeated {} times: {:?}",
repetition_count,
current_calls.first().map(|s| s.chars().take(100).collect::<String>())
);
// Force completion if stuck in a loop for too long
if repetition_count >= LOOP_FORCE_COMPLETE_THRESHOLD {
tracing::error!("Force completing due to infinite loop (repeated {} times)", repetition_count);
let signals = ExecutionSignals {
iterations: iterations_completed,
max_iterations: ctx.max_iterations as u32,
successful_tool_calls,
failed_tool_calls,
files_modified,
repetitive_actions: true,
has_error_messages: true,
partial_progress: files_modified || successful_tool_calls > 0,
cost_spent_cents: total_cost_cents,
budget_total_cents: task.budget().total_cents(),
final_output: format!("Loop detected: repeated same action {} times. Please review results in working directory.", repetition_count),
model_used: model.to_string(),
};
return ExecutionLoopResult {
output: format!("Task stopped due to infinite loop. The agent repeated the same tool call {} times. Partial results may be in the working directory.", repetition_count),
cost_cents: total_cost_cents,
tool_log,
usage,
signals,
success: false,
};
}
// Inject a warning message after threshold to try to break the loop
if repetition_count == LOOP_WARNING_THRESHOLD {
messages.push(ChatMessage::new(
Role::User,
"[SYSTEM WARNING] You are repeating the same tool call multiple times. This suggests you may be stuck in a loop. Please either:\n1. Try a different approach\n2. Summarize your findings and complete the task\n3. If you've already found what you need, call complete_mission\n\nDo NOT repeat the same command again.".to_string()
));
}
} else {
// Reset counter if a different action is taken
repetition_count = 0;
}
last_tool_calls = current_calls;