Goal-Gated Autonomous Loop

Run an autonomous tool-using loop that only stops when an independent judge confirms your goal is met

The goal loop runs your agent with its tools and only stops when an independent judge confirms the goal is met — no more “done”-keyword false positives.

Diagram

Goal Loop

done

continue

max_turns

📋 Task + Goal

🔁 Tool-using Loop

⚖️ Independent Judge

✅ goal_met

📝 Continuation Nudge

⏸️ budget_paused

Quick Start

  1. Simple goal

    Pass a task and a goal — the judge decides when the goal is really met.

    python
    from praisonaiagents import Agent
    
    agent = Agent(instructions="You are an ops assistant", autonomy=True)
    
    result = agent.run_goal(
        "Tidy the repo and open a PR",
        goal="A PR is open with formatting fixes and green CI",
    )
    print(result.success, result.completion_reason)  # True goal_met
  2. With acceptance criteria

    Add structured criteria so the judge anchors to concrete evidence.

    python
    from praisonaiagents import Agent
    from praisonaiagents.goal import GoalCriteria
    
    agent = Agent(instructions="You are an ops assistant", autonomy=True)
    
    result = agent.run_goal(
        "Tidy the repo and open a PR",
        goal="Repo formatted, tests green, PR opened",
        criteria=GoalCriteria(
            outcome="A PR is open with formatting fixes and passing CI",
            verification="A PR URL exists and the test command exits 0",
            constraints=["do not touch CHANGELOG.md", "no force-push"],
        ),
        max_turns=15,
    )
  3. Resume a paused or crashed run

    Give the agent a fixed memory.session_id and run_goal() picks up where the last run stopped — even from a brand-new process. turns_used is kept, never reset.

    python
    from praisonaiagents import Agent
    
    agent = Agent(
        instructions="You are an ops assistant",
        autonomy=True,
        memory={"session_id": "repo-tidy", "history": True},
    )
    
    agent.run_goal("Continue the task", goal="Repo formatted, tests green, PR opened")

    Two cases resume:

    • Budget-paused — the last run hit max_turns (budget_paused).
    • Crashed — the process died mid-run, so the saved state is still active. A fresh Agent in a new process resumes it instead of restarting at turns_used=0 (the behaviour before PR #6050).

How It Works

The loop iterates with tools; after each iteration an independent judge reads the goal plus the latest output and returns done or continue.

Diagram
JudgeAgentUserJudgeAgentUseralt[Evidence meetsthe bar][Not yet /constraintviolated]loop[Until goal_met or max_turns]run_goal(task, goal, criteria)Iterate with toolsgoal + latest output taildonegoal_met (success=True)continue + reasonContinuation nudgebudget_paused (recoverable)

The judge is strict about evidence:

Do NOT accept mere claims of completion (e.g. the word ‘done’) as evidence. Require concrete evidence that the goal is actually met.

How resume matches a saved run

Goal state is saved in the session store after every turn. On run_goal(..., resume=True) the agent loads it — opening the store first, so a fresh process sees it too.

Diagram
Process 2Session storeProcess 1Process 2Session storeProcess 1💥 crashsave goal state (turns_used=3, active)load state for session_idgoal matches, status activecontinue from turns_used=3

A saved run resumes only when all of these match:

CheckRequirement
GoalSame goal text (exact match) — a different goal starts fresh
SessionSame memory.session_id, so the store resolves to the same session
Statuspaused or active (crashed) — finished runs are never resumed

Acceptance Criteria

GoalCriteria describes what “done” means, how to check it, and what must never happen.

Diagram

A one-line definition

A checkable evidence bar

Actions that must never happen

What do you know about done?

outcome

verification

constraints

FieldTypeDefaultDescription
outcomestr""What “done” means in one line — the definition the judge anchors to
verificationstr""The concrete evidence bar the judge requires before returning done
constraintsList[str][]Must-not-violate conditions; any violation forces continue

run_goal() Options

OptionTypeDefaultDescription
taskstr—The task/prompt the agent executes
goalstr—The goal text the judge evaluates against
criteriaOptional[GoalCriteria]NoneStructured acceptance criteria (recommended)
max_turnsint20Judged iterations before a recoverable pause
judge_modelOptional[str]OPENAI_MODEL_NAME env or "gpt-4o-mini"Independent judge model — use a different model from the actor to avoid self-enhancement bias
resumeboolTrueResume a persisted paused or still-active (crashed mid-run) run for the same goal instead of restarting. Works across processes — a fresh Agent sees state saved by an earlier process

Completion Reasons

The loop returns an AutonomyResult whose completion_reason tells you why it stopped.

completion_reasonMeaning
"goal_met"Judge returned done with concrete evidence — success=True
"budget_paused"max_turns exhausted, or the judge returned 3 consecutive unparseable responses — success=False, run is recoverable via a second run_goal(..., resume=True) call
(other)Falls back to the standard run_autonomous heuristics — see Autonomy

The Independent Judge

The judge is deliberately simple, cheap, and safe to fail.

Independent

Uses a separate judge model so the acting model never self-grades — this avoids self-enhancement bias.

Fail-open

A broken or timed-out judge yields continue, so a weak judge never wedges progress.

Cheap

Only the goal plus the last ~4000 characters of agent output are judged, not the whole transcript.

Constraint-strict

Any constraint listed in GoalCriteria.constraints blocks a done verdict.

Auto-pause safety valve

3 consecutive unparseable judge responses auto-pause the loop with budget_paused, so a wedged judge cannot silently burn the whole budget.


Async Variant

run_goal_async() mirrors run_goal() for async code.

python
result = await agent.run_goal_async("Tidy the repo", goal="A PR is open with green CI")

run_until(goal=...) Delegation

Passing goal= to run_until() now delegates to run_goal() — the acceptance-criteria judge gates a real tool-iteration loop instead of re-generating a whole answer.

python
from praisonaiagents import Agent
from praisonaiagents.goal import GoalCriteria

agent = Agent(instructions="You are an ops assistant", autonomy=True)

result = agent.run_until(
    "Do the task",
    goal="A PR is open with green CI",
    goal_criteria=GoalCriteria(outcome="PR merged", verification="PR URL exists"),
)
# Returns AutonomyResult (not EvaluationLoopResult)
print(result.completion_reason)  # "goal_met" | "budget_paused"

Best Practices

Use a different judge model

Set judge_model to a different model than the actor so the run is not self-graded.

Write verification as a checkable condition

Use concrete, checkable evidence (“exit code 0”, “PR URL exists”), not a feeling.

Keep max_turns low for first runs

Start with max_turns=10–20 and let resume=True continue a paused run when needed.

Fill constraints with forbidden actions

constraints is the only field that can block a done verdict — list what must never happen.


Related