Back to Writing

Route User Queries With PydanticAI's Agent Framework

March 1, 2026·3 min read·
AI ContentPydanticAIAgent

In this tutorial, you'll explore a few key features of PydanticAI, a Python agent framework that intends to bring the same developer experience to agent building that FastAPI brought to APIs. The library places type safety and data validation at the forefront, allowing users to produce validated structured output, without adding extra tools or prompt adjustments. PydanticAI is a good fit for beginners and those building production agent applications, as it leans heavily on common principles of class definitions and data modeling, allowing developers to extend this functionality as they dive deeper into multi-turn agents.

Through this tutorial, you'll build a semantic router that returns the best available tool based on the user's query. This pattern is useful for when you have multiple tools available for an agent to choose from, and need to select the right tool(s) based on the information the user needs.

By the end of this tutorial you'll know how to:

  • Define a BaseModel class in Pydantic
  • Instantiate and use an Agent in PydanticAI
  • Validate and return structured data from your agent

Setup Your PydanticAI Project

Before beginning to code, you'll start by setting up your environment following Python's best practice of utilizing virtual environments.

For Linux and Mac OS users, you will:

cd semantic-router
python -m venv .venv
source .venv/bin/activate

For Windows powershell the same can be accomplished by:


cd semantic-router
python -m venv .venv
.venv\Scripts\Activate.ps1

Once you have activated your virtual environment, install the following dependencies to get started.

pip install pydantic-ai

Define Your Data Models

When designing agentic systems, it's important to start with thinking through the data flow and overall data model up front. This allows you to understand what context is needed for the agent to appropriately make decisions, how to present that context to the agent, and how to best manage that context over time. With this in place, scaling your agent becomes less fragile because you have laid out the format for adding additional context, and you can trust the structure of the agent's output.

In PydanticAI, you accomplish this by leveraging the original Pydantic library inside the agentic framework. You'll define the data models for how the agent should expect data, whether the agent needs context dependencies injected to complete its task, and how the agent should format its output. This tutorial uses Pydantic's built-in BaseModel and Field modules to ensure the data is type safe and validated before and after the agent processes a user's query.

Start by importing the necessary dependencies:

from pydantic import BaseModel, Field
from pydantic_ai import Agent
import asyncio

Next you will define your first BaseModel class, which in this case establishes how you expect the agent to return the routing decision based on the user's query. If the output fields are not properly aligned with your BaseModel class, PydanticAI will automatically rerun this for you to ensure that these parameters are met.

class RouteDecision(BaseModel): 
    intent: str = Field( description="The category name that best matches the user" " query. If no category matches, set to 'no-match'" ) 
    confidence: float = Field(ge=0, le=1) 
    reasoning: str

Building the Agent

Now, with the output BaseModel built, you can instantiate an Agent and begin to define what it expects at run time, the model that you prefer to use, as well as what you expect in the form of output.

You will start by using PydanticAI's agent class.

router_agent = Agent( "anthropic:claude-sonnet-4-5-20250514", 
    output_type=RouteDecision, 
    instructions=( "You are a semantic router designed to properly route a" 
    " user's query to the appropriate category for tool" 
    " calling. Do not guess, if you do not know the answer" 
    " provide 'no-match' for intent.\n\n" "Available categories:\n" 
    "- hr-documents: Document repository for HR documents" 
    " detailing employee training and benefits information." 
    ' Examples: "What\'s the 401k employer match?",' 
    ' "How much additional time off do you get each year?"\n' 
    "- employee-information: MCP Server to get employee" 
    " directory information." 
    ' Examples: "What is John Doe\'s office phone number?",' 
    ' "Who does Jane Doe report to?"\n' 
    "- patient-schedule: MCP Server to connect to patient" 
    " scheduling application." 
    ' Examples: "How many appointments does Dr. Doe have' 
    ' today?", "Are there any patients scheduled on' ' 2/2/2022?"' ), )

Now that you've instantiated the Agent() with expected output, instructions, and preferred model you can begin using this to run your router.

It should be noted, that using dependency injection for adding context to the agent at runtime is PydanticAI's preferred method for adding context. This will be covered in another tutorial, as the focus here is on how PydanticAI handles agent definition and output validation.

Running the Agent

Now that the main building blocks are complete, you will need to make sure that you set your AI model's API key in your environment variables.

# Anthropic 
export ANTHROPIC_API_KEY=your-api-key 
# OpenAI 
export OPENAI_API_KEY=your-api-key 
# Google Gemini 
export GEMINI_API_KEY=your-api-key

You can then run your agent by passing in a sample query, and test how it responds to various input.

async def main(): 
    query = input("What can I help you with: ") 
    result = await router_agent.run(query) 
    print(result.output) 
    print(f"Intent: {result.output.intent}") 
    print(f"Confidence: {result.output.confidence}") 
    print(f"Reasoning: {result.output.reasoning}") 

if __name__ == "__main__": 
    asyncio.run(main())

The above code will prompt you for your query, which you can use to ask various business questions and have the semantic router determine which tool is best to use for that question.

Conclusion

You have now created a semantic router with PydanticAI, and have what you need to continue building out the semantic routing layer. Continue to add your tool definitions, or take the next step by using the routing decision to call your tools.

In this tutorial you have learned:

  • How to define and use a Pydantic BaseModel
  • How to instantiate a PydanticAI Agent
  • How to validate structured output from an agent using PydanticAI

It should also be taken into account, that while PydanticAI is a good solution for type safe and validated structured outputs, there are trade-offs to be aware of. The library being newer will naturally have fewer examples and less community support than something like LangChain would. Also, if you are developing a tool that doesn't have strict requirements for structured output, the additional overhead to define and build these data models may not be worth the effort. You should always consider your product and desired end result when selecting the framework and approach you take.