Structured Output for Teams

Get validated Pydantic object from 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.

Basic Usage

1from pydantic import BaseModel, Field
2from kern.agent import Agent
3from kern.models.openai import OpenAIResponses
4from kern.team import Team
5from kern.tools.hackernews import HackerNewsTools
6from kern.tools.yfinance import YFinanceTools
7
8class ResearchReport(BaseModel):
9 title: str
10 summary: str = Field(description="Executive summary of findings")
11 key_insights: list[str] = Field(description="Top 3-5 insights")
12 recommendation: str
13
14news_agent = Agent(
15 name="News Researcher",
16 role="Research tech news and trends",
17 tools=[HackerNewsTools()]
18)
19
20finance_agent = Agent(
21 name="Finance Analyst",
22 role="Analyze financial data and stocks",
23 tools=[YFinanceTools()]
24)
25
26team = Team(
27 name="Research Team",
28 model=OpenAIResponses(id="gpt-5.2"),
29 members=[news_agent, finance_agent],
30 output_schema=ResearchReport,
31)
32
33response = team.run("Research NVIDIA - analyze stock performance and recent news")
34
35# response.content is a validated ResearchReport object
36report: ResearchReport = response.content
37print(report.title)
38print(report.summary)
39print(report.recommendation)

How It Works

When a team has output_schema set:

  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

Only the team's final output is structured. Individual member responses remain unstructured unless those members have their own output_schema.

Control output_schema Per-Run

Override or set the schema at run time:

1team = Team(
2 model=OpenAIResponses(id="gpt-5.2"),
3 members=[news_agent, finance_agent],
4)
5
6# Different schemas for different requests
7report = team.run("Analyze AI market", output_schema=MarketReport)
8comparison = 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:

1from pydantic import BaseModel, Field
2from kern.agent import Agent
3from kern.models.openai import OpenAIResponses
4from kern.team import Team
5from kern.tools.hackernews import HackerNewsTools
6from kern.tools.yfinance import YFinanceTools
7
8# Member schemas
9class NewsInsights(BaseModel):
10 headlines: list[str]
11 sentiment: str = Field(description="positive, negative, or neutral")
12
13class FinanceInsights(BaseModel):
14 price: float
15 change_percent: float
16 recommendation: str
17
18# Team schema
19class CombinedReport(BaseModel):
20 summary: str
21 market_sentiment: str
22 stock_outlook: str
23 final_recommendation: str
24
25news_agent = Agent(
26 name="News Analyst",
27 role="Research news",
28 tools=[HackerNewsTools()],
29 output_schema=NewsInsights,
30)
31
32finance_agent = Agent(
33 name="Finance Analyst",
34 role="Analyze stocks",
35 tools=[YFinanceTools()],
36 output_schema=FinanceInsights,
37)
38
39team = Team(
40 model=OpenAIResponses(id="gpt-5.2"),
41 members=[news_agent, finance_agent],
42 output_schema=CombinedReport,
43)
44
45response = team.run("Full analysis of NVDA")
46report: 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:

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

Include Confidence and Reasoning

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

Structured Comparisons

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

Fallback with use_json_mode

Enable JSON mode for models that don't support structured output natively:

1team = Team(
2 model=SomeModel(),
3 members=[...],
4 output_schema=MySchema,
5 use_json_mode=True,
6)
Note

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

Related