ExecutionConfig
Configure agent execution limits including iterations, rate limits, and timeouts
Control agent execution behavior with limits on iterations, rate limiting, timeouts, and retries.
Quick Start
- Using Presets
Use string presets for common configurations:
python from praisonaiagents import Agent # Fast execution (fewer iterations) agent = Agent( name="Fast Agent", instructions="Quick tasks", execution="fast" ) # Thorough execution (more iterations) agent = Agent( name="Thorough Agent", instructions="Complex analysis", execution="thorough" ) - With Configuration
Fine-grained control:
python from praisonaiagents import Agent from praisonaiagents.config import ExecutionConfig agent = Agent( name="Custom Agent", instructions="Custom execution limits", execution=ExecutionConfig( max_iter=50, max_rpm=100, max_execution_time=300, max_retry_limit=5, max_tool_calls_per_turn=10 ) )
Configuration Options
from praisonaiagents.config import ExecutionConfig
config = ExecutionConfig(
# Iteration limits
max_iter=20,
# Rate limiting: positive int auto-builds a live RateLimiter
max_rpm=None,
# Time limits (seconds)
max_execution_time=None,
# Retry settings
max_retry_limit=2,
# Tool call limits (loop protection)
max_tool_calls_per_turn=10
)| Parameter | Type | Default | Description | |
|---|---|---|---|---|
max_iter | int | 20 | Maximum tool-calling iterations. Now propagated to the LLM instance, so it controls every internal loop (previously some loops were hardcoded to 5/10/20/50). Both the ExecutionConfig default and the LLM-direct-construction default are 20 (aligned as of PR #1898). When the loop reaches this cap, the agent performs one bounded LLM call with tools disabled to synthesise a wrap-up summary instead of returning the old "Task completed." placeholder (as of PR #2577). | |
max_rpm | `int \ | None` | None | Max requests per minute. When set to a positive int with no explicit rate_limiter, the Agent auto-builds RateLimiter(requests_per_minute=max_rpm). Explicit rate_limiter takes precedence. max_rpm <= 0 raises ValueError at construction (validated before precedence, so max_rpm=0 still raises even with a rate_limiter). See Rate Limiter. |
max_execution_time | `int \ | None` | None | Max execution time in seconds |
max_retry_limit | int | 2 | Max retries for retryable tool failures and guardrail validation failures. Total attempts = 1 + max_retry_limit. For tools, this feeds the same effective RetryPolicy as ToolConfig(retry_policy=...); an explicit retry_policy takes precedence. Retries use exponential backoff with jitter (see retry_initial_delay, retry_backoff_factor, retry_jitter). | |
retry_initial_delay | float | 1.0 | First retry delay in seconds. Subsequent delays grow exponentially. Must be > 0. | |
retry_backoff_factor | float | 2.0 | Multiplier applied each attempt (base = initial × factor^(attempt−1)). Must be >= 1.0. | |
retry_jitter | float | 0.1 | Random jitter added as a fraction of the base delay. Must be >= 0. | |
max_tool_calls_per_turn | int | 10 | Maximum tool calls allowed in a single chat turn. When exceeded, execution stops with a clear message instead of looping forever. | |
context_compaction | `bool \ | ContextCompactionPolicy` | False | Proactive context-overflow protection. True uses the BALANCED_POLICY preset. Pass a ContextCompactionPolicy instance for custom routing. Default flips to True in the next release — a DeprecationWarning is emitted today when left at False. See Context Compaction Policy. |
compaction_strategy | `CompactionStrategy \ | None` | None | Which strategy the compactor uses when context_compaction is on. None defaults to CompactionStrategy.TRUNCATE. Set to CompactionStrategy.LLM_SUMMARIZE for summary-based compaction. Import: from praisonaiagents import CompactionStrategy. |
max_context_tokens | `int \ | None` | None (auto-detect from model window) | Trigger for the in-run compaction hook. When unset, derived from the active model’s context window via budgeter.get_model_limit at 80% minus get_output_reserve (headroom for completion); an explicit int wins unchanged. Falls back to a static 8000 only for offline / unknown-model cases. See Context Compaction → Auto-sized trigger. |
max_budget | `float \ | None` | None | Hard USD cap per agent run. None = no limit. |
on_budget_exceeded | `str \ | callable` | "stop" | Action when limit is hit after an LLM call returns. "stop" raises BudgetExceededError. "warn" logs a warning and continues. callable(total_cost, max_budget) is invoked; return value is ignored. |
Execution Presets
| Preset | max_iter | Description |
|---|---|---|
"fast" | 10 | Quick tasks, fewer iterations |
"balanced" | 20 | Default, balanced approach |
"thorough" | 50 | Complex tasks, more iterations |
"unlimited" | 1000 | Long-running tasks |
Iteration Propagation
ExecutionConfig.max_iter is now the single source of truth for iteration limits, replacing previously hardcoded internal caps.
Before: Internal LLM loops were hardcoded to different limits (5, 10, 20, 50)
After: All loops respect the configured max_iter value
from praisonaiagents import Agent
from praisonaiagents.config import ExecutionConfig
# This now controls ALL iteration loops, not just agent-level loops
agent = Agent(
name="Unified Control",
instructions="Respect iteration limits everywhere",
execution=ExecutionConfig(max_iter=15)
)
# The agent's LLM will respect 15 iterations in all internal loopsWhen the limit is reached
When the tool-calling loop reaches max_iter, the agent performs one final LLM call with tool_choice="none" and every advertised tool stripped, asking the model to summarise what it accomplished, what remains unfinished, and any suggested next steps using the tool results already in context. That synthesised text becomes the final answer.
The extra call is bounded to exactly one non-tool completion and is routed through the same retry / failover wrappers as every other completion, so a transient rate-limit error doesn’t drop straight to a placeholder. If the call still fails, the agent falls back to the last-turn text, then to "Reached the step limit before finishing this task." — behaviour never regresses.
There is no opt-out. To avoid truncation, increase max_iter so the agent can finish normally.
Tool Retry & Exponential Backoff
Tool and guardrail retries use exponential backoff with jitter so transient failures recover without hammering APIs.
Delay formula: delay = min(initial_delay × factor^(attempt−1), 60s) + random(0, jitter × base).
With defaults (retry_initial_delay=1.0, retry_backoff_factor=2.0), three retries wait roughly 1.0s, 2.0s, 4.0s (plus up to 10% jitter).
For tools, max_retry_limit / retry_* are translated into the same effective RetryPolicy as ToolConfig(retry_policy=...). An explicit retry_policy (or per-tool @tool(retry_policy=...)) wins over these fields. Users who never set retry_policy= see no behaviour change. The guardrail-retry backoff continues to use these fields regardless.
See Tool Retry & Backoff for retry classification and patterns.
Proactive Context Compaction
Context compaction proactively prevents overflow before LLM calls instead of reacting after errors.
from praisonaiagents import Agent, ExecutionConfig, BALANCED_POLICY
agent = Agent(
name="Researcher",
execution=ExecutionConfig(
max_iter=30,
context_compaction=BALANCED_POLICY, # explicit preset
),
)See Context Compaction Policy for detailed configuration options.
Persistence
Save an ExecutionConfig to a dict for YAML/JSON storage and restore it later — every field round-trips.
from praisonaiagents import Agent, ExecutionConfig, CompactionStrategy
config = ExecutionConfig(
max_iter=30,
retry_initial_delay=3.0,
retry_backoff_factor=4.0,
retry_jitter=0.25,
context_compaction=True,
compaction_strategy=CompactionStrategy.LLM_SUMMARIZE,
)
# Save
saved = config.to_dict()
# Restore
restored = ExecutionConfig.from_dict(saved)
agent = Agent(name="Persisted Agent", instructions="...", execution=restored)Common Patterns
Pattern 1: Rate-Limited Agent
from praisonaiagents import Agent
from praisonaiagents.config import ExecutionConfig
agent = Agent(
name="Rate Limited Agent",
instructions="Respect API limits",
execution=ExecutionConfig(
max_rpm=60, # auto-builds a live RateLimiter(requests_per_minute=60)
max_retry_limit=3
)
)Pattern 2: Time-Bounded Agent
from praisonaiagents import Agent
from praisonaiagents.config import ExecutionConfig
agent = Agent(
name="Timed Agent",
instructions="Complete within time limit",
execution=ExecutionConfig(
max_execution_time=60, # 60 seconds max
max_iter=100
)
)Pattern 3: Resilient Agent
from praisonaiagents import Agent
from praisonaiagents.config import ExecutionConfig
agent = Agent(
name="Resilient Agent",
instructions="Handle failures gracefully",
execution=ExecutionConfig(
max_retry_limit=5,
retry_initial_delay=0.5,
retry_backoff_factor=2.0,
retry_jitter=0.2,
)
)Pattern 4: Loop-Protected Agent
from praisonaiagents import Agent
from praisonaiagents.config import ExecutionConfig
agent = Agent(
name="Protected Agent",
instructions="Agent with experimental tools",
execution=ExecutionConfig(
max_tool_calls_per_turn=5, # Lower limit for potentially noisy tools
max_iter=20
)
)Best Practices
Set Iteration Limits
Always set max_iter to prevent runaway agents consuming resources.
Use Rate Limiting for APIs
Set max_rpm when calling external APIs to avoid rate limit errors. Setting max_rpm now enforces the limit — the Agent auto-builds a live RateLimiter, so no separate rate_limiter object is required for simple RPM throttling.
Set Timeouts for Production
Use max_execution_time in production to prevent hung processes.
Configure Loop Protection
Adjust max_tool_calls_per_turn based on your tools: lower for experimental tools (3-5), higher for complex multi-tool workflows (20-30).
Related
Proactive context overflow protection
LLM Error ClassificationIteration control and error handling integration
Async ExecutionAsync agent execution
Background TasksRun agents in background
Structured LLM ErrorsLLM error handling and retry policies
Tool Retry & BackoffExponential backoff for tool and guardrail retries
Rate LimiterAuto-wire, precedence, and burst/TPM control for max_rpm