Even with detailed output formatting rules, models occasionally output trailing text, invalid JSON keys, or markdown code block wrapping. Output-Constrained Decoding solves this by forcing the model's token generation to conform to a schema at the inference engine level.
Instead of checking output validation after the fact, libraries like Instructor and Outlines build a context-free grammar from Pydantic schemas. At each token selection step, any token that would violate the schema is masked (set to 0 probability). It is mathematically impossible for the model to generate malformed outputs or invalid JSON keys.
from pydantic import BaseModel, Field import instructor from openai import OpenAI
class JobExtract(BaseModel): name: str = Field(description="Candidate name") salary_min: int = Field(description="Minimum salary mid-point") salary_max: int = Field(description="Maximum salary mid-point") stack: list[str] = Field(description="List of programming languages")
client = instructor.from_openai(OpenAI()) job = client.chat.completions.create( model="gpt-4o", response_model=JobExtract, messages=[{"role": "user", "content": email_text}] )
import { OpenAI } from "openai"; import { z } from "zod"; import { zodToJsonSchema } from "zod-to-json-schema";
const TicketSchema = z.object({ category: z.enum(["bug", "feature", "billing"]), urgency: z.enum(["low", "medium", "high"]), assignee: z.string().optional() });
const client = new OpenAI(); const completion = await client.chat.completions.create({ model: "gpt-4o-mini", messages: [{ role: "user", content: ticketText }], response_format: { type: "json_schema", json_schema: { name: "ticket_schema", schema: zodToJsonSchema(TicketSchema) } } });
Claude does not support native json_schema constraints at the engine level yet; use Instructor with tool/function calling parameters to enforce Pydantic structures.