pydantic_ai.models.decision
For how an agent’s output type and tools become a decision model’s questions, and how to implement one, see Decision models.
Descriptions of the two outcomes of a yes/no question.
Description of the yes outcome.
Type: JsonValue Default: None
Description of the no outcome.
Type: JsonValue Default: None
A yes/no question whose answer is the probability of yes.
What to decide about the state.
Type: JsonValue Default: None
Descriptions of the yes and no outcomes.
Type: NoulCriteria | None Default: None
The Decisions protocol question type.
Type: Literal[‘noul’] Default: 'noul'
A question that selects one named option.
Option labels mapped to their descriptions.
What to decide about the state.
Type: JsonValue Default: None
The Decisions protocol question type.
Type: Literal[‘choice’] Default: 'choice'
A question that scores the state against ordered levels.
One description per level, starting at zero.
Type: list[JsonValue]
What to decide about the state.
Type: JsonValue Default: None
The Decisions protocol question type.
Type: Literal[‘score’] Default: 'score'
The probability of yes for a yes/no question.
The probability of yes, from 0 to 1.
Type: float
The Decisions protocol answer type.
Type: Literal[‘noul’] Default: 'noul'
The selected option and its probability distribution.
The selected option label.
Type: str
Confidence in the selected option.
Type: float
Probability for each option label.
The Decisions protocol answer type.
Type: Literal[‘choice’] Default: 'choice'
A score and its probability distribution across levels.
The expected score along the ordered levels.
Type: float
Confidence in the score.
Type: float
Probability for each level.
The descriptions of the levels, as the backend echoes them back, if it does.
Not read to build the output, which comes from score alone; kept so the answer is recorded as it was received.
Type: dict[int, JsonValue] Default: field(default_factory=(dict[int, JsonValue]))
The Decisions protocol answer type.
Type: Literal[‘score’] Default: 'score'
A request to decide typed questions about a state.
The text or JSON value to decide about.
Type: JsonValue
Named questions to answer about the state.
Type: dict[str, DecisionQuestion]
The answers to a DecisionRequest, and what produced them.
Answers keyed by question name.
Type: dict[str, DecisionAnswer]
The model that produced the answers.
Type: str
Usage for this request.
Type: RequestUsage Default: field(default_factory=RequestUsage)
The backend’s identifier for this request, if it returned one.
Recorded as gen_ai.response.id on the request’s decide span. It is not copied to the
ModelResponse, which can be built from two Decisions requests.
Type: str | None Default: None
Bases: ModelSettings
Settings used for a decision model request.
How likely a yes has to be before a bool field is True, from 0 to 1. Default: 0.5.
A decision model answers a yes/no with the probability of yes, and the default rounds it: what the framework
cannot know is what True has to mean for you. Raise it where a false positive is the expensive mistake and a
True should be earned, lower it where a false negative is. It applies to every bool field and to each option
of a list of a Literal or Enum, which is one yes/no per option; a float bounded with ge=0 and le=1
returns the probability itself and is not thresholded.
Reported confidence is the distance from the threshold rather than from the probability, scaled to run from 0 at the threshold to 1 at certainty, so a yes at 0.8 under a threshold of 0.75 reports the narrow margin it is.
Type: float
How likely the picked route has to be before it is taken, from 0 to 1. Default: unset, so the pick always is.
With tools attached, or a union of output types, one more question asks which route the text calls for: a
tool, an output type, an output function or None. The likeliest route is taken. With this set, a pick whose
own probability is below it raises UnsureRoute instead, before
any request to fill it. That is a ModelAPIError, so a
FallbackModel with a language model behind the decision model
hands that model the step; without one, the run raises it.
A route taken without a pick is not held to it: the one route left when every other has returned this turn, or a single output type with nothing else on offer. A higher threshold hands off more steps and gets more of the rest right; tune it on labelled examples of your own.
This is not a guard for a tool with side effects, such as a refund or an account change: require approval for that tool instead.
Type: float
Bases: ModelAPIError
A decision model handed the step off instead of answering it: the base of the hand-offs it raises.
A ModelAPIError, so a
FallbackModel with a language model behind the decision model
hands that model the whole step by default, tools and all, and only the steps the decision model hands off cost
a language model call. Pass fallback_on=DecisionHandOff to hand off only these, and let an error from the
decision model’s backend fail the run rather than go to the language model.
The route the model picked, by the label the route question offered it under.
Type: str Default: route
How likely the model found the picked route, from 0 to 1.
Type: float Default: probability
Bases: DecisionHandOff
A decision model picked a route whose fields or arguments it cannot fill.
A tool with an argument the model cannot express, such as a free-form str, or an output type with such a
field. See DecisionHandOff for how a
FallbackModel takes the step.
Deprecated alias for route.
For a tool, the tool’s name. For an output type, the name the route question offered it under, as Reply,
rather than the name of the output tool Pydantic AI made for it.
Type: str
Bases: DecisionHandOff
A decision model picked a route less likely than decision_route_threshold.
Raised before any request to fill the route. See
DecisionHandOff for how a
FallbackModel takes the step.
The probability the model gave every route, by label.
Type: dict[str, float] Default: probabilities
The decision_route_threshold the pick fell below.
Type: float Default: threshold
Bases: Model[InterfaceClient]
Base class for decision models: models that answer typed questions about a text rather than write text.
A decision model is sent a state, the text or JSON value to judge, and a set of named questions of three
kinds: a yes/no (NoulQuestion), a pick-one
(ChoiceQuestion), and a score against an ordered rubric
(ScoreQuestion). It answers each one with a probability or a
distribution. That exchange is the Decisions protocol, and this class maps an agent run onto it, so that an
agent whose job is to decide something runs on a decision model like on any other model:
- Each field of the
output_typeis one question, and its type picks the kind: aboolis a yes/no, aLiteralorEnumof strings is a pick-one, and whole numbers from 0 with a description per level are a rubric. Alistordictof options is one yes/no per option, and a nested model is its fields. A field of any other type is aUserErrorbefore a request is sent, unless there is another route to take, as below. - The field’s description is the question, the output type’s docstring its goal, and the agent’s
instructionsframing shared by every question; a nested field’s question also carries what it sits in. The latest user prompt is the text to judge, the message history before it goes along beside it, and once a tool has returned, what was done since goes along apart from both. - With tools attached, or a union of output types, one more pick-one asks which route the text calls for, and
the likeliest is taken. The fields of every route the model can fill are asked beside it, each on the premise
of its route, and only the taken route’s answers are read; past a size cutoff, a picked route with fields is
filled in a second request instead. A route whose fields the model cannot express, a single output type’s
included, is raised as
UnfillableRoute, for aFallbackModelto hand to a language model. A pick belowdecision_route_threshold, when set, is raised asUnsureRoutethe same way. - Each field’s confidence, the full distribution of each pick-one and rubric, and the route pick are reported
in
ModelResponse.provider_details.
The answers arrive in one piece, so a streamed run gets the whole answer as one event.
To support a backend, subclass this, implement decide
along with model_name, system and base_url, and set max_choice_options and max_score_levels to the
backend’s limits. See Decision models for the full rules
and an example.
The most options the backend accepts in one pick-one question, or None for no limit.
A pick-one field with more options, or more routes than this on the route question, is a
UserError before a request is sent.
Type: int | None Default: None
The most levels the backend accepts in one rubric, or None for no limit.
Whole numbers from 0 with more levels than this are not a rubric, so a field of them is asked as a pick-one
instead, and counts against max_choice_options.
Type: int | None Default: None
The model profile, with text output off whatever the provider or profile= says.
A decision model answers questions and has no way to write text, so this is a fact about the class
rather than a default to override: with text output left on, an output_type like [Ticket, str]
would pass the shared request preparation and have its str branch silently never taken.
Type: ModelProfile
@abstractmethod
@async
def decide(
request: DecisionRequest,
model_settings: DecisionModelSettings,
) -> DecisionResponse
Send one request to the backend and return its answers.
This is called once per request the model makes: once per step, or twice when a route is picked in one
request and, past the size cutoff for asking every route’s fields up front, filled in a second. Every
question in request.questions needs an answer of the matching kind under the same name.
Raise ModelHTTPError or
ModelAPIError when the backend fails, so a
FallbackModel can take over;
UnexpectedModelBehavior when it returns something that
cannot be read; and UserError when the request cannot be sent as
given. Forward timeout, extra_headers and extra_body from model_settings where the backend
supports them.
DecisionResponse
Bases: StreamedResponse
A decision model’s whole answer as one event, so that a streamed run works on a model that cannot stream.
@async
def close_stream() -> None
No live stream to close: the whole answer was in hand before the first event.
A question supported by the Decisions protocol.
Type: TypeAlias Default: NoulQuestion | ChoiceQuestion | ScoreQuestion
An answer returned by the Decisions protocol.
Type: TypeAlias Default: NoulAnswer | ChoiceAnswer | ScoreAnswer