Handling Complexity: Nested Tool Schemas

Handling Complexity: Nested Tool Schemas

Move beyond simple strings and integers. Learn how to design complex, nested Pydantic models that allow agents to handle multi-step data entry in a single tool call.

Complex Input Schemas

As your agents take over more professional workflows, the data they handle becomes more complex. Instead of just a city name, they might need to send a formatted InvoiceObject, a NetworkConfig, or a NestedFinancialReport.

Providing these to an agent requires more than just strings; it requires Recursive Pydantic Models. In this lesson, we will learn how to design schemas that models can populate safely without hallucinating.


1. Why Nested Schemas?

A "Flat" schema (just keys and values) is hard to maintain and hard for the model to visualize.

  • Flat (Bad): create_user(name, address_street, address_city, address_zip, phone_home, phone_work)
  • Nested (Good): create_user(name, address: AddressObj, contact: ContactObj)

Nested schemas group related data together, which aligns with the "Object-Oriented" way LLMs were trained.


2. Implementing Nested Tools in Pydantic

We can define a hierarchy of models. The LLM will navigate this hierarchy and output a valid JSON tree.

from pydantic import BaseModel, Field
from typing import List

class LineItem(BaseModel):
    name: str = Field(description="Name of the product")
    quantity: int = Field(gt=0)
    price: float

class InvoiceRequest(BaseModel):
    customer_id: str
    items: List[LineItem] = Field(description="A list of 1 or more line items")

3. The "Schema Overload" Problem

If a schema has too many levels of nesting (e.g., 5 levels deep), the model will likely make a mistake in the JSON syntax.

The Rule of Two: Try to limit your tool schemas to no more than 2 levels of nesting. If you need a 5-level deep structure, split it into two tool calls:

  1. prepare_base_report
  2. add_technical_details_to_report

4. Handling Dynamic Keys (The dict problem)

Avoid using dict or Any in your schemas.

  • Problem: If you define a parameter as additional_metadata: dict, the model doesn't know what to put inside. It will likely hallucinate keys or put PII in there.
  • Solution: Use Literal types or Enums to constrain the choices.
from typing import Literal

class ConfigTool(BaseModel):
    environment: Literal["dev", "staging", "prod"]

5. Structured Output vs. Tool Calling

Sometimes, you don't need a "Tool" to be executed. You just need the agent to structure its thoughts into a Pydantic object. LangChain provides .with_structured_output(). This is great for transforming a mess of unstructured text into a clean database row.


6. The "Missing Data" Pattern

In complex schemas, some fields might be optional. Crucial: Tell the model what to do if the data is missing.

Field Description: "If the user did not provide a phone number, set this field to None. Do NOT invent a fake phone number."


Summary and Mental Model

Think of a Complex Schema like a Multi-page Application Form.

  • If the form is just one long list of 50 boxes (Flat), it's intimidating and leads to errors.
  • If the form is divided into "Personal Info," "Billing," and "Shipping" (Nested), it's easier for the brain (and the LLM) to process.

Clean structures lead to clean logic.


Exercise: Schema Construction

  1. Coding: Build a Pydantic model for a Github_Project tool.
    • It must include a title (str).
    • It must include a labels (List of strings).
    • It must include an assignees (List of objects with username and role).
  2. Validation: Add a Field constraint ensuring that the title is at least 5 characters long.
  3. Logic: What happens if the agent tries to call a tool with a nested object but forgets one of the "Required" keys?
    • (Hint: Review the Repair Pattern in Module 5.2). Ready to make your tools context-aware? Let's move to Lesson 3.

Subscribe to our newsletter

Get the latest posts delivered right to your inbox.

Subscribe on LinkedIn