SKILL.md
SKILL.mdBrowse 4 files
4,239 tokens
16,704 bytes
Token encoding: o200k_base
Snapshot 24fd22b
1---2name: instructor3description: Structured LLM outputs validated with Pydantic.4version: 1.0.05author: Orchestra Research6license: MIT7dependencies: [instructor, pydantic, openai, anthropic]8platforms: [linux, macos, windows]9metadata:10 hermes:11 tags: [Prompt Engineering, Instructor, Structured Output, Pydantic, Data Extraction, JSON Parsing, Type Safety, Validation, Streaming, OpenAI, Anthropic]12 13---14 15# Instructor: Structured LLM Outputs16 17## When to Use This Skill18 19Use Instructor when you need to:20- **Extract structured data** from LLM responses reliably21- **Validate outputs** against Pydantic schemas automatically22- **Retry failed extractions** with automatic error handling23- **Parse complex JSON** with type safety and validation24- **Stream partial results** for real-time processing25- **Support multiple LLM providers** with consistent API26 27**GitHub Stars**: 15,000+ | **Battle-tested**: 100,000+ developers28 29## Installation30 31```bash32# Base installation33pip install instructor34 35# With specific providers36pip install "instructor[anthropic]" # Anthropic Claude37pip install "instructor[openai]" # OpenAI38pip install "instructor[all]" # All providers39```40 41## Quick Start42 43### Basic Example: Extract User Data44 45```python46import instructor47from pydantic import BaseModel48from anthropic import Anthropic49 50# Define output structure51class User(BaseModel):52 name: str53 age: int54 email: str55 56# Create instructor client57client = instructor.from_anthropic(Anthropic())58 59# Extract structured data60user = client.messages.create(61 model="claude-sonnet-4-5-20250929",62 max_tokens=1024,63 messages=[{64 "role": "user",65 "content": "John Doe is 30 years old. His email is john@example.com"66 }],67 response_model=User68)69 70print(user.name) # "John Doe"71print(user.age) # 3072print(user.email) # "john@example.com"73```74 75### With OpenAI76 77```python78from openai import OpenAI79 80client = instructor.from_openai(OpenAI())81 82user = client.chat.completions.create(83 model="gpt-4o-mini",84 response_model=User,85 messages=[{"role": "user", "content": "Extract: Alice, 25, alice@email.com"}]86)87```88 89## Core Concepts90 91### 1. Response Models (Pydantic)92 93Response models define the structure and validation rules for LLM outputs.94 95#### Basic Model96 97```python98from pydantic import BaseModel, Field99 100class Article(BaseModel):101 title: str = Field(description="Article title")102 author: str = Field(description="Author name")103 word_count: int = Field(description="Number of words", gt=0)104 tags: list[str] = Field(description="List of relevant tags")105 106article = client.messages.create(107 model="claude-sonnet-4-5-20250929",108 max_tokens=1024,109 messages=[{110 "role": "user",111 "content": "Analyze this article: [article text]"112 }],113 response_model=Article114)115```116 117**Benefits:**118- Type safety with Python type hints119- Automatic validation (word_count > 0)120- Self-documenting with Field descriptions121- IDE autocomplete support122 123#### Nested Models124 125```python126class Address(BaseModel):127 street: str128 city: str129 country: str130 131class Person(BaseModel):132 name: str133 age: int134 address: Address # Nested model135 136person = client.messages.create(137 model="claude-sonnet-4-5-20250929",138 max_tokens=1024,139 messages=[{140 "role": "user",141 "content": "John lives at 123 Main St, Boston, USA"142 }],143 response_model=Person144)145 146print(person.address.city) # "Boston"147```148 149#### Optional Fields150 151```python152from typing import Optional153 154class Product(BaseModel):155 name: str156 price: float157 discount: Optional[float] = None # Optional158 description: str = Field(default="No description") # Default value159 160# LLM doesn't need to provide discount or description161```162 163#### Enums for Constraints164 165```python166from enum import Enum167 168class Sentiment(str, Enum):169 POSITIVE = "positive"170 NEGATIVE = "negative"171 NEUTRAL = "neutral"172 173class Review(BaseModel):174 text: str175 sentiment: Sentiment # Only these 3 values allowed176 177review = client.messages.create(178 model="claude-sonnet-4-5-20250929",179 max_tokens=1024,180 messages=[{181 "role": "user",182 "content": "This product is amazing!"183 }],184 response_model=Review185)186 187print(review.sentiment) # Sentiment.POSITIVE188```189 190### 2. Validation191 192Pydantic validates LLM outputs automatically. If validation fails, Instructor retries.193 194#### Built-in Validators195 196```python197from pydantic import Field, EmailStr, HttpUrl198 199class Contact(BaseModel):200 name: str = Field(min_length=2, max_length=100)201 age: int = Field(ge=0, le=120) # 0 <= age <= 120202 email: EmailStr # Validates email format203 website: HttpUrl # Validates URL format204 205# If LLM provides invalid data, Instructor retries automatically206```207 208#### Custom Validators209 210```python211from pydantic import field_validator212 213class Event(BaseModel):214 name: str215 date: str216 attendees: int217 218 @field_validator('date')219 def validate_date(cls, v):220 """Ensure date is in YYYY-MM-DD format."""221 import re222 if not re.match(r'\d{4}-\d{2}-\d{2}', v):223 raise ValueError('Date must be YYYY-MM-DD format')224 return v225 226 @field_validator('attendees')227 def validate_attendees(cls, v):228 """Ensure positive attendees."""229 if v < 1:230 raise ValueError('Must have at least 1 attendee')231 return v232```233 234#### Model-Level Validation235 236```python237from pydantic import model_validator238 239class DateRange(BaseModel):240 start_date: str241 end_date: str242 243 @model_validator(mode='after')244 def check_dates(self):245 """Ensure end_date is after start_date."""246 from datetime import datetime247 start = datetime.strptime(self.start_date, '%Y-%m-%d')248 end = datetime.strptime(self.end_date, '%Y-%m-%d')249 250 if end < start:251 raise ValueError('end_date must be after start_date')252 return self253```254 255### 3. Automatic Retrying256 257Instructor retries automatically when validation fails, providing error feedback to the LLM.258 259```python260# Retries up to 3 times if validation fails261user = client.messages.create(262 model="claude-sonnet-4-5-20250929",263 max_tokens=1024,264 messages=[{265 "role": "user",266 "content": "Extract user from: John, age unknown"267 }],268 response_model=User,269 max_retries=3 # Default is 3270)271 272# If age can't be extracted, Instructor tells the LLM:273# "Validation error: age - field required"274# LLM tries again with better extraction275```276 277**How it works:**2781. LLM generates output2792. Pydantic validates2803. If invalid: Error message sent back to LLM2814. LLM tries again with error feedback2825. Repeats up to max_retries283 284### 4. Streaming285 286Stream partial results for real-time processing.287 288#### Streaming Partial Objects289 290```python291from instructor import Partial292 293class Story(BaseModel):294 title: str295 content: str296 tags: list[str]297 298# Stream partial updates as LLM generates299for partial_story in client.messages.create_partial(300 model="claude-sonnet-4-5-20250929",301 max_tokens=1024,302 messages=[{303 "role": "user",304 "content": "Write a short sci-fi story"305 }],306 response_model=Story307):308 print(f"Title: {partial_story.title}")309 print(f"Content so far: {partial_story.content[:100]}...")310 # Update UI in real-time311```312 313#### Streaming Iterables314 315```python316class Task(BaseModel):317 title: str318 priority: str319 320# Stream list items as they're generated321tasks = client.messages.create_iterable(322 model="claude-sonnet-4-5-20250929",323 max_tokens=1024,324 messages=[{325 "role": "user",326 "content": "Generate 10 project tasks"327 }],328 response_model=Task329)330 331for task in tasks:332 print(f"- {task.title} ({task.priority})")333 # Process each task as it arrives334```335 336## Provider Configuration337 338### Anthropic Claude339 340```python341import instructor342from anthropic import Anthropic343 344client = instructor.from_anthropic(345 Anthropic(api_key="your-api-key")346)347 348# Use with Claude models349response = client.messages.create(350 model="claude-sonnet-4-5-20250929",351 max_tokens=1024,352 messages=[...],353 response_model=YourModel354)355```356 357### OpenAI358 359```python360from openai import OpenAI361 362client = instructor.from_openai(363 OpenAI(api_key="your-api-key")364)365 366response = client.chat.completions.create(367 model="gpt-4o-mini",368 response_model=YourModel,369 messages=[...]370)371```372 373### Local Models (Ollama)374 375```python376from openai import OpenAI377 378# Point to local Ollama server379client = instructor.from_openai(380 OpenAI(381 base_url="http://localhost:11434/v1",382 api_key="ollama" # Required but ignored383 ),384 mode=instructor.Mode.JSON385)386 387response = client.chat.completions.create(388 model="llama3.1",389 response_model=YourModel,390 messages=[...]391)392```393 394## Common Patterns395 396### Pattern 1: Data Extraction from Text397 398```python399class CompanyInfo(BaseModel):400 name: str401 founded_year: int402 industry: str403 employees: int404 headquarters: str405 406text = """407Tesla, Inc. was founded in 2003. It operates in the automotive and energy408industry with approximately 140,000 employees. The company is headquartered409in Austin, Texas.410"""411 412company = client.messages.create(413 model="claude-sonnet-4-5-20250929",414 max_tokens=1024,415 messages=[{416 "role": "user",417 "content": f"Extract company information from: {text}"418 }],419 response_model=CompanyInfo420)421```422 423### Pattern 2: Classification424 425```python426class Category(str, Enum):427 TECHNOLOGY = "technology"428 FINANCE = "finance"429 HEALTHCARE = "healthcare"430 EDUCATION = "education"431 OTHER = "other"432 433class ArticleClassification(BaseModel):434 category: Category435 confidence: float = Field(ge=0.0, le=1.0)436 keywords: list[str]437 438classification = client.messages.create(439 model="claude-sonnet-4-5-20250929",440 max_tokens=1024,441 messages=[{442 "role": "user",443 "content": "Classify this article: [article text]"444 }],445 response_model=ArticleClassification446)447```448 449### Pattern 3: Multi-Entity Extraction450 451```python452class Person(BaseModel):453 name: str454 role: str455 456class Organization(BaseModel):457 name: str458 industry: str459 460class Entities(BaseModel):461 people: list[Person]462 organizations: list[Organization]463 locations: list[str]464 465text = "Tim Cook, CEO of Apple, announced at the event in Cupertino..."466 467entities = client.messages.create(468 model="claude-sonnet-4-5-20250929",469 max_tokens=1024,470 messages=[{471 "role": "user",472 "content": f"Extract all entities from: {text}"473 }],474 response_model=Entities475)476 477for person in entities.people:478 print(f"{person.name} - {person.role}")479```480 481### Pattern 4: Structured Analysis482 483```python484class SentimentAnalysis(BaseModel):485 overall_sentiment: Sentiment486 positive_aspects: list[str]487 negative_aspects: list[str]488 suggestions: list[str]489 score: float = Field(ge=-1.0, le=1.0)490 491review = "The product works well but setup was confusing..."492 493analysis = client.messages.create(494 model="claude-sonnet-4-5-20250929",495 max_tokens=1024,496 messages=[{497 "role": "user",498 "content": f"Analyze this review: {review}"499 }],500 response_model=SentimentAnalysis501)502```503 504### Pattern 5: Batch Processing505 506```python507def extract_person(text: str) -> Person:508 return client.messages.create(509 model="claude-sonnet-4-5-20250929",510 max_tokens=1024,511 messages=[{512 "role": "user",513 "content": f"Extract person from: {text}"514 }],515 response_model=Person516 )517 518texts = [519 "John Doe is a 30-year-old engineer",520 "Jane Smith, 25, works in marketing",521 "Bob Johnson, age 40, software developer"522]523 524people = [extract_person(text) for text in texts]525```526 527## Advanced Features528 529### Union Types530 531```python532from typing import Union533 534class TextContent(BaseModel):535 type: str = "text"536 content: str537 538class ImageContent(BaseModel):539 type: str = "image"540 url: HttpUrl541 caption: str542 543class Post(BaseModel):544 title: str545 content: Union[TextContent, ImageContent] # Either type546 547# LLM chooses appropriate type based on content548```549 550### Dynamic Models551 552```python553from pydantic import create_model554 555# Create model at runtime556DynamicUser = create_model(557 'User',558 name=(str, ...),559 age=(int, Field(ge=0)),560 email=(EmailStr, ...)561)562 563user = client.messages.create(564 model="claude-sonnet-4-5-20250929",565 max_tokens=1024,566 messages=[...],567 response_model=DynamicUser568)569```570 571### Custom Modes572 573```python574# For providers without native structured outputs575client = instructor.from_anthropic(576 Anthropic(),577 mode=instructor.Mode.JSON # JSON mode578)579 580# Available modes:581# - Mode.ANTHROPIC_TOOLS (recommended for Claude)582# - Mode.JSON (fallback)583# - Mode.TOOLS (OpenAI tools)584```585 586### Context Management587 588```python589# Single-use client590with instructor.from_anthropic(Anthropic()) as client:591 result = client.messages.create(592 model="claude-sonnet-4-5-20250929",593 max_tokens=1024,594 messages=[...],595 response_model=YourModel596 )597 # Client closed automatically598```599 600## Error Handling601 602### Handling Validation Errors603 604```python605from pydantic import ValidationError606 607try:608 user = client.messages.create(609 model="claude-sonnet-4-5-20250929",610 max_tokens=1024,611 messages=[...],612 response_model=User,613 max_retries=3614 )615except ValidationError as e:616 print(f"Failed after retries: {e}")617 # Handle gracefully618 619except Exception as e:620 print(f"API error: {e}")621```622 623### Custom Error Messages624 625```python626class ValidatedUser(BaseModel):627 name: str = Field(description="Full name, 2-100 characters")628 age: int = Field(description="Age between 0 and 120", ge=0, le=120)629 email: EmailStr = Field(description="Valid email address")630 631 class Config:632 # Custom error messages633 json_schema_extra = {634 "examples": [635 {636 "name": "John Doe",637 "age": 30,638 "email": "john@example.com"639 }640 ]641 }642```643 644## Best Practices645 646### 1. Clear Field Descriptions647 648```python649# ❌ Bad: Vague650class Product(BaseModel):651 name: str652 price: float653 654# ✅ Good: Descriptive655class Product(BaseModel):656 name: str = Field(description="Product name from the text")657 price: float = Field(description="Price in USD, without currency symbol")658```659 660### 2. Use Appropriate Validation661 662```python663# ✅ Good: Constrain values664class Rating(BaseModel):665 score: int = Field(ge=1, le=5, description="Rating from 1 to 5 stars")666 review: str = Field(min_length=10, description="Review text, at least 10 chars")667```668 669### 3. Provide Examples in Prompts670 671```python672messages = [{673 "role": "user",674 "content": """Extract person info from: "John, 30, engineer"675 676Example format:677{678 "name": "John Doe",679 "age": 30,680 "occupation": "engineer"681}"""682}]683```684 685### 4. Use Enums for Fixed Categories686 687```python688# ✅ Good: Enum ensures valid values689class Status(str, Enum):690 PENDING = "pending"691 APPROVED = "approved"692 REJECTED = "rejected"693 694class Application(BaseModel):695 status: Status # LLM must choose from enum696```697 698### 5. Handle Missing Data Gracefully699 700```python701class PartialData(BaseModel):702 required_field: str703 optional_field: Optional[str] = None704 default_field: str = "default_value"705 706# LLM only needs to provide required_field707```708 709## Comparison to Alternatives710 711| Feature | Instructor | Manual JSON | LangChain | DSPy |712|---------|------------|-------------|-----------|------|713| Type Safety | ✅ Yes | ❌ No | ⚠️ Partial | ✅ Yes |714| Auto Validation | ✅ Yes | ❌ No | ❌ No | ⚠️ Limited |715| Auto Retry | ✅ Yes | ❌ No | ❌ No | ✅ Yes |716| Streaming | ✅ Yes | ❌ No | ✅ Yes | ❌ No |717| Multi-Provider | ✅ Yes | ⚠️ Manual | ✅ Yes | ✅ Yes |718| Learning Curve | Low | Low | Medium | High |719 720**When to choose Instructor:**721- Need structured, validated outputs722- Want type safety and IDE support723- Require automatic retries724- Building data extraction systems725 726**When to choose alternatives:**727- DSPy: Need prompt optimization728- LangChain: Building complex chains729- Manual: Simple, one-off extractions730 731## Resources732 733- **Documentation**: https://python.useinstructor.com734- **GitHub**: https://github.com/jxnl/instructor (15k+ stars)735- **Cookbook**: https://python.useinstructor.com/examples736- **Discord**: Community support available737 738## See Also739 740- `references/validation.md` - Advanced validation patterns741- `references/providers.md` - Provider-specific configuration742- `references/examples.md` - Real-world use cases743 744 745 Discovery context
Discovered by repository scan. No exact path reference found in the snapshot’s root AGENTS.md.