Skip to main content
Agents can return structured data in a specific format using Pydantic models, ensuring type safety and validation.

Overview

By default, agents return unstructured text answers:
result = agent.run(task="Find the contact email")
print(result.answer)  # "contact@example.com"
With structured output, define the exact format you want:
from pydantic import BaseModel

class ContactInfo(BaseModel):
    email: str
    phone: str | None

result = agent.run(
    task="Extract contact information",
    response_format=ContactInfo
)

print(result.answer.email)  # Type-safe access
print(result.answer.phone)  # None if not found

Basic Example

Define a Pydantic model and pass it to response_format:
from notte_sdk import NotteClient
from pydantic import BaseModel

client = NotteClient()

class Product(BaseModel):
    name: str
    price: float
    in_stock: bool

with client.Session() as session:
    agent = client.Agent(session=session)

    result = agent.run(
        task="Extract product information",
        url="https://example.com/product/123",
        response_format=Product
    )

    # Type-safe access
    product = result.answer
    print(f"{product.name}: ${product.price}")
    if product.in_stock:
        print("Available!")

Complex Models

Nested Structures

Models can contain nested objects:
from pydantic import BaseModel

class Address(BaseModel):
    street: str
    city: str
    zip_code: str

class Company(BaseModel):
    name: str
    address: Address
    employees: int | None

result = agent.run(
    task="Extract company information",
    response_format=Company
)

print(result.answer.name)
print(result.answer.address.city)

Lists of Objects

Extract multiple items:
from pydantic import BaseModel

class Review(BaseModel):
    author: str
    rating: int
    comment: str

result = agent.run(
    task="Extract all product reviews",
    response_format=list[Review]
)

# Iterate over reviews
for review in result.answer:
    print(f"{review.author}: {review.rating}/5")
    print(review.comment)

Optional Fields

Use None for optional fields:
from pydantic import BaseModel

class Product(BaseModel):
    name: str
    price: float
    discount: float | None = None  # Optional
    shipping_cost: float | None = None  # Optional

result = agent.run(
    task="Extract product details",
    response_format=Product
)

# Safe to access optional fields
if result.answer.discount:
    final_price = result.answer.price - result.answer.discount

Use Cases

E-commerce Data Extraction

Extract structured product data:
from pydantic import BaseModel
from datetime import date

class ProductListing(BaseModel):
    name: str
    price: float
    original_price: float | None
    rating: float
    review_count: int
    availability: str
    seller: str

result = agent.run(
    task="Extract product listing information",
    url="https://store.example.com/products/laptop",
    response_format=ProductListing
)

Lead Generation

Extract structured contact information:
from pydantic import BaseModel

class Lead(BaseModel):
    company_name: str
    contact_person: str | None
    email: str
    phone: str | None
    industry: str | None
    website: str

result = agent.run(
    task="Extract company contact details",
    url="https://example.com/about",
    response_format=Lead
)

Job Listings

Extract job posting details:
from pydantic import BaseModel

class JobPosting(BaseModel):
    title: str
    company: str
    location: str
    salary_range: str | None
    job_type: str  # "Full-time", "Part-time", etc.
    posted_date: str
    requirements: list[str]

result = agent.run(
    task="Extract job posting information",
    response_format=JobPosting
)

Social Media Data

Extract social media profiles:
from pydantic import BaseModel

class SocialProfile(BaseModel):
    username: str
    display_name: str
    bio: str | None
    follower_count: int
    following_count: int
    post_count: int
    verified: bool

result = agent.run(
    task="Extract social media profile information",
    response_format=SocialProfile
)

Field Validation

Use Pydantic validators for data quality:
from pydantic import BaseModel, Field, field_validator

class Product(BaseModel):
    name: str = Field(min_length=1)
    price: float = Field(gt=0)  # Must be positive
    rating: float = Field(ge=0, le=5)  # 0-5 range

    @field_validator('price')
    @classmethod
    def validate_price(cls, v):
        if v > 10000:
            raise ValueError('Price seems unreasonably high')
        return v

result = agent.run(
    task="Extract product",
    response_format=Product
)

Best Practices

1. Be Specific in Task Description

Match your task to the response format:
# Good - task matches response_format
result = agent.run(
    task="Extract the product name, price, and stock status",
    response_format=Product
)

# Less clear - agent might not fill all fields
result = agent.run(
    task="Tell me about this product",
    response_format=Product
)

2. Use Appropriate Types

Choose Python types that match the data:
from pydantic import BaseModel
from datetime import date

class Event(BaseModel):
    title: str
    date: date  # Will be parsed as date
    price: float  # Not str
    attendee_count: int  # Not float

3. Make Optional Fields Explicit

Don’t assume data will always be present:
class Product(BaseModel):
    name: str  # Always required
    price: float  # Always required
    discount: float | None = None  # Might not exist
    rating: float | None = None  # Might not exist

4. Use Field Descriptions

Help the agent understand what you want:
from pydantic import BaseModel, Field

class Product(BaseModel):
    name: str = Field(description="Product title/name")
    price: float = Field(description="Current selling price in USD")
    original_price: float | None = Field(
        description="Original price before discount, if any"
    )

5. Start Simple, Then Expand

Begin with basic models:
# Start with minimal model
class Product(BaseModel):
    name: str
    price: float

# Add fields as needed
class DetailedProduct(BaseModel):
    name: str
    price: float
    description: str | None
    specs: dict[str, str] | None

Error Handling

Handle validation errors:
from pydantic import ValidationError

try:
    result = agent.run(
        task="Extract product data",
        response_format=Product
    )
    product = result.answer
except ValidationError as e:
    print(f"Agent returned invalid data: {e}")

Limitations

Not Suitable For

Structured output works best for data extraction, not for:
  • Open-ended creative tasks
  • Tasks requiring explanation or reasoning
  • When you want natural language responses
# Don't do this - use plain text response instead
class Explanation(BaseModel):
    answer: str

# Do this instead
result = agent.run(task="Explain how this product works")
print(result.answer)  # Natural language explanation

Complex Relationships

Very complex nested structures may be challenging:
# Difficult for agents
class ComplexStructure(BaseModel):
    nested: dict[str, list[dict[str, Product]]]

# Better - flatten or simplify
class SimplifiedStructure(BaseModel):
    products: list[Product]
    categories: list[str]

Next Steps