Pydantic AIPydantic AI 2.51 · Python 3.10+
0%
1
Curious builder0 XP earned · 300 to level 2
0 daysFinish a lesson to begin
Badge collection0 of 6 unlocked
29 small wins to finish your pathNext lesson →

Integrations: from stand-in to real backends

An integration is a real backend that replaces a keyless stand-in you built on, such as a hosted model for the scripted one, a persisted store for in-memory history, or a remote server for a local MCP script.

Last updated: 28 Sep, 2026 · Pydantic AI 2.51

Every lesson so far ran with no key, on a stand-in. That is right for learning and testing, but a shipped desk needs the real thing. This lesson maps each stand-in to its production backend and shows the one line that swaps it in.

The stand-in and the real model share a base

The swap is a drop-in because the scripted FunctionModel and a hosted model are both subclasses of the same Model base, so the agent code around them does not change:

python
from pydantic_ai.models import Model
from pydantic_ai.models.openai import OpenAIChatModel
# both are Model subclasses, so either can go in Agent(model=...)

Proving the drop-in shares the base

Both classes answer to issubclass(..., Model), which is why swapping one for the other needs no other change:

Example
from pydantic_ai.models import Model
from pydantic_ai.models.function import FunctionModel
from pydantic_ai.models.openai import OpenAIChatModel

print(issubclass(FunctionModel, Model))    # the stand-in you used
print(issubclass(OpenAIChatModel, Model))  # a real hosted model

From stand-in to a real backend

Each piece you used maps to a production backend from the framework's own integrations, added the same way you added the stand-in:

What you usedA real onePackage or string
FunctionModel / TestModel stand-inA hosted model by provider string"openai:gpt-4.1", "groq:openai/gpt-oss-120b"
In-memory message historyA persisted conversation store you load and saveYour own database, keyed by conversation id
An MCP script started per runA remote MCP server over HTTPMCPToolset("https://host/mcp")
No tracingPydantic Logfire over OpenTelemetrylogfire
run_sync in one processDurable execution that survives a crashTemporal, DBOS, Prefect or Restate (optional)

The one-line real-model swap

Naming a provider string in place of the stand-in is the whole change. It needs a key, so it is shown, not run:

python
from pydantic_ai import Agent

# was: Agent(FunctionModel(desk_reply), ...)
agent = Agent("openai:gpt-4.1", ...)               # needs OPENAI_API_KEY
# or another provider, same shape:
agent = Agent("groq:openai/gpt-oss-120b", ...)     # needs GROQ_API_KEY

Persisting the conversation

The in-memory history from Message history: continuing a conversation lives only for the program's life. In production you save result.all_messages_json() under a conversation id and load it back on the next ticket, so a restart does not lose the thread. The store is your own database; the framework gives the bytes to save.

Durable execution, when a run must not be lost

A long run that calls tools and pauses for approval can be wrapped so it survives a crash and resumes where it stopped. Pydantic AI supports Temporal, DBOS, Prefect and Restate for this. It is optional: reach for it when a lost run costs money, not for a chat reply.

Stand-in vs production

Stand-in (this course)Production
ModelScripted, no keyHosted, by provider string, with a key
HistoryA list in memoryA store keyed by conversation id
Tools from another programA script started per runA remote MCP server over HTTP
VisibilityPrint the message listLogfire traces over OpenTelemetry

Where you use real backends

  • Turning the tested desk into one that answers real customers.
  • Moving a local MCP script to a server the whole team shares.
  • Adding durable execution before a refund flow that must never be dropped.
A model name is a choice, not a default
A provider string such as "openai:gpt-4.1" is only a name until a key is present; the model that name points to changes over time. Read the key from the environment, pick the model per your own cost and quality test, and do not hardcode a model as the one right answer.
Try it yourself
  • Swap desk_model for "openai:gpt-4.1" in app.py, set OPENAI_API_KEY, and run one ticket.
  • Save a run with all_messages_json() to a file and load it back on the next run.
  • Point an MCPToolset at a URL instead of a script path.

You understood something today that you didn't yesterday.