Autonomy

Enable agents to operate independently with doom-loop protection and safety controls

Autonomy lets agents operate independently — detecting doom loops, escalating when stuck, and optionally running in a full-auto iterative mode.

python
from praisonaiagents import Agent

# All four spellings enable the same 'full_auto' preset:
Agent(instructions="t", autonomy="full_auto")
Agent(instructions="t", autonomy="full-auto")
Agent(instructions="t", autonomy="FULL_AUTO")
Agent(instructions="t", autonomy=" full_auto ")

# Typo raises — no silent fallback to a disabled autonomy:
Agent(instructions="t", autonomy="fll_auto")
# ValueError: Unknown preset 'fll_auto' for parameter 'autonomy'.
#             Did you mean 'full_auto'? Valid presets: auto_edit, full_auto, suggest
Call (Python)Before PR #4186After PR #4186
Agent(autonomy="full_auto")✅ autonomy enabled✅ autonomy enabled
Agent(autonomy="full-auto")⚠ validated → silently disabled✅ autonomy enabled
Agent(autonomy=" full_auto ")⚠ validated → silently disabled✅ autonomy enabled
Agent(autonomy="FULL_AUTO")⚠ validated → silently disabled✅ autonomy enabled
Agent(autonomy="fll_auto") (typo)❌ ValueError with suggestion❌ ValueError with suggestion
python
from praisonaiagents import Agent

agent = Agent(
    name="Assistant",
    instructions="You are an autonomous coding assistant.",
    autonomy=True,
)

agent.start("Refactor the authentication module to use JWT tokens.")

The user delegates a coding task; the agent works independently, detects doom loops, and escalates to a human when stuck.

Diagram

Autonomy Flow

No

Yes

📋 Task

⚙️ Execute

🔍 Doom Loop?

✅ Progress

🚨 Escalate

🎉 Complete

👤 Human Input

Quick Start

  1. Level 1 — Bool (simplest)

    Turn on autonomy with a single flag — defaults to the safe suggest level.

    python
    from praisonaiagents import Agent
    
    agent = Agent(
        name="Coder",
        instructions="You are an autonomous task-completion agent.",
        autonomy=True,
    )
    agent.start("Write and test a Python function to merge sorted arrays.")
  2. Level 2 — String (pick a level)

    Pass a level name to unlock more independence.

    python
    from praisonaiagents import Agent
    
    agent = Agent(
        name="Coder",
        instructions="You are an autonomous coding agent.",
        autonomy="full_auto",
    )
    agent.start("Build a REST API endpoint for user authentication.")
  3. Level 3 — Config class (full control)

    Use AutonomyConfig to tune iteration caps, doom-loop detection, and escalation.

    python
    from praisonaiagents import Agent, AutonomyConfig
    
    agent = Agent(
        name="Coder",
        instructions="You are a precise autonomous agent.",
        autonomy=AutonomyConfig(
            level="auto_edit",
            max_iterations=30,
            doom_loop_threshold=5,
            auto_escalate=True,
        ),
    )
    agent.start("Review and improve the test coverage for the payment module.")

Autonomy Levels

Diagram

Suggestions only

Auto-edit files

Full autonomous loop

How much autonomy?

suggest\ndefault — propose changes, user approves

auto_edit\ncan edit files automatically

full_auto\niterative loop with completion detection


From YAML

When you set autonomy in agents.yaml, the wrapper accepts an integer 0-10 (validated by the Pydantic schema) and maps it onto the core preset before handing it to Agent(autonomy=…):

YAML valueCore preset
0 (or omitted)off
1 – 3suggest
4 – 7auto_edit
8 – 10full_auto
yaml
framework: praisonai
roles:
  refactor_bot:
    role: "Refactor Bot"
    goal: "Refactor code safely"
    backstory: "Meticulous senior engineer"
    autonomy: 5           # -> auto_edit preset
    tasks:
      refactor_module:
        description: "Refactor auth.py to use JWT"
        expected_output: "A cleaner auth.py"

bool / str preset / dict / AutonomyConfig values in YAML are passed through unchanged, mirroring the Python API.


How It Works

Diagram
SafetyAutonomyAgentUserSafetyAutonomyAgentUseralt[Doom loop detected][No doom loop]loop[Autonomy iterations]taskExecute stepDoom loop checkEscalate to userRequest clarificationContinueTask complete
PhaseWhat happens
1. ExecuteAgent takes action toward the goal
2. Safety checkDoom loop tracker monitors for repeated identical actions
3. EscalateIf stuck, agent asks user for guidance instead of looping
4. CompleteIterative mode detects task completion signal

Completion Signals

The loop can stop on several signals — ranked by how reliably they mean “really done”.

Completion SignalHow It WorksReliability
Goal judge (run_goal)Independent judge evaluates acceptance criteria after each iteration⭐⭐⭐⭐ Most reliable — explicit contract
Tool completionModel stops calling tools after a substantive response⭐⭐⭐ Good for tool-driven tasks
Completion promiseModel emits the configured completion_promise signal text⭐⭐ Depends on the model following instructions
Keyword heuristicWords like “done”/”finished” or 2 no-tool turns⭐ Prone to false positives

Configuration Options

Card

Full list of options, types, and defaults — AutonomyConfig

The most common options at a glance:

OptionTypeDefaultDescription
levelstr"suggest""suggest", "auto_edit", or "full_auto"
max_iterationsint20Max iterations before stopping
doom_loop_thresholdint3Repeated actions before doom loop detection
auto_escalateboolTrueAutomatically escalate when stuck
completion_promise`str \None`NoneSignal text that marks task completion
max_budget_usd`float \None`NoneCumulative USD spend cap for the run. None = unlimited
max_tokens`int \None`NoneCumulative token cap (input + output). None = unlimited
budget_actionstr"pause""pause" (recoverable) or "stop" (terminal) when a cap is hit
snapshotboolFalseTake a pre-run workspace snapshot in run_autonomous() so agent.undo() can restore it. Requires track_changes. Off by default because snapshotting walks the whole project. Needed for agent.undo() — see File Snapshot

Common Patterns

Pattern 1 — Auto-edit mode for coding tasks

python
from praisonaiagents import Agent, AutonomyConfig

agent = Agent(
    instructions="You are a senior software engineer.",
    autonomy=AutonomyConfig(
        level="auto_edit",
        max_iterations=15,
        doom_loop_threshold=3,
    ),
)
response = agent.start("Add input validation to all API endpoints in the users module.")
print(response)

Pattern 2 — Full-auto iterative mode

python
from praisonaiagents import Agent, AutonomyConfig

agent = Agent(
    instructions="You are an autonomous DevOps agent.",
    autonomy=AutonomyConfig(
        level="full_auto",
        completion_promise="DEPLOYMENT_COMPLETE",
        max_iterations=50,
    ),
)
agent.start("Deploy the application to staging and run smoke tests.")

Pattern 3 — Budget-bounded autonomous runs

Cap an unattended run by money or tokens so it can’t burn dollars silently.

python
from praisonaiagents import Agent, AutonomyConfig

agent = Agent(
    instructions="You are an autonomous research agent.",
    autonomy=AutonomyConfig(
        level="full_auto",
        max_iterations=50,
        max_budget_usd=2.00,     # stop if this run crosses $2
        max_tokens=500_000,      # or 500k tokens, whichever trips first
        budget_action="pause",   # keep the partial result so we can resume
    ),
)

result = agent.run_autonomous("Research and summarise recent LLM safety papers.")

if result.completion_reason == "budget_exhausted":
    print(f"Paused at ${result.metadata['spend_usd']:.2f}")
    print("Partial output:", result.output)

Caps are measured from a loop-start baseline, so reusing the same agent across runs never carries prior spend into a new run’s cap.


Choosing a Cap

Pick the cap that matches what you care about most for the run.

Diagram

Turns matter most

Money matters most

Tokens matter most

Should it be recoverable?

Resume later

Terminal

What bounds this run?

max_iterations

max_budget_usd

max_tokens

budget_action

pause

stop


Handling budget_exhausted

When a cap trips, the run stops with completion_reason="budget_exhausted" and a metadata payload describing the spend.

Diagram
BudgetAgentUserBudgetAgentUseralt[Under cap][Cap exceeded]loop[Each iteration]run_autonomous(“task”)check spend vs capcontinuebudget_exhaustedAutonomyResult(status=”paused”, metadata={…})

The metadata dict carries the actual spend, the configured caps, and the status:

python
result = agent.run_autonomous("...")

if result.completion_reason == "budget_exhausted":
    print("Status:", result.metadata["status"])       # "paused" or "stopped"
    print("Spent USD:", result.metadata["spend_usd"])
    print("Spent tokens:", result.metadata["tokens"])
    print("Partial output so far:", result.output)

    # Pause is recoverable — raise the cap and re-run:
    agent.autonomy_config["max_budget_usd"] = 5.0
    result = agent.run_autonomous("...")
KeyTypeDescription
spend_usdfloatActual USD spent this run (baseline-adjusted)
tokensintActual tokens spent this run
max_budget_usd`float \None`The configured USD cap
max_tokens`int \None`The configured token cap
statusstr"paused" (recoverable) or "stopped" (terminal)

Best Practices

Start with suggest level

Start with autonomy=True (level suggest) to see what the agent proposes before giving it permission to edit files or run in a full loop. Graduate to auto_edit or full_auto when you trust the agent’s behavior.

Set doom_loop_threshold conservatively

The default threshold of 3 means 3 identical actions trigger escalation. Lower this to 2 for high-stakes tasks, or raise it to 5 for tasks where retrying the same action is expected behavior (like polling).

Use completion_promise for iterative mode

In full_auto mode, the agent loops until it detects completion. Set completion_promise="TASK_COMPLETE" and instruct the agent to output this signal when done, giving you clean loop termination.

Cap unattended runs with max_budget_usd

For full_auto mode running unattended (nightly jobs, background workers), set max_budget_usd as a hard ceiling. Combine with budget_action="pause" so a hit cap preserves the partial output and lets you raise the cap and resume rather than restart from zero.

Pick pause vs stop deliberately

budget_action="pause" returns a recoverable result — the partial output is preserved and metadata["status"] is "paused". budget_action="stop" marks it terminal (status="stopped"). Pick pause when a human might raise the cap and continue; pick stop when the run should be killed unconditionally.

Don't rely on AutonomyConfig.sandbox for isolation

AutonomyConfig.sandbox is a forward-compat placeholder — no autonomy level auto-enables sandboxing and nothing reads the field. Use Agent(sandbox=…) to enable the explicit agent.execute_code() API, or AgentFlow(run_on="docker") to isolate a whole workflow in a real container. See Placement.


Related