Structured Output for Teams

Get a validated Pydantic object from a team instead of raw text.

Set output_schema on a team to constrain its final response to a Pydantic model. The team leader synthesizes member outputs into a validated object.

Setup

Install the dependencies in your Python environment and set your OpenAI API key:

pip install agno openai yfinance
export OPENAI_API_KEY="your-api-key"

The later examples reuse the imports and members defined in Basic Usage.

Basic Usage

from pydantic import BaseModel, Field
from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from agno.team import Team
from agno.tools.hackernews import HackerNewsTools
from agno.tools.yfinance import YFinanceTools

class ResearchReport(BaseModel):
    title: str
    summary: str = Field(description="Executive summary of findings")
    key_insights: list[str] = Field(description="Top 3-5 insights")
    recommendation: str

news_agent = Agent(
    name="News Researcher",
    role="Research tech news and trends",
    tools=[HackerNewsTools()]
)

finance_agent = Agent(
    name="Finance Analyst",
    role="Analyze financial data and stocks",
    tools=[YFinanceTools()]
)

team = Team(
    name="Research Team",
    model=OpenAIResponses(id="gpt-5.2"),
    members=[news_agent, finance_agent],
    output_schema=ResearchReport,
)

response = team.run("Research NVIDIA - analyze stock performance and recent news")

# Check that a structured result was returned before accessing fields
if not isinstance(response.content, ResearchReport):
    raise ValueError(f"Expected ResearchReport, got: {response.content!r}")
report: ResearchReport = response.content
print(report.title)
print(report.summary)
print(report.recommendation)

How It Works

In the default coordinate mode used above, a team with output_schema can:

  1. The team leader delegates tasks to members
  2. Members execute and return their results
  3. The leader synthesizes all member outputs
  4. The final response is structured according to your schema

In coordinate mode, the team's schema applies to its final output. Individual members use their own output_schema, if configured. In route mode (respond_directly=True), Agno passes the team's output schema to a selected member that has no schema, since that member supplies the final response directly.

Fallback parsing failures are logged and can leave response.content as a string. Check its type before accessing schema fields, including when you force JSON mode.

Control output_schema Per-Run

Override or set the schema at run time:

class MarketReport(BaseModel):
    summary: str
    trends: list[str]

class StockComparison(BaseModel):
    symbols: list[str]
    comparison: str

team = Team(
    model=OpenAIResponses(id="gpt-5.2"),
    members=[news_agent, finance_agent],
)

# Different schemas for different requests
report = team.run("Analyze AI market", output_schema=MarketReport)
comparison = team.run("Compare NVDA vs AMD", output_schema=StockComparison)

Control output_schema Per Member/Team

You can set output_schema on both individual members and the team:

from pydantic import BaseModel, Field
from agno.agent import Agent
from agno.models.openai import OpenAIResponses
from agno.team import Team
from agno.tools.hackernews import HackerNewsTools
from agno.tools.yfinance import YFinanceTools

# Member schemas
class NewsInsights(BaseModel):
    headlines: list[str]
    sentiment: str = Field(description="positive, negative, or neutral")

class FinanceInsights(BaseModel):
    price: float
    change_percent: float
    recommendation: str

# Team schema
class CombinedReport(BaseModel):
    summary: str
    market_sentiment: str
    stock_outlook: str
    final_recommendation: str

news_agent = Agent(
    name="News Analyst",
    role="Research news",
    tools=[HackerNewsTools()],
    output_schema=NewsInsights,
)

finance_agent = Agent(
    name="Finance Analyst",
    role="Analyze stocks",
    tools=[YFinanceTools()],
    output_schema=FinanceInsights,
)

team = Team(
    model=OpenAIResponses(id="gpt-5.2"),
    members=[news_agent, finance_agent],
    output_schema=CombinedReport,
)

response = team.run("Full analysis of NVDA")
report: CombinedReport = response.content

Member schemas ensure consistent intermediate outputs. The team schema controls the final synthesized response.

Schema Design Tips

Aggregate Multiple Perspectives

Design schemas that capture synthesized insights:

class CompetitiveAnalysis(BaseModel):
    company: str
    market_position: str = Field(description="Leader, challenger, or follower")
    technical_strengths: list[str] = Field(description="From technical research")
    financial_strengths: list[str] = Field(description="From financial analysis")
    combined_outlook: str

Include Confidence and Reasoning

class InvestmentRecommendation(BaseModel):
    ticker: str
    action: str = Field(description="buy, hold, or sell")
    price_target: float | None = None
    reasoning: str = Field(description="Synthesized reasoning from all analysts")
    risk_factors: list[str]
    confidence: float = Field(ge=0, le=1)

Structured Comparisons

class CompanyComparison(BaseModel):
    companies: list[str]
    winner: str
    comparison_criteria: list[str]
    scores: dict[str, dict[str, int]]  # company -> criterion -> score
    summary: str

JSON Mode

Agno uses a JSON fallback automatically when the model lacks native schema support. Set use_json_mode=True to force JSON mode for a model that supports native structured output:

team = Team(
    model=OpenAIResponses(id="gpt-5.2"),
    members=[news_agent, finance_agent],
    output_schema=ResearchReport,
    use_json_mode=True,
)

JSON mode instructs the model to respond in JSON but doesn't guarantee schema compliance. Prefer models with native structured output support.