Pydantic AI Behind a Gateway: Routing, Identity, and What the Type System Does Not Cover
Pydantic AI gives an agent a typed output contract, typed dependencies, and validated tool signatures, which removes a real class of parsing and argument bugs. Schema validation runs after the model call and inside the process, so it covers structure rather than authorization, egress, or evidence. This walks what the type system enforces, the four gaps that remain, and how to route a Pydantic AI agent through a gateway without changing agent code.

Pydantic AI applies the validation model that made Pydantic ubiquitous in Python to agent code. You declare an output type, the framework asks the model for structure matching it, and a response that fails validation gets retried with the errors fed back. Tool functions are ordinary Python with type hints, and their signatures become the schema the model sees. Dependencies arrive through a typed injection container rather than through globals.
That removes a genuine class of bugs. A parsing error where the model returned prose instead of JSON, a tool receiving a string where it expected an integer, a missing field discovered three functions later. Those stop happening.
Four things it does not do, and each of them is a security property rather than a correctness property. I want to walk them, then show the routing change that addresses three.
What the type system covers
output_type binds the agent's result to a Pydantic model. The framework handles the structured-output request to the provider, validates what comes back, and retries on failure. Downstream code receives a typed object.
Tool registration derives the JSON schema from the function signature, so a tool annotated def query_orders(customer_id: int, limit: int) -> list[Order] presents that shape to the model, and arguments are coerced and validated before your function body runs. That closes the naive version of the argument problem described in LLM tool integration security.
RunContext and the typed dependency container carry per-run state into tools without globals, which matters below.
Gap 1: Validation confirms shape, not authorization
A validated customer_id: int is an integer. Whether the caller may read that customer is a different question, and the type system has no opinion on it.
The specific defect to avoid is a tool signature that accepts identity from the model:
Pydantic AI's dependency injection is well suited to the correct pattern, which is why the wrong one is worth calling out. RunContext exists precisely so the authenticated principal travels alongside the run rather than through the context window. Teams reach for tool_plain because it is shorter, and identity ends up in the argument list.
Gap 2: Validation runs after the call
The output validator executes on the response. By then the prompt has already left your network, reached the provider, and been processed.
Every control concerning what goes out sits before that point: which data classes may appear in the context window, which identity is permitted to call which model, whether this request should have been made at all. A retry loop that rejects a malformed response does nothing about a prompt that carried an account number to a consumer-tier endpoint. The volume behind that concern is documented, with 18,033 TB of enterprise data moving into AI tools in Zscaler's ThreatLabz 2026 measurement, up 93% year over year (Infosecurity Magazine).
Gap 3: Validation runs in your process
The validator, the tool definitions, and the retry policy all live in the same repository as the agent. A developer widening a tool's scope and relaxing its check does both in one pull request, and no system outside that repository observes the change.
The multi-service version is worse. Three teams running Pydantic AI agents maintain three sets of validators at three levels of attention, and the policy that applies to a model call depends on which team wrote the caller. That inconsistency is the reason the enforcement point belongs outside the application, argued in why AI security must be inline.
Gap 4: Instrumentation is telemetry, not evidence
Pydantic AI instruments runs through OpenTelemetry, and Logfire consumes that nicely. Traces of agent runs, model calls, token counts, and tool invocations are genuinely useful for debugging and cost attribution.
They are emitted by the agent process about itself. When someone asks whether policy was enforced on a specific call on June 14, a trace produced by the code under question answers with an attestation from the party being audited. The distinction matters for EU AI Act Article 12 and for every regime that expects records to survive inspection (Article 12 detail).
Routing an agent through a gateway
The change is configuration rather than agent code. Pydantic AI's provider classes accept a base URL, so pointing an agent at a gateway is a constructor argument:
Two details carry the weight. The credential is scoped to the calling principal rather than a shared service key, so the gateway can attribute and authorize per identity instead of seeing one caller for the whole fleet. And the base URL is only meaningful if the network enforces it:
Without the egress rule, a base URL is a default that any environment variable can override.
What I would keep in the framework
The typed output contract and the typed dependency container, without hesitation. Structured output validation with automatic retry is the best ergonomics available in Python for this, and RunContext is the right shape for carrying an authenticated principal into tools. My objection is only to treating those as the security boundary. They are the correctness layer, and they are good at it.
Loop bounds
One control that belongs in the agent regardless of what sits in front of it: a cap on model calls per run. Pydantic AI exposes usage limits for exactly this. An agent that has made 40 calls on a task budgeted for 12 has left its intended trajectory whatever call 41 contains, and a ceiling costs a counter. The broader case is in AI agent guardrails.
DeepInspect
This is the gap DeepInspect closes. A Pydantic AI agent's model calls are HTTP requests to a provider endpoint, and DeepInspect sits inline at that boundary as a stateless proxy, reached by setting base_url and constraining egress.
For every call the agent makes, DeepInspect binds the request to the identity the caller presents, classifies the assembled prompt against your own data classes, evaluates identity-aware policy, and returns a deterministic fail-closed decision before the request reaches the provider. That runs before the model sees the prompt, which is where the framework's post-hoc validation cannot reach, and it applies uniformly to every service in the fleet rather than per repository. Every decision commits a signed, identity-bound audit record before the response returns, on a write path the agent process never controls, which is the record an assessor can test rather than a trace the agent wrote about itself. DeepInspect governs the model traffic and leaves database grants, IAM roles, and tool credentials to the systems that enforce them. If your Pydantic AI agents share one provider key and the traces come from the agents themselves, book a demo today.
Frequently asked questions
- Does Pydantic AI validation protect against prompt injection?
Not directly. Output validation confirms the response matches a declared schema, and an injected instruction can produce a schema-conformant response that carries the wrong content or triggers the wrong tool. What limits an injection is scoped tool credentials and authorization resolved from run context rather than from model-supplied arguments.
- How do you route Pydantic AI through an AI gateway?
Set
base_urlon the provider to the gateway endpoint and issue a per-principal credential instead of a shared service key. Then deny direct egress to provider domains from the agent tier so the base URL cannot be bypassed by an environment variable override.- Should tools take the user identity as an argument?
No. A tool accepting an identity from the model has delegated authorization to a text generator. Use
RunContextand the typed dependency container to carry the authenticated principal into the tool, where the model can neither see nor modify it, and resolve authorization against that.- Is Logfire instrumentation enough for AI compliance?
It is strong operational telemetry and it is emitted by the agent about itself. Regimes that expect records capable of surviving inspection, including EU AI Act Article 12, want decisions recorded outside the application being governed. Application traces and independent decision records serve different purposes and most programmes need both.