Files
wehub-resource-sync 97e91a83f3
Ruff / Ruff (push) Has been cancelled
Test / Core Tests (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.10) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.11) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.12) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.13) (push) Has been cancelled
Test / Offline Coverage Tests (Python 3.9) (push) Has been cancelled
Test / Full Coverage (Python 3.11) (push) Has been cancelled
Test / Core Provider Tests (OpenAI) (push) Has been cancelled
Test / Core Provider Tests (Anthropic) (push) Has been cancelled
Test / Core Provider Tests (Google) (push) Has been cancelled
Test / Core Provider Tests (Other) (push) Has been cancelled
Test / Anthropic Tests (push) Has been cancelled
Test / Gemini Tests (push) Has been cancelled
Test / Google GenAI Tests (push) Has been cancelled
Test / Vertex AI Tests (push) Has been cancelled
Test / OpenAI Tests (push) Has been cancelled
Test / Writer Tests (push) Has been cancelled
Test / Auto Client Tests (push) Has been cancelled
ty / type-check (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:36:38 +08:00

237 lines
6.3 KiB
Markdown

---
description: Discover why Instructor is the simplest, most reliable way to get structured outputs from LLMs.
---
# Why use Instructor?
You've built something with an LLM, but 15% of the time it returns garbage. Parsing JSON is a nightmare. Different providers have different APIs. There has to be a better way.
## The pain of unstructured outputs
Let's be honest about what working with LLMs is really like:
```python
# What you want:
user_info = extract_user("John is 25 years old")
print(user_info.name) # "John"
print(user_info.age) # 25
# What you actually get:
response = llm.complete("Extract: John is 25 years old")
# "I'd be happy to help! Based on the text, the user's name is John
# and their age is 25. Is there anything else you'd like me to extract?"
# Now you need to:
# 1. Parse this text somehow
# 2. Handle when it returns JSON with syntax errors
# 3. Validate the data matches what you expect
# 4. Retry when it fails (which it will)
# 5. Do this differently for each LLM provider
```
## The Instructor difference
Here's the same task with Instructor:
```python
import instructor
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
client = instructor.from_provider("openai/gpt-5.4-mini")
user = client.create(
response_model=User,
messages=[{"role": "user", "content": "John is 25 years old"}],
)
print(user.name) # "John"
print(user.age) # 25
```
**That's it.** No parsing. No retries. No provider-specific code.
## Real problems Instructor solves
### 1. "It works 90% of the time"
Without Instructor, your LLM returns perfect JSON most of the time. But that 10% will ruin your weekend.
```python
# Without Instructor: Brittle code that breaks randomly
try:
data = json.loads(llm_response)
user = User(**data) # KeyError: 'name'
except:
# Now what? Retry? Parse the text? Give up?
pass
# With Instructor: Automatic retries with validation errors
user = client.create(
response_model=User,
messages=[{"role": "user", "content": "..."}],
max_retries=3, # Retries with validation errors
)
# Always returns valid User object or raises clear exception
```
### 2. "Each provider is different"
Every LLM provider has its own API. Your code becomes a mess of conditionals.
```python
# Without Instructor: Provider-specific spaghetti
if provider == "openai":
response = openai.chat.completions.create(
tools=[{"type": "function", "function": {...}}]
)
data = json.loads(response.choices[0].message.tool_calls[0].function.arguments)
elif provider == "anthropic":
response = anthropic.messages.create(
tools=[{"name": "extract", "input_schema": {...}}]
)
data = response.content[0].input
elif provider == "google":
# ... different API again
# With Instructor: One API for all providers
client = instructor.from_provider("openai/gpt-5.4-mini")
# or
client = instructor.from_provider("anthropic/claude-3")
# or
client = instructor.from_provider("google/gemini-pro")
# Same code for all providers
user = client.create(
response_model=User,
messages=[{"role": "user", "content": "..."}],
)
```
### 3. "Complex data structures are impossible"
Nested objects, lists, enums - LLMs struggle with complex schemas.
```python
# Without Instructor: Good luck with this
schema = {
"type": "object",
"properties": {
"users": {
"type": "array",
"items": {
"type": "object",
"properties": {
"name": {"type": "string"},
"addresses": {
"type": "array",
"items": {
"type": "object",
"properties": {
"street": {"type": "string"},
"city": {"type": "string"}
}
}
}
}
}
}
}
}
# With Instructor: Just use Python
from typing import List
class Address(BaseModel):
street: str
city: str
class User(BaseModel):
name: str
addresses: List[Address]
class UserList(BaseModel):
users: List[User]
# Works perfectly
result = client.create(
response_model=UserList,
messages=[{"role": "user", "content": "..."}],
)
```
## The cost of not using Instructor
Let's talk real numbers:
**Time wasted:**
- 2-3 hours implementing JSON parsing and validation
- 4-6 hours debugging edge cases
- 2-3 hours for each new provider you add
- Ongoing maintenance as APIs change
**Bugs in production:**
- Malformed JSON crashes your app
- Missing fields cause silent failures
- Type mismatches corrupt your database
- Customer complaints about reliability
**Developer frustration:**
- "It worked in testing!"
- "Why is the JSON different this time?"
- "How do I handle when it returns a string instead of a number?"
## What developers are saying
Based on our GitHub issues and Discord:
- **"Reduced our LLM code by 80%"** - Common feedback
- **"Finally, LLM outputs I can trust"** - From production users
- **"The retries alone are worth it"** - Saves hours of edge-case handling
- **"Works exactly the same with every provider"** - No more provider lock-in
## Start now, thank yourself later
Every day without Instructor is another day of:
- Debugging malformed JSON
- Writing provider-specific code
- Handling validation manually
- Explaining to your PM why the LLM integration is flaky
Install Instructor:
```bash
pip install instructor
```
Try it in 30 seconds:
```python
import instructor
from pydantic import BaseModel
client = instructor.from_provider("openai/gpt-5.4-mini")
class User(BaseModel):
name: str
age: int
user = client.create(
response_model=User,
messages=[{"role": "user", "content": "John is 25 years old"}],
)
print(user) # User(name='John', age=25)
```
## When NOT to use Instructor
Let's be clear - you might not need Instructor if:
- You only need raw text responses (chatbots, creative writing)
- You're building a one-off script with no error handling
- You enjoy debugging JSON parsing errors at 3am
For everyone else building production LLM applications, Instructor is the obvious choice.
[Get Started →](index.md#quick-start-extract-structured-data-in-3-lines){ .md-button .md-button--primary }