Skip to content

Goal API

The goal loop keeps the agent working toward a completion condition across turns, judged by a small, fast model. See Goal loop for the overview and the CLI /goal command.

GoalEvaluator

pydantic_deep.goal.GoalEvaluator dataclass

Judges whether a goal condition is met using a small, fast model.

The evaluator does not call tools; it reasons only over the transcript built by :func:build_goal_transcript, matching Claude Code's behaviour where the condition must be demonstrable from what the agent has already surfaced.

Parameters:

Name Type Description Default
model Model | str

pydantic-ai model string or :class:~pydantic_ai.models.Model instance (default: a small Haiku model).

DEFAULT_GOAL_MODEL
max_context_messages int

Recent-message budget for the transcript.

12
max_chars_per_part int

Per-part truncation budget for the transcript.

600

evaluate(condition, messages) async

Evaluate condition against the conversation messages.

The model is constrained to a :class:Verdict via output_type (no reply-text parsing). On any failure the evaluation defaults to not met (with an error reason) so a transient evaluator hiccup keeps the agent working rather than declaring premature success.

GoalState

pydantic_deep.goal.GoalState dataclass

Mutable, session-scoped state for one active goal.

The host owns the wall-clock timer (started_monotonic) and increments turns / token counters as it drives the loop. max_turns is a hard safety cap so a poorly-specified condition can never loop forever.

Parameters:

Name Type Description Default
condition str

The completion condition the agent works toward.

required
turns int

Number of evaluation turns spent so far.

0
achieved bool

Set once the evaluator confirms the condition is met.

False
last_reason str

The evaluator's most recent reason.

''
max_turns int

Hard cap on evaluation turns before the loop stops itself.

100
started_monotonic float | None

time.monotonic() baseline set by the host.

None
input_tokens int

Cumulative evaluator prompt tokens.

0
output_tokens int

Cumulative evaluator completion tokens.

0

is_active property

Whether the goal is still being worked toward.

exhausted property

Whether the turn cap has been reached without achieving the goal.

record(evaluation)

Fold one evaluation into the running state.

GoalEvaluation

pydantic_deep.goal.GoalEvaluation dataclass

Result of a single goal evaluation.

Parameters:

Name Type Description Default
met bool

Whether the evaluator judged the condition satisfied.

required
reason str

One-sentence explanation, surfaced to the user and fed back to the agent as guidance for the next turn when met is False.

required
impossible bool

Whether the evaluator judged the condition genuinely unachievable this session (self-contradictory, needs an unavailable resource, or the agent exhausted reasonable approaches). Always implies met is False; the host stops the loop instead of grinding to the turn cap. Never True when met is True.

False
input_tokens int

Prompt tokens spent by the evaluator (0 if unknown).

0
output_tokens int

Completion tokens spent by the evaluator (0 if unknown).

0

parse_goal_command

pydantic_deep.goal.parse_goal_command(arg)

Interpret the argument to a /goal command.

Returns a (action, condition) pair:

  • empty argument → ("status", "")
  • a clear alias (see :data:GOAL_CLEAR_ALIASES) → ("clear", "")
  • anything else → ("set", <condition truncated to the char cap>)

build_goal_transcript

pydantic_deep.goal.build_goal_transcript(messages, max_recent=12, max_chars=600)

Render a compact transcript for the evaluator to judge against.

Unlike the periodic-reminder transcript, this keeps the content the evaluator needs — assistant text and tool results (truncated) — because the verdict depends on evidence the agent surfaced (test output, build status, file state). The original request is always included as anchoring context.

Parameters:

Name Type Description Default
messages list[ModelMessage]

Full conversation history.

required
max_recent int

How many of the most recent messages to render in detail.

12
max_chars int

Per-part truncation budget.

600

goal_continue_directive

pydantic_deep.goal.goal_continue_directive(condition, reason)

Build the synthetic prompt that drives the next goal turn.

format_goal_status

pydantic_deep.goal.format_goal_status(state, elapsed_seconds=None)

Render a human-readable status block for /goal with no argument.