# How-To: Customizing the Agent Loop
How to modify the agent's behavior: iteration limits, tool strategies,
memory triggers, and circuit breaker behavior.
---
## 1. The AgentLoop lifecycle
The agent loop runs in iterations:
```
User message
→ Assemble context (system prompt + memories + recent messages)
→ Generate from model
→ Text response → yield to user
→ Tool calls → execute → loop back with results
→ Check memory triggers (observation/reflection)
→ Check loop detection
→ Check budget
→ Continue or terminate
```
Two iteration levels:
- **Inner iteration** — tool call rounds within a single generation
- **Outer iteration** — full conversation rounds (user → agent → user)
## 2. Configuring iteration limits
```rust
let config = AgentConfig {
max_inner_iterations: 10, // Default: 10 tool call rounds
max_outer_iterations: 5, // Default: 5 conversation rounds
..AgentConfig::default()
};
let session = AgentSession::builder(SessionId::new(), router)
.with_config(config)
.build()?;
```
**When to change:**
- **Increase inner** for complex tool chains (e.g., read → analyze → write)
- **Decrease inner** for fast responses (agent stops after N tool calls)
- **Increase outer** for long conversations
- **Decrease outer** for single-turn interactions
## 3. Circuit breaker behavior
The circuit breaker switches to fallback models after repeated failures:
```rust
let config = AgentConfig {
circuit_breaker_threshold: 3, // Failures before fallback (default: 3)
fallback_models: vec![
ModelId::Name("gpt-4".into(), None),
ModelId::Name("local-llama".into(), None),
],
..AgentConfig::default()
};
```
**How it works:**
1. Primary model fails → increment failure counter
2. Counter reaches threshold → switch to first fallback
3. Fallback also fails → switch to next fallback
4. All fallbacks exhausted → terminate with error
**When to change:**
- **Lower threshold** (2) for production systems (fail fast, switch quickly)
- **Higher threshold** (5) for development (give primary more chances)
## 4. Context pressure management
The agent monitors context window usage and can compress or truncate:
```rust
let config = AgentConfig {
context_pressure_threshold: 0.8, // Warn at 80% context usage
preflight_compression_threshold: 0.9, // Compress at 90%
..AgentConfig::default()
};
```
**What happens at each threshold:**
- **Warning (context_pressure_threshold)** — log a warning, continue normally
- **Compression (preflight_compression_threshold)** — compress recent messages
before next generation (summarize verbose turns)
- **Hard limit** — terminate with `AgenticError::ContextOverflow`
## 5. Memory trigger customization
Control when the agent generates observations and reflections:
```rust
let memory_config = MemoryConfig {
observation_trigger_tokens: 30_000, // Generate observation after 30K tokens
reflection_trigger_tokens: 40_000, // Generate reflection after 40K tokens
..MemoryConfig::default()
};
let session = AgentSession::builder(SessionId::new(), router)
.with_memory_config(memory_config)
.build()?;
```
**When to change:**
- **Lower thresholds** (10K/15K) for memory-intensive agents (research, analysis)
- **Higher thresholds** (50K/80K) for token-efficient agents (simple Q&A)
- **Disable memory** — set thresholds to `usize::MAX`
## 6. Model parameter tuning
```rust
let config = AgentConfig {
model_params: ModelParams {
temperature: 0.7, // 0.0 (deterministic) to 1.0 (creative)
top_p: 0.95, // Nucleus sampling (0.0 to 1.0)
max_tokens: 4096, // Max tokens per response
stop_sequences: vec![], // Stop when any sequence appears
..ModelParams::default()
},
..AgentConfig::default()
};
```
**When to change:**
- **Low temperature** (0.1-0.3) for code generation, factual responses
- **High temperature** (0.7-1.0) for creative writing, brainstorming
- **Low top_p** (0.7) for focused, safe responses
- **High top_p** (0.95) for diverse, varied responses
## 7. Custom tool strategies
### Tool selection per model
```rust
// Some models handle tools better than others
let toolshed = if model.is_tool_capable() {
build_full_toolshed()
} else {
ToolShed::default() // No tools for this model
};
```
### Tool retry configuration
Tools can be retried via the `ToolRetryConfig` in `ToolCallRequest`:
```rust
// Tools have retry configuration
let request = ToolCallRequest {
id: "call-1".into(),
name: "read_file".into(),
arguments: /* ... */,
depends_on: vec![],
execution_hint: ExecutionHint::default(),
};
```
Retry behavior is controlled by the `ToolCallManager` and the error policy.
For custom retry logic, wrap tool execution in the manager or use the
circuit breaker's fallback model chain.
## 8. Error handling customization
The agent's error policy maps errors to actions:
| Context overflow | `AgentAction::RetryWithReducedContext` |
| Rate limit | `AgentAction::SwitchModel` |
| Tool error | `AgentAction::Continue` |
| Budget exceeded | `AgentAction::Terminate` |
| Provider error | `AgentAction::Terminate` |
Customize the policy with a closure:
```rust
AgenticError::Generation(f) if f.kind == GenKind::RateLimit => {
AgentAction::RetryWithReducedContext
}
AgenticError::ToolCall { ref tool_name, .. } if tool_name == "critical_tool" => {
AgentAction::Terminate(error)
}
_ => ErrorPolicy::new().classify(error), // fall through to defaults
}
});
```
`ErrorPolicy::customize()` takes any `Fn(AgenticError) -> AgentAction + Send + Sync + 'static`.
The custom classifier is checked first; if none is set, the default classification applies.