Complex nested extraction

Real-world data is nested. A resume has personal info, a list of work experiences (each with company, title, dates), a list of education entries, and a list of skills. Pydantic handles this beautifully with nested models.

Nested model hierarchy

How Pydantic models nest to represent complex data like resumes

Pydantic supports arbitrary nesting depth. An Order can contain Products, each Product can contain Ingredients, each Ingredient can contain Allergens. However, the deeper you nest, the more specific your prompt needs to be so the AI knows the exact structure. Two to three levels deep is common in practice.

03-structured-outputs.ipynb
python
class WorkExperience(BaseModel):
    company: str
    title: str
    start_date: str
    end_date: str
    description: Optional[str] = None

class Education(BaseModel):
    school: str
    degree: str
    year: str

class Resume(BaseModel):
    name: str
    email: str
    phone: Optional[str] = None
    work_experience: list[WorkExperience]
    education: list[Education]
    skills: list[str]

Nested Pydantic models: Resume contains lists of WorkExperience and Education objects. Each is independently validated.

list[WorkExperience] is modern Python syntax (3.9+) for type hints. It means "a list containing WorkExperience objects." Older code uses List[WorkExperience] from the typing module, which means the same thing.

03-structured-outputs.ipynb
python
resume_text = """
John Smith | [email protected] | (555) 987-6543

EXPERIENCE
Senior Developer at TechCorp (2020-Present)
- Led team of 5, built microservices platform
Junior Developer at StartupXYZ (2018-2020)
- Full-stack web development with React

EDUCATION
BS Computer Science, MIT, 2018

SKILLS: Python, JavaScript, React, AWS, Docker
"""

prompt = f"Extract resume as JSON: {resume_text}"
data = extract_json(prompt)
resume = Resume(**data)

print(f"Name: {resume.name}")
print(f"Skills: {', '.join(resume.skills)}")
print(f"Jobs: {len(resume.work_experience)}")

The AI parses free-form resume text into a fully structured, validated Resume object with nested lists.

Use flat models when your data is a simple list of fields, like extracting a name and email. Use nested models when data has repeating groups, like multiple jobs on a resume or multiple items on an invoice. If you find yourself wanting to name fields job1_title, job2_title, that is a sign you need a nested list instead.

03-structured-outputs.ipynb
python
class InvoiceItem(BaseModel):
    description: str
    quantity: int
    unit_price: float
    total: float

class Invoice(BaseModel):
    invoice_number: str
    date: str
    vendor: str
    items: list[InvoiceItem]
    subtotal: float
    tax: float
    total: float

# Extract and validate
invoice_data = extract_json(prompt)
invoice = Invoice(**invoice_data)

# Always verify totals!
verified_subtotal = sum(i.total for i in invoice.items)
print(f"Subtotal matches: {abs(verified_subtotal - invoice.subtotal) < 0.01}")

Invoice processing: extract line items with quantities and prices, then verify all the math independently.

The same nested extraction pattern works for emails. Here we extract action items, each with a task, deadline, priority, and assignee, all validated by Pydantic.

03-structured-outputs.ipynb
python
class ActionItem(BaseModel):
    task: str
    deadline: str
    priority: str
    assigned_to: str

class EmailAnalysis(BaseModel):
    subject: str
    sender: str
    action_items: list[ActionItem]

# Extract action items from a long email thread
email_data = extract_json(prompt)
analysis = EmailAnalysis(**email_data)

for item in analysis.action_items:
    print(f"[{item.priority}] {item.task} -> {item.assigned_to} by {item.deadline}")

Extract actionable tasks from emails with deadlines, priorities, and assignments, all validated by Pydantic.

AI prompt: Try it with AI

Loading practiceโ€ฆ

Ordering exercise: Structured output pipeline steps

Loading practiceโ€ฆ

Checkpoint: Structured outputs concepts

Loading practiceโ€ฆ

You can now extract complex nested data from any text. Next, we will review best practices and put everything together in a final structured outputs challenge.