Using Instructor for Schema-Constrained Outputs Across Providers
Use Instructor to extract validated Pydantic objects from many LLM providers with one response_model workflow and automatic reask on validation failure.
Search across all documentation pages
Use Instructor to extract validated Pydantic objects from many LLM providers with one response_model workflow and automatic reask on validation failure.
Patch or create a provider client with Instructor, pass a Pydantic response_model, and let the library parse, validate, and retry until the schema is satisfied or max_retries is exhausted.
instructor and create a client with instructor.from_provider("provider/model") (or provider-specific helpers).client.create(response_model=YourModel, messages=[...], max_retries=N).import instructor
from pydantic import BaseModel, Field, field_validator
from enum import Enum
class Category(str, Enum):
billing = "billing"
technical = "technical"
other = "other"
class SupportIntent(BaseModel):
category: Category
urgency: int = Field(ge=1, le=5, description="1=low, 5=immediate")
summary: str = Field(min_length=10)
contact_email: str | None = None
@field_validator("summary")
@classmethod
def no_placeholders(cls, v: str) -> str:
if "TODO" in v.upper():
raise ValueError("summary must not contain TODO placeholders")
return v.strip()
# Same schema, different providers (verify model ids at build)
client = instructor.from_provider("openai/gpt-4o-mini")
# client = instructor.from_provider("anthropic/claude-3-5-sonnet-20240620")
# client = instructor.from_provider("google/gemini-2.5-flash")
# client = instructor.from_provider("ollama/llama3.2") # local
intent = client.create(
response_model=SupportIntent,
max_retries=3,
messages=[
{
"role": "user",
"content": (
"Classify this ticket: "
"I was double charged on invoice #4412 and need a refund today. "
"Email me at user@example.com."
),
}
],
)
print(intent.category, intent.urgency)
print(intent.model_dump())You get a SupportIntent instance or an error after retries.
You do not hand-parse JSON fences.
Instructor is a structured output client, not a full agent runtime.
Strengths:
create_partial, create_iterable - verify names at build)Use it for classification, document extraction, form fill, and "LLM as function" calls.
Use Pydantic AI when you need tools, multi-step loops, deps injection, and UI stream adapters.
Keep three layers separate:
from_provider / API keys / timeoutsresponse_model + retry policyThat separation lets you route cheap vs strong models without rewriting validators.
On validation failure Instructor typically:
max_retriesThis recovers many near-miss outputs (wrong enum casing, missing optional confusion, out-of-range ints). It does not fix systematically impossible schemas or missing source data.
For lists of records, prefer iterable/streaming modes when processing long documents so you can persist rows incrementally.
For progressive UI, partial object streaming can show fields as they fill; still commit only after final validation.
Attach completion hooks for logging kwargs, errors, and latency. Export retry counts as metrics: high retries mean schema smell or weak model choice.
You can point OpenAI-compatible clients at gateways (including OpenRouter) when Instructor supports that provider wiring. Keep model slugs and structured-output capabilities verified per route; not every upstream handles schema mode equally.
max_retries and alert when average retries rise.async_client=True (or async APIs) in async apps; do not block the event loop with sync create.| Approach | Pros | Cons |
|---|---|---|
| Instructor | Simple, multi-provider, retries | Not a full agent framework |
Pydantic AI output_type | Agent-native structure | Heavier for pure extract |
| Provider SDK structured outputs | First-party features | Provider lock-in |
| LangChain structured parsers | Ecosystem integrations | More abstraction layers |
| Manual JSON parse | Zero deps | High production failure rate |
Pick Instructor for single-call or few-call structured extraction across providers. Pick Pydantic AI when the workflow needs tools, multi-turn agent control, or AI SDK streaming adapters.
Yes for many local models via the Ollama integration. Keep schemas smaller and test retries; local models vary widely on structured fidelity.
Put models in a shared package. Use them as Instructor response_model now and Pydantic AI output_type later without translation layers.
Messages carry the instance data and task. Field descriptions and validators carry durable contract rules that should apply to every call.
Yes. Nested Pydantic models and lists are core use cases. Watch token size and consider chunking huge documents.
Chunk with overlap, extract per chunk into list models, then merge with deterministic code or a second structured pass.
from_provider required?It is the unified entry point in current docs. Older code may use from_openai style helpers; verify which API your installed version exposes.
Unit-test validators with raw dicts via Model.model_validate. Keep a small live suite for provider-schema compatibility.
Catch the error, route to a human queue or fallback model, and store the invalid raw completion for evals. Do not silently coerce.
Yes. Put category enums and extracted entities on one model when they share the same source text and trust boundary.
Stack versions: Pins from the category manifest (verify at build): OpenRouter (~315+ models, July 2026 pricing/fees); LangGraph 1.0+; CrewAI 1.14+; Microsoft Agent Framework 1.0; Vercel AI SDK 6; Pydantic AI (latest); LlamaIndex (latest); OpenAI Agents SDK (latest + MCP); MCP (Linux Foundation governance); A2A (HTTP+SSE+JSON-RPC 2.0); Solana
@solana/web3.js+@solana/spl-token.
Reviewed by Chris St. John·Last updated Jul 16, 2026