Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…

,\n description=\"3-20 alphanumeric characters\"\n )\n bio: str = Field(max_length=500, description=\"Bio up to 500 chars\")\n status: str = Field(pattern=r'^(active|inactive|pending)

Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…

)\n\n# pattern validates against regex\n```\n\n### Email and URL Validation\n\n```python\nfrom pydantic import EmailStr, HttpUrl, AnyUrl\n\nclass Contact(BaseModel):\n email: EmailStr # Validates email format\n website: HttpUrl # Validates HTTP/HTTPS URLs\n portfolio: AnyUrl # Any valid URL scheme\n\ncontact = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"Extract: [email protected], https://example.com\"\n }],\n response_model=Contact\n)\n```\n\n### Date and DateTime Validation\n\n```python\nfrom datetime import date, datetime\nfrom pydantic import Field, field_validator\n\nclass Event(BaseModel):\n event_date: date # Validates date format\n created_at: datetime # Validates datetime format\n year: int = Field(ge=1900, le=2100)\n\n @field_validator('event_date')\n def future_date(cls, v):\n \"\"\"Ensure event is in the future.\"\"\"\n if v \u003c date.today():\n raise ValueError('Event must be in the future')\n return v\n```\n\n### List and Dict Validation\n\n```python\nclass Document(BaseModel):\n tags: list[str] = Field(min_length=1, max_length=10)\n keywords: list[str] = Field(min_length=3, description=\"At least 3 keywords\")\n metadata: dict[str, str] = Field(description=\"String key-value pairs\")\n\n @field_validator('tags')\n def unique_tags(cls, v):\n \"\"\"Ensure tags are unique.\"\"\"\n if len(v) != len(set(v)):\n raise ValueError('Tags must be unique')\n return v\n```\n\n## Custom Field Validators\n\n### Basic Field Validator\n\n```python\nfrom pydantic import field_validator\n\nclass Person(BaseModel):\n name: str\n age: int\n\n @field_validator('name')\n def name_must_not_be_empty(cls, v):\n \"\"\"Validate name is not empty or just whitespace.\"\"\"\n if not v or not v.strip():\n raise ValueError('Name cannot be empty')\n return v.strip()\n\n @field_validator('age')\n def age_must_be_reasonable(cls, v):\n \"\"\"Validate age is between 0 and 120.\"\"\"\n if v \u003c 0 or v > 120:\n raise ValueError('Age must be between 0 and 120')\n return v\n```\n\n### Validator with Field Info\n\n```python\nfrom pydantic import ValidationInfo\n\nclass Article(BaseModel):\n title: str\n content: str\n\n @field_validator('content')\n def content_length(cls, v, info: ValidationInfo):\n \"\"\"Validate content is longer than title.\"\"\"\n if 'title' in info.data:\n title_len = len(info.data['title'])\n if len(v) \u003c title_len * 2:\n raise ValueError('Content should be at least 2x title length')\n return v\n```\n\n### Multiple Fields Validation\n\n```python\nclass TimeRange(BaseModel):\n start_time: str\n end_time: str\n\n @field_validator('start_time', 'end_time')\n def valid_time_format(cls, v):\n \"\"\"Validate both times are in HH:MM format.\"\"\"\n import re\n if not re.match(r'^\\d{2}:\\d{2}

Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…

, v):\n raise ValueError('Time must be in HH:MM format')\n return v\n```\n\n### Transform and Validate\n\n```python\nclass URL(BaseModel):\n url: str\n\n @field_validator('url')\n def normalize_url(cls, v):\n \"\"\"Add https:// if missing.\"\"\"\n if not v.startswith(('http://', 'https://')):\n v = f'https://{v}'\n return v\n```\n\n## Model-Level Validation\n\n### Cross-Field Validation\n\n```python\nfrom pydantic import model_validator\n\nclass DateRange(BaseModel):\n start_date: str\n end_date: str\n\n @model_validator(mode='after')\n def check_dates(self):\n \"\"\"Ensure end_date is after start_date.\"\"\"\n from datetime import datetime\n start = datetime.strptime(self.start_date, '%Y-%m-%d')\n end = datetime.strptime(self.end_date, '%Y-%m-%d')\n\n if end \u003c start:\n raise ValueError('end_date must be after start_date')\n return self\n\nclass PriceRange(BaseModel):\n min_price: float\n max_price: float\n\n @model_validator(mode='after')\n def check_price_range(self):\n \"\"\"Ensure max > min.\"\"\"\n if self.max_price \u003c= self.min_price:\n raise ValueError('max_price must be greater than min_price')\n return self\n```\n\n### Conditional Validation\n\n```python\nclass Order(BaseModel):\n order_type: str # \"standard\" or \"express\"\n delivery_date: str\n delivery_time: Optional[str] = None\n\n @model_validator(mode='after')\n def check_delivery_time(self):\n \"\"\"Express orders need delivery time.\"\"\"\n if self.order_type == \"express\" and not self.delivery_time:\n raise ValueError('Express orders require delivery_time')\n return self\n```\n\n### Complex Business Logic\n\n```python\nclass Discount(BaseModel):\n code: str\n percentage: float = Field(ge=0, le=100)\n min_purchase: float = Field(ge=0)\n max_discount: float = Field(ge=0)\n\n @model_validator(mode='after')\n def validate_discount(self):\n \"\"\"Ensure discount logic is sound.\"\"\"\n # Max discount can't exceed percentage of min_purchase\n theoretical_max = (self.percentage / 100) * self.min_purchase\n if self.max_discount > theoretical_max:\n self.max_discount = theoretical_max\n return self\n```\n\n## Complex Validation Patterns\n\n### Nested Model Validation\n\n```python\nclass Address(BaseModel):\n street: str\n city: str\n country: str\n postal_code: str\n\n @field_validator('postal_code')\n def validate_postal_code(cls, v, info: ValidationInfo):\n \"\"\"Validate postal code format based on country.\"\"\"\n if 'country' in info.data:\n country = info.data['country']\n if country == \"USA\":\n import re\n if not re.match(r'^\\d{5}(-\\d{4})?

Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…

, v):\n raise ValueError('Invalid US postal code')\n elif country == \"Canada\":\n if not re.match(r'^[A-Z]\\d[A-Z] \\d[A-Z]\\d

Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…

, v):\n raise ValueError('Invalid Canadian postal code')\n return v\n\nclass Person(BaseModel):\n name: str\n address: Address\n\n# Nested validation runs automatically\n```\n\n### List of Models\n\n```python\nclass Task(BaseModel):\n title: str = Field(min_length=1)\n priority: int = Field(ge=1, le=5)\n\nclass Project(BaseModel):\n name: str\n tasks: list[Task] = Field(min_length=1, description=\"At least 1 task\")\n\n @field_validator('tasks')\n def at_least_one_high_priority(cls, v):\n \"\"\"Ensure at least one task has priority >= 4.\"\"\"\n if not any(task.priority >= 4 for task in v):\n raise ValueError('Project needs at least one high-priority task')\n return v\n```\n\n### Union Type Validation\n\n```python\nfrom typing import Union\n\nclass TextBlock(BaseModel):\n type: str = \"text\"\n content: str = Field(min_length=1)\n\nclass ImageBlock(BaseModel):\n type: str = \"image\"\n url: HttpUrl\n alt_text: str\n\nclass Page(BaseModel):\n title: str\n blocks: list[Union[TextBlock, ImageBlock]]\n\n @field_validator('blocks')\n def validate_block_types(cls, v):\n \"\"\"Ensure first block is TextBlock.\"\"\"\n if v and not isinstance(v[0], TextBlock):\n raise ValueError('First block must be text')\n return v\n```\n\n### Dependent Fields\n\n```python\nclass Subscription(BaseModel):\n plan: str # \"free\", \"pro\", \"enterprise\"\n max_users: int\n features: list[str]\n\n @model_validator(mode='after')\n def validate_plan_limits(self):\n \"\"\"Enforce plan-specific limits.\"\"\"\n limits = {\n \"free\": {\"max_users\": 1, \"required_features\": [\"basic\"]},\n \"pro\": {\"max_users\": 10, \"required_features\": [\"basic\", \"advanced\"]},\n \"enterprise\": {\"max_users\": 999, \"required_features\": [\"basic\", \"advanced\", \"premium\"]}\n }\n\n if self.plan in limits:\n limit = limits[self.plan]\n\n if self.max_users > limit[\"max_users\"]:\n raise ValueError(f'{self.plan} plan limited to {limit[\"max_users\"]} users')\n\n for feature in limit[\"required_features\"]:\n if feature not in self.features:\n raise ValueError(f'{self.plan} plan requires {feature} feature')\n\n return self\n```\n\n## Error Handling\n\n### Graceful Degradation\n\n```python\nclass OptionalExtraction(BaseModel):\n # Required fields\n title: str\n\n # Optional fields with defaults\n author: Optional[str] = None\n date: Optional[str] = None\n tags: list[str] = Field(default_factory=list)\n\n# LLM can succeed even if it can't extract everything\n```\n\n### Partial Validation\n\n```python\nfrom pydantic import ValidationError\n\ndef extract_with_fallback(text: str):\n \"\"\"Try full extraction, fall back to partial.\"\"\"\n try:\n # Try full extraction\n return client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\"role\": \"user\", \"content\": text}],\n response_model=FullModel\n )\n except ValidationError:\n # Fall back to partial model\n return client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\"role\": \"user\", \"content\": text}],\n response_model=PartialModel\n )\n```\n\n### Validation Error Inspection\n\n```python\nfrom pydantic import ValidationError\n\ntry:\n result = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[...],\n response_model=MyModel,\n max_retries=3\n )\nexcept ValidationError as e:\n # Inspect specific errors\n for error in e.errors():\n field = error['loc'][0]\n message = error['msg']\n print(f\"Field '{field}' failed: {message}\")\n\n # Custom handling per field\n if field == 'email':\n # Handle email validation failure\n pass\n```\n\n### Custom Error Messages\n\n```python\nclass DetailedModel(BaseModel):\n name: str = Field(\n min_length=2,\n max_length=100,\n description=\"Name between 2-100 characters\"\n )\n age: int = Field(\n ge=0,\n le=120,\n description=\"Age between 0 and 120 years\"\n )\n\n @field_validator('name')\n def validate_name(cls, v):\n \"\"\"Provide helpful error message.\"\"\"\n if not v.strip():\n raise ValueError(\n 'Name cannot be empty. '\n 'Please provide a valid name from the text.'\n )\n return v\n\n# When validation fails, LLM sees these helpful messages\n```\n\n## Validation Best Practices\n\n### 1. Be Specific\n\n```python\n# ❌ Bad: Vague validation\nclass Item(BaseModel):\n name: str\n\n# ✅ Good: Specific constraints\nclass Item(BaseModel):\n name: str = Field(\n min_length=1,\n max_length=200,\n description=\"Item name, 1-200 characters\"\n )\n```\n\n### 2. Provide Context\n\n```python\n# ✅ Good: Explain why validation failed\n@field_validator('price')\ndef validate_price(cls, v):\n if v \u003c= 0:\n raise ValueError(\n 'Price must be positive. '\n 'Extract numeric price from text without currency symbols.'\n )\n return v\n```\n\n### 3. Use Enums for Fixed Sets\n\n```python\n# ❌ Bad: String validation\nstatus: str\n\n@field_validator('status')\ndef validate_status(cls, v):\n if v not in ['active', 'inactive', 'pending']:\n raise ValueError('Invalid status')\n return v\n\n# ✅ Good: Enum\nclass Status(str, Enum):\n ACTIVE = \"active\"\n INACTIVE = \"inactive\"\n PENDING = \"pending\"\n\nstatus: Status # Validation automatic\n```\n\n### 4. Balance Strictness\n\n```python\n# Too strict: May fail unnecessarily\nclass StrictModel(BaseModel):\n date: str = Field(pattern=r'^\\d{4}-\\d{2}-\\d{2}

Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…

)\n # Fails if LLM uses \"2024-1-5\" instead of \"2024-01-05\"\n\n# Better: Normalize in validator\nclass FlexibleModel(BaseModel):\n date: str\n\n @field_validator('date')\n def normalize_date(cls, v):\n from datetime import datetime\n # Parse flexible formats\n for fmt in ['%Y-%m-%d', '%Y/%m/%d', '%m/%d/%Y']:\n try:\n dt = datetime.strptime(v, fmt)\n return dt.strftime('%Y-%m-%d') # Normalize\n except ValueError:\n continue\n raise ValueError('Invalid date format')\n```\n\n### 5. Test Validation\n\n```python\n# Test your validators with edge cases\ndef test_validation():\n # Should succeed\n valid = MyModel(field=\"valid_value\")\n\n # Should fail\n try:\n invalid = MyModel(field=\"invalid\")\n assert False, \"Should have raised ValidationError\"\n except ValidationError:\n pass # Expected\n\n# Run tests before using in production\n```\n\n## Advanced Techniques\n\n### Conditional Required Fields\n\n```python\nfrom typing import Optional\n\nclass ConditionalModel(BaseModel):\n type: str\n detail_a: Optional[str] = None\n detail_b: Optional[str] = None\n\n @model_validator(mode='after')\n def check_required_details(self):\n \"\"\"Require different fields based on type.\"\"\"\n if self.type == \"type_a\" and not self.detail_a:\n raise ValueError('type_a requires detail_a')\n if self.type == \"type_b\" and not self.detail_b:\n raise ValueError('type_b requires detail_b')\n return self\n```\n\n### Validation with External Data\n\n```python\nclass Product(BaseModel):\n sku: str\n name: str\n\n @field_validator('sku')\n def validate_sku(cls, v):\n \"\"\"Check SKU exists in database.\"\"\"\n # Query database or API\n if not database.sku_exists(v):\n raise ValueError(f'SKU {v} not found in catalog')\n return v\n```\n\n### Progressive Validation\n\n```python\n# Start with loose validation\nclass Stage1(BaseModel):\n data: str # Any string\n\n# Then strict validation\nclass Stage2(BaseModel):\n data: str = Field(pattern=r'^[A-Z]{3}-\\d{6}

Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…

)\n\n# Use Stage1 for initial extraction\n# Use Stage2 for final validation\n```\n\n## Resources\n\n- **Pydantic Docs**: https://docs.pydantic.dev/latest/concepts/validators/\n- **Instructor Examples**: https://python.useinstructor.com/examples\n","content_type":"text/markdown; charset=utf-8","language":"markdown","size":15306,"content_sha256":"83039b93f697c72141e618210d9acdd9fa50654b22cc810b3eff23fddbd7dfe4"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Instructor: Structured LLM Outputs","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use This Skill","type":"text"}]},{"type":"paragraph","content":[{"text":"Use Instructor when you need to:","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Extract structured data","type":"text","marks":[{"type":"strong"}]},{"text":" from LLM responses reliably","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Validate outputs","type":"text","marks":[{"type":"strong"}]},{"text":" against Pydantic schemas automatically","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Retry failed extractions","type":"text","marks":[{"type":"strong"}]},{"text":" with automatic error handling","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Parse complex JSON","type":"text","marks":[{"type":"strong"}]},{"text":" with type safety and validation","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Stream partial results","type":"text","marks":[{"type":"strong"}]},{"text":" for real-time processing","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Support multiple LLM providers","type":"text","marks":[{"type":"strong"}]},{"text":" with consistent API","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"GitHub Stars","type":"text","marks":[{"type":"strong"}]},{"text":": 15,000+ | ","type":"text"},{"text":"Battle-tested","type":"text","marks":[{"type":"strong"}]},{"text":": 100,000+ developers","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Installation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"bash"},"content":[{"text":"# Base installation\npip install instructor\n\n# With specific providers\npip install \"instructor[anthropic]\" # Anthropic Claude\npip install \"instructor[openai]\" # OpenAI\npip install \"instructor[all]\" # All providers","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Quick Start","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Basic Example: Extract User Data","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import instructor\nfrom pydantic import BaseModel\nfrom anthropic import Anthropic\n\n# Define output structure\nclass User(BaseModel):\n name: str\n age: int\n email: str\n\n# Create instructor client\nclient = instructor.from_anthropic(Anthropic())\n\n# Extract structured data\nuser = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"John Doe is 30 years old. His email is [email protected]\"\n }],\n response_model=User\n)\n\nprint(user.name) # \"John Doe\"\nprint(user.age) # 30\nprint(user.email) # \"[email protected]\"","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"With OpenAI","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from openai import OpenAI\n\nclient = instructor.from_openai(OpenAI())\n\nuser = client.chat.completions.create(\n model=\"gpt-4o-mini\",\n response_model=User,\n messages=[{\"role\": \"user\", \"content\": \"Extract: Alice, 25, [email protected]\"}]\n)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Core Concepts","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Response Models (Pydantic)","type":"text"}]},{"type":"paragraph","content":[{"text":"Response models define the structure and validation rules for LLM outputs.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Basic Model","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pydantic import BaseModel, Field\n\nclass Article(BaseModel):\n title: str = Field(description=\"Article title\")\n author: str = Field(description=\"Author name\")\n word_count: int = Field(description=\"Number of words\", gt=0)\n tags: list[str] = Field(description=\"List of relevant tags\")\n\narticle = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"Analyze this article: [article text]\"\n }],\n response_model=Article\n)","type":"text"}]},{"type":"paragraph","content":[{"text":"Benefits:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Type safety with Python type hints","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Automatic validation (word_count > 0)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Self-documenting with Field descriptions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"IDE autocomplete support","type":"text"}]}]}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Nested Models","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class Address(BaseModel):\n street: str\n city: str\n country: str\n\nclass Person(BaseModel):\n name: str\n age: int\n address: Address # Nested model\n\nperson = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"John lives at 123 Main St, Boston, USA\"\n }],\n response_model=Person\n)\n\nprint(person.address.city) # \"Boston\"","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Optional Fields","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from typing import Optional\n\nclass Product(BaseModel):\n name: str\n price: float\n discount: Optional[float] = None # Optional\n description: str = Field(default=\"No description\") # Default value\n\n# LLM doesn't need to provide discount or description","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Enums for Constraints","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from enum import Enum\n\nclass Sentiment(str, Enum):\n POSITIVE = \"positive\"\n NEGATIVE = \"negative\"\n NEUTRAL = \"neutral\"\n\nclass Review(BaseModel):\n text: str\n sentiment: Sentiment # Only these 3 values allowed\n\nreview = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"This product is amazing!\"\n }],\n response_model=Review\n)\n\nprint(review.sentiment) # Sentiment.POSITIVE","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Validation","type":"text"}]},{"type":"paragraph","content":[{"text":"Pydantic validates LLM outputs automatically. If validation fails, Instructor retries.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Built-in Validators","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pydantic import Field, EmailStr, HttpUrl\n\nclass Contact(BaseModel):\n name: str = Field(min_length=2, max_length=100)\n age: int = Field(ge=0, le=120) # 0 \u003c= age \u003c= 120\n email: EmailStr # Validates email format\n website: HttpUrl # Validates URL format\n\n# If LLM provides invalid data, Instructor retries automatically","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Custom Validators","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pydantic import field_validator\n\nclass Event(BaseModel):\n name: str\n date: str\n attendees: int\n\n @field_validator('date')\n def validate_date(cls, v):\n \"\"\"Ensure date is in YYYY-MM-DD format.\"\"\"\n import re\n if not re.match(r'\\d{4}-\\d{2}-\\d{2}', v):\n raise ValueError('Date must be YYYY-MM-DD format')\n return v\n\n @field_validator('attendees')\n def validate_attendees(cls, v):\n \"\"\"Ensure positive attendees.\"\"\"\n if v \u003c 1:\n raise ValueError('Must have at least 1 attendee')\n return v","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Model-Level Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pydantic import model_validator\n\nclass DateRange(BaseModel):\n start_date: str\n end_date: str\n\n @model_validator(mode='after')\n def check_dates(self):\n \"\"\"Ensure end_date is after start_date.\"\"\"\n from datetime import datetime\n start = datetime.strptime(self.start_date, '%Y-%m-%d')\n end = datetime.strptime(self.end_date, '%Y-%m-%d')\n\n if end \u003c start:\n raise ValueError('end_date must be after start_date')\n return self","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Automatic Retrying","type":"text"}]},{"type":"paragraph","content":[{"text":"Instructor retries automatically when validation fails, providing error feedback to the LLM.","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# Retries up to 3 times if validation fails\nuser = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"Extract user from: John, age unknown\"\n }],\n response_model=User,\n max_retries=3 # Default is 3\n)\n\n# If age can't be extracted, Instructor tells the LLM:\n# \"Validation error: age - field required\"\n# LLM tries again with better extraction","type":"text"}]},{"type":"paragraph","content":[{"text":"How it works:","type":"text","marks":[{"type":"strong"}]}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"LLM generates output","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Pydantic validates","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"If invalid: Error message sent back to LLM","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"LLM tries again with error feedback","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Repeats up to max_retries","type":"text"}]}]}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Streaming","type":"text"}]},{"type":"paragraph","content":[{"text":"Stream partial results for real-time processing.","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Streaming Partial Objects","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from instructor import Partial\n\nclass Story(BaseModel):\n title: str\n content: str\n tags: list[str]\n\n# Stream partial updates as LLM generates\nfor partial_story in client.messages.create_partial(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"Write a short sci-fi story\"\n }],\n response_model=Story\n):\n print(f\"Title: {partial_story.title}\")\n print(f\"Content so far: {partial_story.content[:100]}...\")\n # Update UI in real-time","type":"text"}]},{"type":"heading","attrs":{"level":4},"content":[{"text":"Streaming Iterables","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class Task(BaseModel):\n title: str\n priority: str\n\n# Stream list items as they're generated\ntasks = client.messages.create_iterable(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"Generate 10 project tasks\"\n }],\n response_model=Task\n)\n\nfor task in tasks:\n print(f\"- {task.title} ({task.priority})\")\n # Process each task as it arrives","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Provider Configuration","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Anthropic Claude","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"import instructor\nfrom anthropic import Anthropic\n\nclient = instructor.from_anthropic(\n Anthropic(api_key=\"your-api-key\")\n)\n\n# Use with Claude models\nresponse = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[...],\n response_model=YourModel\n)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"OpenAI","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from openai import OpenAI\n\nclient = instructor.from_openai(\n OpenAI(api_key=\"your-api-key\")\n)\n\nresponse = client.chat.completions.create(\n model=\"gpt-4o-mini\",\n response_model=YourModel,\n messages=[...]\n)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Local Models (Ollama)","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from openai import OpenAI\n\n# Point to local Ollama server\nclient = instructor.from_openai(\n OpenAI(\n base_url=\"http://localhost:11434/v1\",\n api_key=\"ollama\" # Required but ignored\n ),\n mode=instructor.Mode.JSON\n)\n\nresponse = client.chat.completions.create(\n model=\"llama3.1\",\n response_model=YourModel,\n messages=[...]\n)","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Common Patterns","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pattern 1: Data Extraction from Text","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class CompanyInfo(BaseModel):\n name: str\n founded_year: int\n industry: str\n employees: int\n headquarters: str\n\ntext = \"\"\"\nTesla, Inc. was founded in 2003. It operates in the automotive and energy\nindustry with approximately 140,000 employees. The company is headquartered\nin Austin, Texas.\n\"\"\"\n\ncompany = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": f\"Extract company information from: {text}\"\n }],\n response_model=CompanyInfo\n)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pattern 2: Classification","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class Category(str, Enum):\n TECHNOLOGY = \"technology\"\n FINANCE = \"finance\"\n HEALTHCARE = \"healthcare\"\n EDUCATION = \"education\"\n OTHER = \"other\"\n\nclass ArticleClassification(BaseModel):\n category: Category\n confidence: float = Field(ge=0.0, le=1.0)\n keywords: list[str]\n\nclassification = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": \"Classify this article: [article text]\"\n }],\n response_model=ArticleClassification\n)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pattern 3: Multi-Entity Extraction","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class Person(BaseModel):\n name: str\n role: str\n\nclass Organization(BaseModel):\n name: str\n industry: str\n\nclass Entities(BaseModel):\n people: list[Person]\n organizations: list[Organization]\n locations: list[str]\n\ntext = \"Tim Cook, CEO of Apple, announced at the event in Cupertino...\"\n\nentities = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": f\"Extract all entities from: {text}\"\n }],\n response_model=Entities\n)\n\nfor person in entities.people:\n print(f\"{person.name} - {person.role}\")","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pattern 4: Structured Analysis","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class SentimentAnalysis(BaseModel):\n overall_sentiment: Sentiment\n positive_aspects: list[str]\n negative_aspects: list[str]\n suggestions: list[str]\n score: float = Field(ge=-1.0, le=1.0)\n\nreview = \"The product works well but setup was confusing...\"\n\nanalysis = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": f\"Analyze this review: {review}\"\n }],\n response_model=SentimentAnalysis\n)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Pattern 5: Batch Processing","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"def extract_person(text: str) -> Person:\n return client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[{\n \"role\": \"user\",\n \"content\": f\"Extract person from: {text}\"\n }],\n response_model=Person\n )\n\ntexts = [\n \"John Doe is a 30-year-old engineer\",\n \"Jane Smith, 25, works in marketing\",\n \"Bob Johnson, age 40, software developer\"\n]\n\npeople = [extract_person(text) for text in texts]","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Advanced Features","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Union Types","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from typing import Union\n\nclass TextContent(BaseModel):\n type: str = \"text\"\n content: str\n\nclass ImageContent(BaseModel):\n type: str = \"image\"\n url: HttpUrl\n caption: str\n\nclass Post(BaseModel):\n title: str\n content: Union[TextContent, ImageContent] # Either type\n\n# LLM chooses appropriate type based on content","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Dynamic Models","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pydantic import create_model\n\n# Create model at runtime\nDynamicUser = create_model(\n 'User',\n name=(str, ...),\n age=(int, Field(ge=0)),\n email=(EmailStr, ...)\n)\n\nuser = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[...],\n response_model=DynamicUser\n)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Custom Modes","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# For providers without native structured outputs\nclient = instructor.from_anthropic(\n Anthropic(),\n mode=instructor.Mode.JSON # JSON mode\n)\n\n# Available modes:\n# - Mode.ANTHROPIC_TOOLS (recommended for Claude)\n# - Mode.JSON (fallback)\n# - Mode.TOOLS (OpenAI tools)","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Context Management","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# Single-use client\nwith instructor.from_anthropic(Anthropic()) as client:\n result = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[...],\n response_model=YourModel\n )\n # Client closed automatically","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Error Handling","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Handling Validation Errors","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"from pydantic import ValidationError\n\ntry:\n user = client.messages.create(\n model=\"claude-sonnet-4-5-20250929\",\n max_tokens=1024,\n messages=[...],\n response_model=User,\n max_retries=3\n )\nexcept ValidationError as e:\n print(f\"Failed after retries: {e}\")\n # Handle gracefully\n\nexcept Exception as e:\n print(f\"API error: {e}\")","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"Custom Error Messages","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class ValidatedUser(BaseModel):\n name: str = Field(description=\"Full name, 2-100 characters\")\n age: int = Field(description=\"Age between 0 and 120\", ge=0, le=120)\n email: EmailStr = Field(description=\"Valid email address\")\n\n class Config:\n # Custom error messages\n json_schema_extra = {\n \"examples\": [\n {\n \"name\": \"John Doe\",\n \"age\": 30,\n \"email\": \"[email protected]\"\n }\n ]\n }","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Best Practices","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"1. Clear Field Descriptions","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# ❌ Bad: Vague\nclass Product(BaseModel):\n name: str\n price: float\n\n# ✅ Good: Descriptive\nclass Product(BaseModel):\n name: str = Field(description=\"Product name from the text\")\n price: float = Field(description=\"Price in USD, without currency symbol\")","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"2. Use Appropriate Validation","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# ✅ Good: Constrain values\nclass Rating(BaseModel):\n score: int = Field(ge=1, le=5, description=\"Rating from 1 to 5 stars\")\n review: str = Field(min_length=10, description=\"Review text, at least 10 chars\")","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"3. Provide Examples in Prompts","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"messages = [{\n \"role\": \"user\",\n \"content\": \"\"\"Extract person info from: \"John, 30, engineer\"\n\nExample format:\n{\n \"name\": \"John Doe\",\n \"age\": 30,\n \"occupation\": \"engineer\"\n}\"\"\"\n}]","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"4. Use Enums for Fixed Categories","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"# ✅ Good: Enum ensures valid values\nclass Status(str, Enum):\n PENDING = \"pending\"\n APPROVED = \"approved\"\n REJECTED = \"rejected\"\n\nclass Application(BaseModel):\n status: Status # LLM must choose from enum","type":"text"}]},{"type":"heading","attrs":{"level":3},"content":[{"text":"5. Handle Missing Data Gracefully","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":"python"},"content":[{"text":"class PartialData(BaseModel):\n required_field: str\n optional_field: Optional[str] = None\n default_field: str = \"default_value\"\n\n# LLM only needs to provide required_field","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Comparison to Alternatives","type":"text"}]},{"type":"table","attrs":{"layout":null},"content":[{"type":"tr","content":[{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Feature","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Instructor","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Manual JSON","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"LangChain","type":"text"}]}]},{"type":"th","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"DSPy","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Type Safety","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌ No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"⚠️ Partial","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Auto Validation","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌ No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌ No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"⚠️ Limited","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Auto Retry","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌ No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌ No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Streaming","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌ No","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"❌ No","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Multi-Provider","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"⚠️ Manual","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"✅ Yes","type":"text"}]}]}]},{"type":"tr","content":[{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Learning Curve","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Low","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Low","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"Medium","type":"text"}]}]},{"type":"td","attrs":{"colspan":1,"rowspan":1,"colwidth":null,"alignment":""},"content":[{"type":"paragraph","content":[{"text":"High","type":"text"}]}]}]}]},{"type":"paragraph","content":[{"text":"When to choose Instructor:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Need structured, validated outputs","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Want type safety and IDE support","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Require automatic retries","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Building data extraction systems","type":"text"}]}]}]},{"type":"paragraph","content":[{"text":"When to choose alternatives:","type":"text","marks":[{"type":"strong"}]}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"DSPy: Need prompt optimization","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"LangChain: Building complex chains","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Manual: Simple, one-off extractions","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Resources","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Documentation","type":"text","marks":[{"type":"strong"}]},{"text":": https://python.useinstructor.com","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"GitHub","type":"text","marks":[{"type":"strong"}]},{"text":": https://github.com/jxnl/instructor (15k+ stars)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Cookbook","type":"text","marks":[{"type":"strong"}]},{"text":": https://python.useinstructor.com/examples","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Discord","type":"text","marks":[{"type":"strong"}]},{"text":": Community support available","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"See Also","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/validation.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Advanced validation patterns","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/providers.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Provider-specific configuration","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"references/examples.md","type":"text","marks":[{"type":"code_inline"}]},{"text":" - Real-world use cases","type":"text"}]}]}]}]},"metadata":{"date":"2026-11-02","name":"instructor","tags":["Prompt Engineering","Instructor","Structured Output","Pydantic","Data Extraction","JSON Parsing","Type Safety","Validation","Streaming","OpenAI","Anthropic"],"author":"Orchestra Research","source":{"stars":14202,"repo_name":"AI-Research-SKILLs","origin_url":"https://github.com/Orchestra-Research/AI-Research-SKILLs/blob/HEAD/16-prompt-engineering/instructor/SKILL.md","repo_owner":"Orchestra-Research","body_sha256":"7be950beb848b05aa4f41e61cfe27b162cad4a4d029d0abe13401aa743bab794","cluster_key":"d971802ee96e8e7cf73b0a68aaf57ef72d75f6be77b5a071cf387240f529f4b8","clean_bundle":{"format":"clean-skill-bundle-v1","source":"Orchestra-Research/AI-Research-SKILLs/16-prompt-engineering/instructor/SKILL.md","text_bytes":18955,"source_commit":"773a52944ba4747a18bd4ae9ade53fff041adcbc","skills_sh_rank":13213,"attachment_count":3,"text_attachments":3,"attachment_storage":"skill_attachments","binary_attachments":0},"cluster_size":1,"skill_md_path":"16-prompt-engineering/instructor/SKILL.md","import_metadata":{"date":"2026-11-02","author":"Orchestra Research","version":"1.0.0","category":"ai-agent-development","category_label":"AI"},"exact_dupes_collapsed_into_this":0},"license":"MIT","version":"1.0.0","category":"ai-agent-development","import_tag":"skills-sh-top20k-2026-11","description":"Extract structured data from LLM responses with Pydantic validation, retry failed extractions automatically, parse complex JSON with type safety, and stream partial results with Instructor - battle-tested structured output library","dependencies":["instructor","pydantic","openai","anthropic"]}},"renderedAt":1793671712527}

Instructor: Structured LLM Outputs When to Use This Skill Use Instructor when you need to: - Extract structured data from LLM responses reliably - Validate outputs against Pydantic schemas automatically - Retry failed extractions with automatic error handling - Parse complex JSON with type safety and validation - Stream partial results for real-time processing - Support multiple LLM providers with consistent API GitHub Stars : 15,000+ | Battle-tested : 100,000+ developers Installation Quick Start Basic Example: Extract User Data With OpenAI Core Concepts 1. Response Models (Pydantic) Response…