Relying on prompts to output valid JSON is a fragile anti-pattern in production. Constrained Decoding forces the model's token-selection logic to align with a formal grammar (like a JSON Schema or Pydantic model). Using frameworks like Instructor, the model is constrained at the engine level to ensure output conforms 100% to your schema, mathematically eliminating malformed JSON.
from pydantic import BaseModel, Field import instructor import openai
class Candidate(BaseModel): name: str = Field(description="Full name") skills: list[str] = Field(description="Key technical skills") years_experience: int = Field(gt=0, description="Must be positive")
client = instructor.from_openai(openai.OpenAI()) candidate = client.chat.completions.create( model="gpt-4o-mini", response_model=Candidate, messages=[{"role": "user", "content": "Marcus Webb has 6 years Go experience"}] )
from pydantic import BaseModel, Field, field_validator import instructor from openai import OpenAI
class LogEntry(BaseModel): timestamp: str = Field(description="ISO 8601 timestamp") severity: str = Field(description="INFO, WARNING, or ERROR") component: str = Field(description="Service component name") message: str = Field(description="Sanitised message")
@field_validator('severity')
def validate_severity(cls, v):
if v not in ['INFO', 'WARNING', 'ERROR']:
raise ValueError("Severity must be INFO, WARNING, or ERROR")
return v
client = instructor.from_openai(OpenAI()) log_data = client.chat.completions.create( model="gpt-4o-mini", response_model=LogEntry, messages=[{"role": "user", "content": "[2026-07-06T15:04:12Z] [ERROR] payment-api: Connection pool saturated"}] )
Claude supports JSON Mode and Tool Calling, which Instructor uses to enforce schemas. Ensure descriptions on Pydantic fields are detailed since Claude reads them as tool definitions.