Skip to Content

Structured Outputs

Get validated JSON from agent workflows using JSON Schema, Zod, or Pydantic. Get type-safe, structured data after multi-turn tool use.

Overview

Structured outputs let you define the exact shape of data you want back from an agent. The agent can use any tools it needs to complete the task, and you still get validated JSON matching your schema at the end.

Key benefits:

  • Type safety: Get strongly-typed objects instead of free-form text
  • Validation: Automatic schema validation with retry logic
  • Pydantic integration: Use familiar Python models
  • Claude SDK compatible: Same patterns as Claude Agent SDK

Quick Start

Basic JSON Schema

from isa_agent_sdk import query, ISAAgentOptions, OutputFormat # Define the shape of data you want schema = { "type": "object", "properties": { "company_name": {"type": "string"}, "founded_year": {"type": "number"}, "headquarters": {"type": "string"} }, "required": ["company_name"] } async for msg in query( prompt="Research Anthropic and provide key company information", options=ISAAgentOptions( output_format=OutputFormat.json_schema(schema) ) ): if msg.has_structured_output: print(msg.structured_output) # {'company_name': 'Anthropic', 'founded_year': 2021, 'headquarters': 'San Francisco'}

With Pydantic Models

For full type safety, use Pydantic models:

from pydantic import BaseModel from isa_agent_sdk import query, ISAAgentOptions, OutputFormat class Step(BaseModel): step_number: int description: str complexity: str # 'low', 'medium', 'high' class FeaturePlan(BaseModel): feature_name: str summary: str steps: list[Step] risks: list[str] async for msg in query( prompt="Plan how to add dark mode support to a React app", options=ISAAgentOptions( output_format=OutputFormat.from_pydantic(FeaturePlan) ) ): if msg.has_structured_output: # Parse into typed Pydantic model plan = msg.parse(FeaturePlan) print(f"Feature: {plan.feature_name}") print(f"Summary: {plan.summary}") for step in plan.steps: print(f" {step.step_number}. [{step.complexity}] {step.description}")

OutputFormat Configuration

Creating OutputFormat

from isa_agent_sdk import OutputFormat # Method 1: From Pydantic model (recommended) output_format = OutputFormat.from_pydantic(MyModel) # Method 2: From JSON schema output_format = OutputFormat.json_schema({ "type": "object", "properties": {...}, "required": [...] }) # Method 3: Simple JSON object mode (no schema validation) output_format = OutputFormat.json_object() # Method 4: Direct construction output_format = OutputFormat( type="json_schema", schema=my_schema, strict=True )

OutputFormat Options

OptionTypeDefaultDescription
typestr"text"Format type: "text", "json_object", or "json_schema"
schemadictNoneJSON Schema for validation (required for json_schema type)
strictboolTrueEnforce strict schema validation

Working with Results

Checking for Structured Output

async for msg in query(prompt, options=options): # Check if structured output is available if msg.has_structured_output: data = msg.structured_output # Dict # Check success/error status if msg.is_structured_output_success: print("Validation succeeded!") elif msg.is_structured_output_error: print("Validation failed after retries")

Parsing to Pydantic

# Safe parsing (returns None on failure) user = msg.parse_or_none(User) if user: print(f"Got user: {user.name}") # Strict parsing (raises on failure) try: user = msg.parse(User) except ValueError as e: print(f"No structured output: {e}") except ValidationError as e: print(f"Validation failed: {e}")

Result Subtypes

The subtype field indicates the result status:

SubtypeMeaning
successOutput was generated and validated successfully
error_max_turnsAgent hit maximum iteration limit
error_max_structured_output_retriesCouldn’t produce valid output after retries
error_tool_executionTool execution failed
error_modelModel inference error
interruptedExecution was interrupted (HIL)
from isa_agent_sdk import ResultSubtype if msg.subtype == ResultSubtype.SUCCESS.value: print("Success!") elif msg.subtype == ResultSubtype.ERROR_MAX_STRUCTURED_OUTPUT_RETRIES.value: print("Failed to produce valid structured output")

Example: TODO Tracker

This example demonstrates structured outputs with multi-step tool use:

from pydantic import BaseModel from isa_agent_sdk import query, ISAAgentOptions, OutputFormat class TodoItem(BaseModel): text: str file: str line: int author: str | None = None date: str | None = None class TodoReport(BaseModel): todos: list[TodoItem] total_count: int async for msg in query( prompt="Find all TODO comments in this codebase and identify who added them", options=ISAAgentOptions( allowed_tools=["grep_search", "bash_execute", "read_file"], output_format=OutputFormat.from_pydantic(TodoReport) ) ): if msg.is_tool_use: print(f"Using tool: {msg.tool_name}") if msg.has_structured_output: report = msg.parse(TodoReport) print(f"\nFound {report.total_count} TODOs:") for todo in report.todos: print(f" {todo.file}:{todo.line} - {todo.text}") if todo.author: print(f" Added by {todo.author} on {todo.date}")

Error Handling

async for msg in query(prompt, options=options): if msg.type == "result": if msg.subtype == "success" and msg.structured_output: # Use the validated output data = msg.structured_output elif msg.subtype == "error_max_structured_output_retries": # Handle the failure print("Could not produce valid output") print(f"Raw content: {msg.content}")

Tips for Avoiding Errors

  1. Keep schemas focused: Deeply nested schemas with many required fields are harder to satisfy
  2. Match schema to task: Make fields optional if the task might not have all information
  3. Use clear prompts: Ambiguous prompts make it harder to know what output to produce
  4. Start simple: Add complexity to your schema as needed

Low-Level Parsing Utilities

For advanced use cases, you can use the parsing utilities directly:

from isa_agent_sdk import ( StructuredOutputParser, StructuredOutputResult, parse_structured_output, parse_json_response ) # Parse JSON from text (handles markdown code blocks, etc.) result = parse_json_response(raw_text) if result.success: data = result.data # Parse with schema validation parser = StructuredOutputParser( schema=my_schema, pydantic_model=MyModel ) result = parser.parse(raw_text) # Direct parsing function result = parse_structured_output( content=raw_text, model=MyModel, schema=my_schema )

Comparison with isA_Model

The isA Agent SDK structured outputs build on isA_Model’s capabilities:

FeatureisA_ModelisA Agent SDK
response_format={"type": "json_object"}YesYes (via OutputFormat.json_object())
JSON Schema validationYes (prompt-based)Yes (native + parsing)
Pydantic integrationManualBuilt-in (OutputFormat.from_pydantic())
structured_output fieldNoYes
Automatic retryManualBuilt-in
Result subtypesNoYes