use noos::regulator::tools::TOOL_LOOP_THRESHOLD;
use noos::regulator::{CircuitBreakReason, Decision, LLMEvent, Regulator};
fn main() {
println!("══════ Regulator demo 4 — tool-call loop intercept ══════\n");
println!("Agent task: find recent orders for user 7");
println!("Agent strategy: call `search_orders` repeatedly with query tweaks");
println!("Each call returns `{{\"results\": []}}` — protocol-level success, no progress.\n");
let mut regulator = Regulator::for_user("user_7");
regulator.on_event(LLMEvent::TurnStart {
user_message: "find recent orders for user 7".into(),
});
let queries = [
r#"{"user_id": 7, "days": 7}"#,
r#"{"user_id": 7, "days": 14}"#,
r#"{"user_id": 7, "days": 30}"#,
r#"{"user_id": 7, "days": 60}"#,
r#"{"user_id": 7, "days": 90}"#,
r#"{"user_id": 7, "days": 180}"#,
];
let mut halted = false;
for (i, args) in queries.iter().enumerate() {
regulator.on_event(LLMEvent::ToolCall {
tool_name: "search_orders".into(),
args_json: Some((*args).to_string()),
});
regulator.on_event(LLMEvent::ToolResult {
tool_name: "search_orders".into(),
success: true, duration_ms: 95,
error_summary: None,
});
println!(
"── Call {call_num:>2} / threshold {threshold} ──",
call_num = i + 1,
threshold = TOOL_LOOP_THRESHOLD,
);
println!(" args: {args}");
println!(" result: {{\"results\": []}}");
match regulator.decide() {
Decision::CircuitBreak {
reason:
CircuitBreakReason::RepeatedToolCallLoop {
ref tool_name,
consecutive_count,
},
ref suggestion,
} => {
println!("\n[Decision::CircuitBreak] ← HALT");
println!(" reason: RepeatedToolCallLoop");
println!(" tool_name: {tool_name}");
println!(" consecutive_count: {consecutive_count}");
println!(" suggestion: {suggestion}");
println!(
"\nRegulator observability at halt:\n \
tool_total_calls: {}\n \
tool_total_duration_ms: {} ms\n \
tool_failure_count: {}",
regulator.tool_total_calls(),
regulator.tool_total_duration_ms(),
regulator.tool_failure_count(),
);
halted = true;
break;
}
Decision::Continue => {
println!(" decision: Continue (agent keeps calling)");
}
other => {
println!(" decision: {other:?}");
}
}
println!();
}
println!();
println!("══════ Take-away ══════");
if halted {
println!(
"Regulator halted at call {TOOL_LOOP_THRESHOLD} — the signature of a \
runaway loop on a tool that keeps returning unhelpful results."
);
} else {
println!(
"(The loop did not fire on this run — this should only happen if \
TOOL_LOOP_THRESHOLD was modified.)"
);
}
println!();
println!("Baseline agent (no regulator):");
println!(" • continues calling until total turn cost / max_iterations trips;");
println!(" • wastes tool-latency budget on structurally identical queries;");
println!(" • burns LLM tokens re-summarising the same empty response.");
println!();
println!("Retry / backoff crates (tenacity, backoff, tokio-retry):");
println!(" • trip on transport errors only — a protocol-level success with");
println!(" empty payload does not register as a retryable failure.");
println!();
println!("Agent frameworks (LangChain, CrewAI, AutoGen):");
println!(" • use `max_iterations` / `max_steps` that bounds ALL iteration,");
println!(" including productive interleaved tool calls — the regulator's");
println!(" signal is structural (consecutive same-tool) so productive");
println!(" agents are not penalised.");
}