Structured Outputs

Agentle allows you to define the structure of agent responses using Pydantic models, enabling strongly-typed outputs that can be easily integrated into your application logic.

Basic Structured Output

Here’s a simple example of using structured outputs:

from pydantic import BaseModel
from typing import List, Optional
from agentle.agents.agent import Agent
from agentle.generations.providers.google.google_genai_generation_provider import GoogleGenaiGenerationProvider

# Define your output schema
class WeatherForecast(BaseModel):
    location: str
    current_temperature: float
    conditions: str
    forecast: List[str]
    humidity: Optional[int] = None

# Create an agent with structured output
structured_agent = Agent(
    name="Weather Agent",
    generation_provider=GoogleGenaiGenerationProvider(),
    model="gemini-2.0-flash",
    instructions="You are a weather forecasting assistant. Provide accurate forecasts.",
    response_schema=WeatherForecast  # Define the expected response structure
)

# Run the agent
response = structured_agent.run("What's the weather like in San Francisco?")

# Access structured data with type hints
weather = response.parsed
print(f"Weather for: {weather.location}")
print(f"Temperature: {weather.current_temperature}°C")
print(f"Conditions: {weather.conditions}")
for day in weather.forecast:
    print(f"- {day}")

Working with Complex Models

You can use nested models and complex structures:

from pydantic import BaseModel, Field
from typing import List, Optional, Dict
from enum import Enum
from datetime import datetime

# Define an enum for weather conditions
class WeatherCondition(str, Enum):
    SUNNY = "sunny"
    CLOUDY = "cloudy"
    RAINY = "rainy"
    SNOWY = "snowy"
    STORMY = "stormy"

# Define a nested model for daily forecast
class DailyForecast(BaseModel):
    date: datetime
    high_temp: float
    low_temp: float
    condition: WeatherCondition
    precipitation_chance: float = Field(ge=0, le=1)  # Between 0 and 1
    wind_speed: float

# Define the main response model
class DetailedWeatherForecast(BaseModel):
    location: str
    country: str
    current: Dict[str, float]
    conditions: WeatherCondition
    forecast: List[DailyForecast]
    alerts: Optional[List[str]] = None
    last_updated: datetime

# Create an agent with the complex schema
detailed_weather_agent = Agent(
    name="Detailed Weather Agent",
    generation_provider=GoogleGenaiGenerationProvider(),
    model="gemini-2.0-flash",
    instructions="You are a weather forecasting assistant that provides detailed, structured forecasts.",
    response_schema=DetailedWeatherForecast
)

# Get structured response
response = detailed_weather_agent.run("Give me a detailed 5-day forecast for Tokyo, Japan")

# Access the structured data
forecast = response.parsed
print(f"Weather for {forecast.location}, {forecast.country}")
print(f"Current temperature: {forecast.current['temperature']}°C")
print(f"Current conditions: {forecast.conditions.value}")

print("\n5-day forecast:")
for day in forecast.forecast:
    date_str = day.date.strftime("%A, %B %d")
    print(f"{date_str}: {day.condition.value}, {day.low_temp}°C to {day.high_temp}°C")

Combining with Tools

For even more powerful agents, combine structured outputs with tool calling:

from pydantic import BaseModel
from typing import List, Optional

# Define a tool
def get_city_data(city: str) -> dict:
    """Get basic information about a city."""
    city_database = {
        "Paris": {
            "country": "France",
            "population": 2161000,
            "timezone": "CET",
            "famous_for": ["Eiffel Tower", "Louvre", "Notre Dame"],
        },
        # More cities...
    }
    return city_database.get(city, {"error": f"No data found for {city}"})

# Define the structured response schema
class TravelRecommendation(BaseModel):
    city: str
    country: str
    population: int
    local_time: str
    attractions: List[str]
    best_time_to_visit: str
    estimated_daily_budget: float
    safety_rating: Optional[int] = None

# Create an agent with both tools and a structured output schema
travel_agent = Agent(
    name="Travel Advisor",
    generation_provider=GoogleGenaiGenerationProvider(),
    model="gemini-2.0-flash",
    instructions="""You are a travel advisor that provides structured recommendations for city visits.""",
    tools=[get_city_data],
    response_schema=TravelRecommendation,
)

# Run the agent
response = travel_agent.run("Create a travel recommendation for Tokyo.")

# Access structured data
rec = response.parsed
print(f"TRAVEL RECOMMENDATION FOR {rec.city}, {rec.country}")
print(f"Population: {rec.population:,}")
print(f"Best time to visit: {rec.best_time_to_visit}")

Best Practices

  1. Clear Instructions: Make sure your agent instructions align with your schema requirements

  2. Schema Complexity: Balance schema complexity with model capabilities - too complex schemas may lead to validation errors

  3. Field Documentation: Add field descriptions to help the model generate appropriate values

4. Optional Fields: Use Optional for fields that might not always be present 6. Default Values: Provide sensible defaults for fields where appropriate