Structured Output with Agents
Master Kern's template-based output system for reliable JSON from small language models.
Structured outputs are essential when agents need to feed downstream APIs, populate databases, or return clean JSON. While general frameworks send raw JSON Schemas to the model (which confuses models under 7B parameters), Kern uses a template-based structured output engine and automatic JSON repair.
Why templates work better for small models
Models under 7 billion parameters struggle with abstract JSON Schema definitions (e.g. {"type": "object", "properties": {...}}). They often get confused by nesting, miss required fields, and output malformed JSON.
Kern solves this by converting your Pydantic model into a fill-in-the-blank template:
- Shows the pattern: The model sees exactly what the final text structure should look like.
- Close-ended completion: The model just fills in the values, mirroring how text completions naturally work.
- No extra commentary: The template restricts the model's generation scope, keeping it on track.
Basic Usage
To request structured output, define a Pydantic model and pass it as the output_schema to your agent:
1from pydantic import BaseModel, Field2from typing import Literal3from kern import Agent4from kern.models.openai import OpenAIChat56class MovieAnalysis(BaseModel):7 title: str = Field(description="The title of the movie")8 release_year: int = Field(description="Year the movie was released")9 sentiment: Literal["positive", "negative", "neutral"] = Field(description="Review sentiment")10 summary: str = Field(description="A 2-sentence summary of the review")1112agent = Agent(13 model=OpenAIChat(14 id="local-model",15 base_url="http://localhost:8080/v1",16 ),17 description="You are a movie review analyst.",18 output_schema=MovieAnalysis,19)2021result = agent.run("The Matrix (1999) is an absolute masterpiece of science fiction!")2223# result.content is a validated MovieAnalysis Pydantic object24print(result.content.title) # "The Matrix"25print(result.content.release_year) # 199926print(result.content.sentiment) # "positive"Automatic JSON Repair
Small language models frequently output slightly broken JSON (e.g. omitting a closing bracket, adding a trailing comma, or wrapping the JSON in markdown code blocks or conversational commentary).
Kern intercepts the raw model output and passes it through an automatic JSON repair engine. The engine:
- Removes pre/post conversational text.
- Standardizes quote characters.
- Completes unclosed brackets or braces.
- Handles trailing commas.
- Strips invalid markdown fences.
You can also use this repair capability directly on raw strings in your code:
1from kern.repair import extract_json23raw_output = """4Sure! Here is the JSON data:5{6 "title": "Inception",7 "director": "Christopher Nolan"8""" # Note the unclosed brace910data = extract_json(raw_output)11print(data)12# {'title': 'Inception', 'director': 'Christopher Nolan'}13# Auto-completed the closing brace!Best Practices for Structured Outputs on Small Models
- Provide Clear Descriptions: Use Pydantic's
Field(description="...")to give explicit instructions for each field. - Use Literal Types: Use
Literaltypes (e.g.Literal["low", "medium", "high"]) instead of free-form strings. Small models follow strict lists much better. - Avoid Deep Nesting: Keep your Pydantic schemas relatively flat. Deeply nested objects require multi-step reasoning that small models (1-7B) struggle with.
- Lower the Temperature: Set
temperature=0.1or0.0for structured extraction. Lower values make outputs more deterministic and structured.