참고소스 수정본
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
---
|
||||
title: Your First LLM Extraction with Instructor
|
||||
description: Step-by-step tutorial for your first structured data extraction from language models using Instructor and Pydantic.
|
||||
---
|
||||
|
||||
# Your First LLM Extraction: Structured Outputs Tutorial
|
||||
|
||||
Learn how to extract structured data from LLMs using Instructor in this hands-on tutorial. We'll build a simple yet powerful example that demonstrates how to transform unstructured text into validated Python objects using GPT-4, Claude, or any supported LLM.
|
||||
|
||||
## Quick Start: Extract Structured Data from LLMs
|
||||
|
||||
This LLM tutorial shows you how to extract structured information from natural language. We'll parse a person's name and age - a perfect starting point for understanding Instructor's power:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
# 1. Define your data model for LLM extraction
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
# 2. Initialize Instructor with your LLM provider
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
# 3. Extract structured data from LLM
|
||||
person = client.create(
|
||||
response_model=Person, # Type-safe extraction
|
||||
messages=[
|
||||
{"role": "user", "content": "John Doe is 30 years old"}
|
||||
]
|
||||
)
|
||||
|
||||
# 4. Use validated, structured data from LLM
|
||||
print(f"Name: {person.name}, Age: {person.age}")
|
||||
# Output: Name: John Doe, Age: 30
|
||||
```
|
||||
|
||||
## How Instructor LLM Extraction Works
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
||||
│ Define │ -> │ Instruct LLM │ -> │ Get Typed │
|
||||
│ Structure │ │ to Extract │ │ Response │
|
||||
└─────────────┘ └──────────────┘ └─────────────┘
|
||||
```
|
||||
|
||||
Understanding the LLM structured output pipeline:
|
||||
|
||||
### Step 1: Define Your LLM Output Schema
|
||||
|
||||
```python
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
```
|
||||
|
||||
Pydantic models define the structure for LLM outputs:
|
||||
- `name`: String field for extracting names from LLM
|
||||
- `age`: Integer field with automatic type validation
|
||||
|
||||
### Step 2: Configure Your LLM Client
|
||||
|
||||
```python
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
```
|
||||
|
||||
Instructor enhances your LLM client with structured output capabilities. Works with OpenAI, Anthropic, Google, and 15+ providers.
|
||||
|
||||
### Step 3: Execute LLM Extraction
|
||||
|
||||
```python
|
||||
person = client.create(
|
||||
response_model=Person,
|
||||
messages=[
|
||||
{"role": "user", "content": "John Doe is 30 years old"}
|
||||
]
|
||||
)
|
||||
```
|
||||
|
||||
Key parameters for structured LLM outputs:
|
||||
- `response_model`: Pydantic model for type-safe extraction
|
||||
- `messages`: Input text for the LLM to process
|
||||
|
||||
Note: The model is already specified when creating the client with `from_provider()`, so you don't need to pass it again.
|
||||
|
||||
### Step 4: Work with Validated LLM Data
|
||||
|
||||
```python
|
||||
print(f"Name: {person.name}, Age: {person.age}")
|
||||
```
|
||||
|
||||
Get back a fully validated Python object from your LLM - no JSON parsing, no validation errors, just clean data ready to use.
|
||||
|
||||
## Enhance LLM Extraction with Field Descriptions
|
||||
|
||||
Improve LLM accuracy by providing clear field descriptions:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str = Field(description="Person's full name")
|
||||
age: int = Field(description="Person's age in years")
|
||||
```
|
||||
|
||||
Field descriptions act as prompts, guiding the LLM to extract exactly what you need.
|
||||
|
||||
## Handle Optional Data in LLM Responses
|
||||
|
||||
Real-world LLM extractions often have missing data. Handle it gracefully:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: Optional[int] = None # Now age is optional
|
||||
```
|
||||
|
||||
## Continue Your LLM Tutorial Journey
|
||||
|
||||
You've successfully extracted structured data from an LLM! Next steps:
|
||||
|
||||
1. **[Advanced Response Models](response_models.md)** - Complex schemas for LLM outputs
|
||||
2. **[Multi-Provider Setup](../../concepts/from_provider.md)** - Use GPT-4, Claude, Gemini interchangeably
|
||||
3. **[Production Patterns](../patterns/simple_object.md)** - Real-world LLM extraction examples
|
||||
|
||||
## Common LLM Extraction Patterns
|
||||
|
||||
- **Entity Extraction**: Names, dates, locations from unstructured text
|
||||
- **Sentiment Analysis**: Structured sentiment scores with reasoning
|
||||
- **Data Classification**: Categorize text into predefined schemas
|
||||
- **Information Parsing**: Convert documents into structured databases
|
||||
|
||||
Ready to build more complex LLM extractions? Continue to [Response Models](response_models.md) →
|
||||
147
참고/instructor-main/docs/learning/getting_started/installation.md
Normal file
147
참고/instructor-main/docs/learning/getting_started/installation.md
Normal file
@@ -0,0 +1,147 @@
|
||||
---
|
||||
title: Installing Instructor for LLM Structured Outputs
|
||||
description: Complete installation guide for Instructor with support for OpenAI, Anthropic, Google, and 15+ LLM providers. Get started in minutes.
|
||||
---
|
||||
|
||||
# Instructor Installation Guide: Setup for LLM Structured Outputs
|
||||
|
||||
Learn how to install Instructor, the leading Python library for extracting structured data from LLMs like GPT-4, Claude, and Gemini. This comprehensive installation tutorial covers all major LLM providers and gets you ready for production use.
|
||||
|
||||
## Quick Start: Install Instructor for LLM Development
|
||||
|
||||
Get started with structured LLM outputs in seconds. Install Instructor using pip:
|
||||
|
||||
```shell
|
||||
pip install instructor
|
||||
```
|
||||
|
||||
Instructor leverages Pydantic for type-safe LLM data extraction:
|
||||
|
||||
```shell
|
||||
pip install pydantic
|
||||
```
|
||||
|
||||
> **Pro Tip**: Use `uv` for faster installation: `uv pip install instructor`
|
||||
|
||||
## LLM Provider Installation Guide
|
||||
|
||||
Instructor supports 15+ LLM providers. Here's how to install and configure each:
|
||||
|
||||
### OpenAI (GPT-4, GPT-3.5)
|
||||
|
||||
OpenAI is the default LLM provider for Instructor. Perfect for GPT-4 and GPT-3.5-turbo structured outputs:
|
||||
|
||||
```shell
|
||||
pip install instructor
|
||||
```
|
||||
|
||||
Configure your OpenAI API key for LLM access:
|
||||
|
||||
```shell
|
||||
export OPENAI_API_KEY=your_openai_key
|
||||
```
|
||||
|
||||
### Anthropic Claude LLM Setup
|
||||
|
||||
Extract structured data from Claude 3 models (Opus, Sonnet, Haiku) with native tool support:
|
||||
|
||||
```shell
|
||||
pip install "instructor[anthropic]"
|
||||
```
|
||||
|
||||
Configure Claude API access:
|
||||
|
||||
```shell
|
||||
export ANTHROPIC_API_KEY=your_anthropic_key
|
||||
```
|
||||
|
||||
### Google Gemini LLM Integration
|
||||
|
||||
Use Gemini Pro and Flash models for structured outputs with function calling:
|
||||
|
||||
```shell
|
||||
pip install "instructor[google-genai]"
|
||||
```
|
||||
|
||||
Set up Gemini API access:
|
||||
|
||||
```shell
|
||||
export GOOGLE_API_KEY=your_google_key
|
||||
```
|
||||
|
||||
### Cohere
|
||||
|
||||
To use with Cohere's models:
|
||||
|
||||
```shell
|
||||
pip install "instructor[cohere]"
|
||||
```
|
||||
|
||||
Set up your Cohere API key:
|
||||
|
||||
```shell
|
||||
export COHERE_API_KEY=your_cohere_key
|
||||
```
|
||||
|
||||
### Mistral
|
||||
|
||||
To use with Mistral AI's models:
|
||||
|
||||
```shell
|
||||
pip install "instructor[mistralai]"
|
||||
```
|
||||
|
||||
Set up your Mistral API key:
|
||||
|
||||
```shell
|
||||
export MISTRAL_API_KEY=your_mistral_key
|
||||
```
|
||||
|
||||
### LiteLLM (Multiple Providers)
|
||||
|
||||
To use LiteLLM for accessing multiple providers:
|
||||
|
||||
```shell
|
||||
pip install "instructor[litellm]"
|
||||
```
|
||||
|
||||
Set up API keys for the providers you want to use.
|
||||
|
||||
## Verify Your Instructor LLM Setup
|
||||
|
||||
Test your Instructor installation with this simple LLM structured output example:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
person = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=Person,
|
||||
messages=[
|
||||
{"role": "user", "content": "John Doe is 30 years old"}
|
||||
]
|
||||
)
|
||||
|
||||
print(f"Name: {person.name}, Age: {person.age}")
|
||||
```
|
||||
|
||||
## Next Steps in Your LLM Tutorial Journey
|
||||
|
||||
With Instructor installed, you're ready to build powerful LLM applications:
|
||||
|
||||
1. **[Create Your First LLM Extraction](first_extraction.md)** - Build structured outputs with any LLM
|
||||
2. **[Master Response Models](response_models.md)** - Learn Pydantic models for LLM data validation
|
||||
3. **[Configure LLM Clients](../../concepts/from_provider.md)** - Set up OpenAI, Anthropic, Google, and more
|
||||
|
||||
## Common Installation Issues
|
||||
|
||||
- **Import Errors**: Ensure you've installed the provider-specific extras (e.g., `instructor[anthropic]`)
|
||||
- **API Key Issues**: Verify your environment variables are set correctly
|
||||
- **Version Conflicts**: Use `pip install --upgrade instructor` to get the latest version
|
||||
|
||||
Ready to extract structured data from LLMs? Continue to [Your First Extraction](first_extraction.md) →
|
||||
@@ -0,0 +1,202 @@
|
||||
---
|
||||
title: Understanding Response Models in Instructor
|
||||
description: Learn how to create response models with Pydantic to define structure, validation rules, and extract complex data from LLMs.
|
||||
---
|
||||
|
||||
# Understanding Response Models
|
||||
|
||||
Response models are at the core of Instructor's functionality. They define the structure of the data you want to extract and provide validation rules. This guide explains how to create different types of response models for various use cases.
|
||||
|
||||
## Basic Models
|
||||
|
||||
Let's start with a simple model similar to what we've seen before:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
|
||||
class User(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
```
|
||||
|
||||
This defines a model with two required fields: `name` (a string) and `age` (an integer).
|
||||
|
||||
## Adding Field Metadata
|
||||
|
||||
You can add metadata to fields using the `Field` class:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class WeatherForecast(BaseModel):
|
||||
"""Weather forecast for a specific location"""
|
||||
|
||||
temperature: float = Field(
|
||||
description="Current temperature in Celsius"
|
||||
)
|
||||
condition: str = Field(
|
||||
description="Weather condition (sunny, cloudy, rainy, etc.)"
|
||||
)
|
||||
humidity: int = Field(
|
||||
description="Humidity percentage from 0-100"
|
||||
)
|
||||
```
|
||||
|
||||
Field descriptions help the LLM understand what information to extract for each field.
|
||||
|
||||
## Field Validation
|
||||
|
||||
You can add validation rules to ensure the extracted data meets your requirements:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str = Field(min_length=3)
|
||||
price: float = Field(gt=0) # greater than 0
|
||||
quantity: int = Field(ge=0) # greater than or equal to 0
|
||||
description: str = Field(max_length=500)
|
||||
```
|
||||
|
||||
Common validation parameters include:
|
||||
- `min_length`/`max_length`: For strings
|
||||
- `ge`/`gt`/`le`/`lt`: For numbers (greater/less than or equal/than)
|
||||
- `pattern`: For regex pattern matching
|
||||
|
||||
For more on validation, see the [Field Validation](../patterns/field_validation.md) and [Validation Basics](../validation/basics.md) guides.
|
||||
|
||||
## Nested Models
|
||||
|
||||
You can create complex data structures with nested models:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
from typing import List, Optional
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: Optional[str] = None
|
||||
country: str
|
||||
|
||||
class User(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
addresses: List[Address]
|
||||
```
|
||||
|
||||
This allows you to extract hierarchical data structures. For more examples, check out the [Simple Nested Structure](../patterns/nested_structure.md) guide.
|
||||
|
||||
## Using Enums
|
||||
|
||||
Enums help when you want to restrict a field to a set of specific values:
|
||||
|
||||
```python
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel
|
||||
|
||||
class UserType(str, Enum):
|
||||
ADMIN = "admin"
|
||||
REGULAR = "regular"
|
||||
GUEST = "guest"
|
||||
|
||||
class User(BaseModel):
|
||||
name: str
|
||||
user_type: UserType
|
||||
```
|
||||
|
||||
## Optional Fields
|
||||
|
||||
For fields that might not be present in the source text:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
class Contact(BaseModel):
|
||||
name: str
|
||||
email: str
|
||||
phone: Optional[str] = None
|
||||
address: Optional[str] = None
|
||||
```
|
||||
|
||||
For more about working with optional fields, see the [Optional Fields](../patterns/optional_fields.md) guide.
|
||||
|
||||
## Lists and Arrays
|
||||
|
||||
To extract multiple items of the same type:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
|
||||
class BlogPost(BaseModel):
|
||||
title: str
|
||||
content: str
|
||||
tags: List[str]
|
||||
```
|
||||
|
||||
For more about working with lists, see the [List Extraction](../patterns/list_extraction.md) guide.
|
||||
|
||||
## Using Your Models with Instructor
|
||||
|
||||
Once you've defined your model, you can use it for extraction:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
forecast = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=WeatherForecast,
|
||||
messages=[
|
||||
{"role": "user", "content": "What's the weather in New York today?"}
|
||||
]
|
||||
)
|
||||
|
||||
print(forecast.model_dump_json(indent=2))
|
||||
```
|
||||
|
||||
## Model Documentation
|
||||
|
||||
You can add documentation to your models using docstrings and field descriptions:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Investment(BaseModel):
|
||||
"""Represents an investment opportunity with risk and return details."""
|
||||
|
||||
name: str = Field(description="Name of the investment")
|
||||
amount: float = Field(description="Investment amount in USD")
|
||||
expected_return: float = Field(description="Expected annual return percentage")
|
||||
risk_level: str = Field(description="Risk level (low, medium, high)")
|
||||
```
|
||||
|
||||
This documentation helps both the LLM understand what to extract and makes your code more maintainable.
|
||||
|
||||
## Advanced Validation with Validators
|
||||
|
||||
For more complex validation rules, you can use validator methods:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
from datetime import date
|
||||
|
||||
class Reservation(BaseModel):
|
||||
check_in: date
|
||||
check_out: date
|
||||
guests: int = Field(ge=1)
|
||||
|
||||
@field_validator("check_out")
|
||||
def check_dates(cls, v, values):
|
||||
if "check_in" in values.data and v <= values.data["check_in"]:
|
||||
raise ValueError("check_out must be after check_in")
|
||||
return v
|
||||
```
|
||||
|
||||
For more advanced validation techniques, check out the [Custom Validators](../validation/custom_validators.md) guide.
|
||||
|
||||
## Next Steps
|
||||
|
||||
In the next section, learn about [from_provider](../../concepts/from_provider.md) to configure different LLM providers and understand the various modes of operation.
|
||||
@@ -0,0 +1,148 @@
|
||||
---
|
||||
title: Getting Started with Structured LLM Outputs
|
||||
description: Learn the basics of extracting structured data from language models using Instructor. Understand the difference between unstructured and structured outputs.
|
||||
---
|
||||
|
||||
# Getting Started with Structured Outputs
|
||||
|
||||
Large language models (LLMs) are powerful tools for generating text, but extracting structured data from their outputs can be challenging. Structured outputs solve this problem by having LLMs return data in consistent, machine-readable formats.
|
||||
|
||||
## The Problem with Unstructured Outputs
|
||||
|
||||
Let's look at what happens when we ask an LLM to extract information without any structure:
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
|
||||
client = OpenAI()
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Extract customer: John Doe, age 35, email: john@example.com",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
```
|
||||
|
||||
The output might look like:
|
||||
```
|
||||
Customer Name: John Doe
|
||||
Age: 35
|
||||
Email: john@example.com
|
||||
```
|
||||
|
||||
Or it could be:
|
||||
```
|
||||
I found the following customer information:
|
||||
- Name: John Doe
|
||||
- Age: 35
|
||||
- Email address: john@example.com
|
||||
```
|
||||
|
||||
This inconsistency makes it difficult to reliably parse the information in downstream applications.
|
||||
|
||||
## The Solution: Structured Outputs with Instructor
|
||||
|
||||
Instructor solves this problem by using Pydantic models to define the expected structure of the output:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from pydantic import BaseModel, Field, EmailStr
|
||||
class Customer(BaseModel):
|
||||
name: str = Field(description="Customer's full name")
|
||||
age: int = Field(description="Customer's age in years", ge=0, le=120)
|
||||
email: EmailStr = Field(description="Customer's email address")
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
customer = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Extract customer: John Doe, age 35, email: john@example.com",
|
||||
}
|
||||
],
|
||||
response_model=Customer, # This is the key part
|
||||
)
|
||||
|
||||
print(customer) # Customer(name='John Doe', age=35, email='john@example.com')
|
||||
print(f"Name: {customer.name}, Age: {customer.age}, Email: {customer.email}")
|
||||
```
|
||||
|
||||
The benefits of this approach include:
|
||||
|
||||
1. **Consistency**: Always get data in the same format
|
||||
2. **Validation**: Age must be between 0 and 120, email must be valid
|
||||
3. **Type Safety**: `age` is always an integer, not a string
|
||||
4. **Documentation**: Model fields are self-documenting with descriptions
|
||||
|
||||
## Complex Example: Nested Structures
|
||||
|
||||
Instructor shines with complex data structures:
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: str
|
||||
zip_code: str
|
||||
|
||||
class Contact(BaseModel):
|
||||
email: Optional[str] = None
|
||||
phone: Optional[str] = None
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
occupation: str
|
||||
address: Address
|
||||
contact: Contact
|
||||
skills: List[str] = Field(description="List of professional skills")
|
||||
|
||||
person = client.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": """
|
||||
Extract detailed information for this person:
|
||||
John Smith is a 42-year-old software engineer living at 123 Main St, San Francisco, CA 94105.
|
||||
His email is john.smith@example.com and phone is 555-123-4567.
|
||||
John is skilled in Python, JavaScript, and cloud architecture.
|
||||
""",
|
||||
}
|
||||
],
|
||||
response_model=Person,
|
||||
)
|
||||
|
||||
print(f"Name: {person.name}")
|
||||
print(f"Location: {person.address.city}, {person.address.state}")
|
||||
print(f"Skills: {', '.join(person.skills)}")
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
To get started with Instructor, install it via pip:
|
||||
|
||||
```shell
|
||||
pip install instructor pydantic
|
||||
```
|
||||
|
||||
You'll also need to set up your API keys for the LLM provider you're using.
|
||||
|
||||
## Next Steps
|
||||
|
||||
In the next sections, you'll learn how to:
|
||||
|
||||
1. Create your [first extraction](first_extraction.md)
|
||||
2. Understand the different [response models](response_models.md) you can create
|
||||
3. Set up [clients for various LLM providers](../../concepts/from_provider.md)
|
||||
52
참고/instructor-main/docs/learning/index.md
Normal file
52
참고/instructor-main/docs/learning/index.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# Instructor LLM Tutorial: Complete Guide to Structured Outputs
|
||||
|
||||
Learn how to use Instructor for LLM structured outputs with this comprehensive tutorial. Instructor is the leading Python library for extracting structured, validated data from large language models (LLMs) like GPT-4, Claude, and Gemini.
|
||||
|
||||
## What You'll Learn in This LLM Tutorial
|
||||
|
||||
This Instructor tutorial covers everything from basic LLM integration to advanced structured output patterns. Whether you're building AI applications, automating data extraction, or creating LLM-powered APIs, this guide provides practical, production-ready examples.
|
||||
|
||||
## Getting Started with Instructor LLM Tutorial
|
||||
|
||||
Start your journey with these beginner-friendly tutorials for LLM integration:
|
||||
|
||||
* [**Installation Guide**](getting_started/installation.md) - Install Instructor for Python LLM development
|
||||
* [**Your First LLM Extraction**](getting_started/first_extraction.md) - Build your first structured output with OpenAI, Anthropic, or Google LLMs
|
||||
* [**Response Models Tutorial**](getting_started/response_models.md) - Master Pydantic models for LLM outputs
|
||||
* [**LLM Client Setup**](../concepts/from_provider.md) - Configure Instructor for OpenAI, Anthropic, Gemini, and 15+ LLM providers
|
||||
|
||||
## LLM Data Extraction Patterns
|
||||
|
||||
Learn essential patterns for extracting structured data from language models:
|
||||
|
||||
* [**Simple Object Extraction**](patterns/simple_object.md) - Extract structured objects from LLM responses
|
||||
* [**List Extraction Tutorial**](patterns/list_extraction.md) - Generate lists and arrays with LLMs
|
||||
* [**Nested Data Structures**](patterns/nested_structure.md) - Handle complex, hierarchical LLM outputs
|
||||
* [**Optional Fields**](patterns/optional_fields.md) - Manage missing data in LLM responses
|
||||
* [**Field Validation**](patterns/field_validation.md) - Validate LLM outputs with Pydantic
|
||||
* [**Prompt Engineering Templates**](patterns/prompt_templates.md) - Optimize prompts for better LLM extraction
|
||||
|
||||
## LLM Output Validation Tutorial
|
||||
|
||||
Ensure reliability with these validation tutorials:
|
||||
|
||||
* [**Validation Fundamentals**](validation/basics.md) - Core concepts for validating LLM outputs
|
||||
* [**Field-Level Validation**](validation/field_level_validation.md) - Granular validation for LLM data
|
||||
* [**Custom Validators**](validation/custom_validators.md) - Build domain-specific LLM validators
|
||||
* [**Retry Strategies**](validation/retry_mechanisms.md) - Handle LLM failures gracefully
|
||||
|
||||
## Streaming LLM Responses
|
||||
|
||||
Real-time LLM output processing tutorials:
|
||||
|
||||
* [**Streaming Basics**](streaming/basics.md) - Stream LLM responses for better UX
|
||||
* [**Streaming Lists**](streaming/lists.md) - Process LLM arrays in real-time
|
||||
|
||||
## Why This Instructor LLM Tutorial?
|
||||
|
||||
- **Production-Ready Examples**: Real-world LLM integration patterns used by thousands of developers
|
||||
- **Multi-Provider Support**: Works with OpenAI, Anthropic, Google, Cohere, and more
|
||||
- **Type-Safe Outputs**: Leverage Python's type system for reliable LLM applications
|
||||
- **Progressive Learning Path**: From basic LLM calls to advanced extraction techniques
|
||||
|
||||
Ready to master structured outputs with LLMs? Start with our [installation guide](getting_started/installation.md) and build your first LLM-powered application today!
|
||||
385
참고/instructor-main/docs/learning/patterns/field_validation.md
Normal file
385
참고/instructor-main/docs/learning/patterns/field_validation.md
Normal file
@@ -0,0 +1,385 @@
|
||||
# Field Validation
|
||||
|
||||
This guide covers how to add validation to fields when extracting structured data with Instructor. Field validation ensures that your extracted data meets specific criteria and constraints.
|
||||
|
||||
## Why Field Validation Matters
|
||||
|
||||
Field validation helps you:
|
||||
|
||||
1. Ensure data quality and consistency
|
||||
2. Enforce business rules
|
||||
3. Prevent errors in downstream processing
|
||||
4. Provide clear feedback for invalid data
|
||||
|
||||
Instructor uses Pydantic's validation system, which is applied automatically during extraction.
|
||||
|
||||
## Basic Field Constraints
|
||||
|
||||
You can add basic constraints to fields using Pydantic's `Field` function:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class User(BaseModel):
|
||||
name: str = Field(..., min_length=2, max_length=50)
|
||||
age: int = Field(..., ge=0, le=120) # greater than or equal to 0, less than or equal to 120
|
||||
email: str = Field(..., pattern=r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$')
|
||||
|
||||
# Extract with validation
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "I'm John Smith, 35 years old, with email john@example.com"}
|
||||
],
|
||||
response_model=User
|
||||
)
|
||||
```
|
||||
|
||||
Common Field constraints include:
|
||||
|
||||
| Constraint | Description | Example |
|
||||
|------------|-------------|---------|
|
||||
| `min_length` | Minimum string length | `min_length=2` |
|
||||
| `max_length` | Maximum string length | `max_length=50` |
|
||||
| `pattern` | Regex pattern to match | `pattern=r'^[0-9]+$'` |
|
||||
| `gt` | Greater than | `gt=0` (for numbers) |
|
||||
| `ge` | Greater than or equal | `ge=18` |
|
||||
| `lt` | Less than | `lt=100` |
|
||||
| `le` | Less than or equal | `le=120` |
|
||||
| `min_items` | Minimum list items | `min_items=1` |
|
||||
| `max_items` | Maximum list items | `max_items=10` |
|
||||
|
||||
For more information on field definitions, see the [Fields](../../concepts/fields.md) concepts page.
|
||||
|
||||
## Validation with Field Validators
|
||||
|
||||
For more complex validation logic, use Pydantic's `field_validator` decorator:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
import re
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
sku: str
|
||||
price: float
|
||||
|
||||
@field_validator('name')
|
||||
@classmethod
|
||||
def validate_name(cls, v):
|
||||
if len(v.strip()) < 3:
|
||||
raise ValueError("Product name must be at least 3 characters")
|
||||
return v.strip()
|
||||
|
||||
@field_validator('sku')
|
||||
@classmethod
|
||||
def validate_sku(cls, v):
|
||||
if not re.match(r'^[A-Z]{3}-\d{4}$', v):
|
||||
raise ValueError("SKU must be in format XXX-0000")
|
||||
return v
|
||||
|
||||
@field_validator('price')
|
||||
@classmethod
|
||||
def validate_price(cls, v):
|
||||
if v <= 0:
|
||||
raise ValueError("Price must be greater than zero")
|
||||
if v > 10000:
|
||||
raise ValueError("Price exceeds maximum allowed value")
|
||||
return v
|
||||
|
||||
# Extract validated data
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Product: Wireless Headphones, SKU: ABC-1234, Price: $79.99"}
|
||||
],
|
||||
response_model=Product
|
||||
)
|
||||
```
|
||||
|
||||
Field validators can:
|
||||
- Perform complex validation logic
|
||||
- Clean and normalize data
|
||||
- Transform values
|
||||
- Check values against external data sources
|
||||
|
||||
For more on custom validators, see the [Custom Validators](../validation/custom_validators.md) guide.
|
||||
|
||||
## Model-level Validation
|
||||
|
||||
Sometimes validation needs to check relationships between fields. For this, use `model_validator`:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
from datetime import date
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class DateRange(BaseModel):
|
||||
start_date: date
|
||||
end_date: date
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_date_range(self):
|
||||
if self.end_date < self.start_date:
|
||||
raise ValueError("End date must be after start date")
|
||||
return self
|
||||
```
|
||||
|
||||
## Validation in Nested Structures
|
||||
|
||||
You can apply validation at any level in nested structures:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
from typing import List
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: str
|
||||
zip_code: str
|
||||
|
||||
@field_validator('state')
|
||||
@classmethod
|
||||
def validate_state(cls, v):
|
||||
valid_states = {"CA", "NY", "TX", "FL"} # Example: just a few states
|
||||
if v not in valid_states:
|
||||
raise ValueError(f"State must be one of: {', '.join(valid_states)}")
|
||||
return v
|
||||
|
||||
@field_validator('zip_code')
|
||||
@classmethod
|
||||
def validate_zip(cls, v):
|
||||
if not v.isdigit() or len(v) != 5:
|
||||
raise ValueError("ZIP code must be 5 digits")
|
||||
return v
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
addresses: List[Address] # Nested structure with validation
|
||||
```
|
||||
|
||||
For more on nested structures, see the [Nested Structure](nested_structure.md) guide.
|
||||
|
||||
## List Item Validation
|
||||
|
||||
You can validate items in a list:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class TagList(BaseModel):
|
||||
tags: List[str] = Field(..., min_items=1, max_items=5)
|
||||
|
||||
@field_validator('tags')
|
||||
@classmethod
|
||||
def validate_tags(cls, tags):
|
||||
# Convert all tags to lowercase
|
||||
tags = [tag.lower() for tag in tags]
|
||||
|
||||
# Check for minimum length of each tag
|
||||
for tag in tags:
|
||||
if len(tag) < 2:
|
||||
raise ValueError("Each tag must be at least 2 characters")
|
||||
|
||||
# Check for duplicates
|
||||
if len(tags) != len(set(tags)):
|
||||
raise ValueError("Tags must be unique")
|
||||
|
||||
return tags
|
||||
```
|
||||
|
||||
For more on lists, see the [List Extraction](list_extraction.md) guide.
|
||||
|
||||
## Using Enumerations for Validation
|
||||
|
||||
Enums provide a way to validate fields against a predefined set of values:
|
||||
|
||||
```python
|
||||
from enum import Enum
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Status(str, Enum):
|
||||
PENDING = "pending"
|
||||
APPROVED = "approved"
|
||||
REJECTED = "rejected"
|
||||
|
||||
class Priority(str, Enum):
|
||||
LOW = "low"
|
||||
MEDIUM = "medium"
|
||||
HIGH = "high"
|
||||
|
||||
class Task(BaseModel):
|
||||
title: str
|
||||
description: str
|
||||
status: Status # Must be one of the enum values
|
||||
priority: Priority # Must be one of the enum values
|
||||
|
||||
# Extract with enum validation
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Task: Update website, Description: Refresh content on homepage, Status: pending, Priority: high"}
|
||||
],
|
||||
response_model=Task
|
||||
)
|
||||
```
|
||||
|
||||
For more information on enums, see the [Enums](../../concepts/enums.md) concepts page.
|
||||
|
||||
## Custom Error Messages
|
||||
|
||||
You can customize validation error messages for better feedback:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class CreditCard(BaseModel):
|
||||
number: str = Field(
|
||||
...,
|
||||
pattern=r'^\d{16}$',
|
||||
json_schema_extra={"error_msg": "Credit card number must be exactly 16 digits"}
|
||||
)
|
||||
expiry_month: int = Field(
|
||||
...,
|
||||
ge=1,
|
||||
le=12,
|
||||
json_schema_extra={"error_msg": "Expiry month must be between 1 and 12"}
|
||||
)
|
||||
expiry_year: int = Field(
|
||||
...,
|
||||
ge=2023,
|
||||
le=2030,
|
||||
json_schema_extra={"error_msg": "Expiry year must be between 2023 and 2030"}
|
||||
)
|
||||
cvv: str = Field(
|
||||
...,
|
||||
pattern=r'^\d{3,4}$',
|
||||
json_schema_extra={"error_msg": "CVV must be 3 or 4 digits"}
|
||||
)
|
||||
```
|
||||
|
||||
## Handling Validation Failures
|
||||
|
||||
When validation fails, Instructor will:
|
||||
|
||||
1. Capture the validation error
|
||||
2. Add the error message to the context
|
||||
3. Retry the request with this feedback (if retries are enabled)
|
||||
|
||||
To control retry behavior:
|
||||
|
||||
```python
|
||||
client = instructor.from_provider(
|
||||
"openai/gpt-4o",
|
||||
max_retries=2, # Number of retries after the initial attempt
|
||||
throw_error=True # Whether to raise an exception on validation failure
|
||||
)
|
||||
```
|
||||
|
||||
For more on retries, see the [Retry Mechanisms](../validation/retry_mechanisms.md) guide.
|
||||
|
||||
## Real-world Example: Form Data Validation
|
||||
|
||||
Here's a more complete example validating form inputs:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
import instructor
|
||||
import re
|
||||
from datetime import date, datetime
|
||||
from typing import Optional
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class RegistrationForm(BaseModel):
|
||||
username: str = Field(..., min_length=3, max_length=20)
|
||||
email: str
|
||||
password: str
|
||||
confirm_password: str
|
||||
birth_date: date
|
||||
|
||||
@field_validator('username')
|
||||
@classmethod
|
||||
def validate_username(cls, v):
|
||||
if not re.match(r'^[a-zA-Z0-9_]+$', v):
|
||||
raise ValueError("Username can only contain letters, numbers, and underscores")
|
||||
return v
|
||||
|
||||
@field_validator('email')
|
||||
@classmethod
|
||||
def validate_email(cls, v):
|
||||
if not re.match(r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$', v):
|
||||
raise ValueError("Invalid email format")
|
||||
return v
|
||||
|
||||
@field_validator('password')
|
||||
@classmethod
|
||||
def validate_password(cls, v):
|
||||
if len(v) < 8:
|
||||
raise ValueError("Password must be at least 8 characters")
|
||||
if not re.search(r'[A-Z]', v):
|
||||
raise ValueError("Password must contain at least one uppercase letter")
|
||||
if not re.search(r'[a-z]', v):
|
||||
raise ValueError("Password must contain at least one lowercase letter")
|
||||
if not re.search(r'[0-9]', v):
|
||||
raise ValueError("Password must contain at least one number")
|
||||
return v
|
||||
|
||||
@field_validator('birth_date')
|
||||
@classmethod
|
||||
def validate_age(cls, v):
|
||||
today = date.today()
|
||||
age = today.year - v.year - ((today.month, today.day) < (v.month, v.day))
|
||||
if age < 18:
|
||||
raise ValueError("You must be at least 18 years old to register")
|
||||
return v
|
||||
|
||||
@model_validator(mode='after')
|
||||
def passwords_match(self):
|
||||
if self.password != self.confirm_password:
|
||||
raise ValueError("Passwords do not match")
|
||||
return self
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Validation Basics](../validation/basics.md) - Core validation concepts
|
||||
- [Custom Validators](../validation/custom_validators.md) - Creating custom validation logic
|
||||
- [Field-level Validation](../validation/field_level_validation.md) - Advanced field validation
|
||||
- [Retry Mechanisms](../validation/retry_mechanisms.md) - Handling validation failures
|
||||
- [Fields](../../concepts/fields.md) - Understanding field definitions
|
||||
- [Enums](../../concepts/enums.md) - Using enumeration types
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Optional Fields](optional_fields.md) for handling missing data
|
||||
- Explore [Custom Validators](../validation/custom_validators.md) for complex validation
|
||||
- Check out [Nested Structure](nested_structure.md) for complex data relationships
|
||||
291
참고/instructor-main/docs/learning/patterns/list_extraction.md
Normal file
291
참고/instructor-main/docs/learning/patterns/list_extraction.md
Normal file
@@ -0,0 +1,291 @@
|
||||
---
|
||||
title: List Extraction from LLMs Tutorial
|
||||
description: Master extracting multiple structured objects from language models using Instructor with type-safe list validation.
|
||||
---
|
||||
|
||||
# List Extraction Tutorial: Extract Multiple Objects from LLMs
|
||||
|
||||
Master the art of extracting lists and arrays from LLMs in this comprehensive tutorial. Learn how to use Instructor to extract multiple structured objects from language models like GPT-4, Claude, and Gemini with type-safe validation.
|
||||
|
||||
## Basic List Extraction
|
||||
|
||||
To extract a list of items, you define a model for a single item and then use Python's typing system to specify you want a list of that type:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
# Initialize the client
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
# Define a single item model
|
||||
class Person(BaseModel):
|
||||
name: str = Field(..., description="The person's full name")
|
||||
age: int = Field(..., description="The person's age in years")
|
||||
|
||||
# Define a wrapper model for the list
|
||||
class PeopleList(BaseModel):
|
||||
people: List[Person] = Field(..., description="List of people mentioned in the text")
|
||||
|
||||
# Extract the list
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Here's information about some people:
|
||||
- John Smith is 35 years old
|
||||
- Mary Johnson is 28 years old
|
||||
- Robert Davis is 42 years old
|
||||
"""}
|
||||
],
|
||||
response_model=PeopleList
|
||||
)
|
||||
|
||||
# Access the extracted data
|
||||
for i, person in enumerate(response.people):
|
||||
print(f"Person {i+1}: {person.name}, {person.age} years old")
|
||||
```
|
||||
|
||||
This example shows how to:
|
||||
1. Define a model for a single item (`Person`)
|
||||
2. Create a wrapper model that contains a list of items (`PeopleList`)
|
||||
3. Access each item in the list through the response
|
||||
|
||||
## Direct List Extraction
|
||||
|
||||
You can also extract a list directly without a wrapper model:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Book(BaseModel):
|
||||
title: str
|
||||
author: str
|
||||
publication_year: int
|
||||
|
||||
# Extract a list directly
|
||||
books = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Classic novels:
|
||||
1. To Kill a Mockingbird by Harper Lee (1960)
|
||||
2. 1984 by George Orwell (1949)
|
||||
3. The Great Gatsby by F. Scott Fitzgerald (1925)
|
||||
"""}
|
||||
],
|
||||
response_model=List[Book] # Direct list extraction
|
||||
)
|
||||
|
||||
# Access the extracted data
|
||||
for book in books:
|
||||
print(f"{book.title} by {book.author} ({book.publication_year})")
|
||||
```
|
||||
|
||||
## Nested Lists
|
||||
|
||||
You can extract nested lists by combining list types:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Author(BaseModel):
|
||||
name: str
|
||||
nationality: str
|
||||
|
||||
class Book(BaseModel):
|
||||
title: str
|
||||
authors: List[Author] # Nested list of authors
|
||||
publication_year: int
|
||||
|
||||
# Extract data with nested lists
|
||||
books = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Book 1: "Good Omens" (1990)
|
||||
Authors: Terry Pratchett (British), Neil Gaiman (British)
|
||||
|
||||
Book 2: "The Talisman" (1984)
|
||||
Authors: Stephen King (American), Peter Straub (American)
|
||||
"""}
|
||||
],
|
||||
response_model=List[Book]
|
||||
)
|
||||
|
||||
# Access the nested data
|
||||
for book in books:
|
||||
author_names = ", ".join([author.name for author in book.authors])
|
||||
print(f"{book.title} ({book.publication_year}) by {author_names}")
|
||||
```
|
||||
|
||||
## Using Streaming with Lists
|
||||
|
||||
You can stream list extraction results using Instructor's streaming capabilities:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
import instructor
|
||||
from pydantic import BaseModel, Field
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Task(BaseModel):
|
||||
description: str
|
||||
priority: str
|
||||
deadline: str
|
||||
|
||||
# Stream a list of tasks
|
||||
for task in client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Generate a list of 5 sample tasks for a project manager"}
|
||||
],
|
||||
response_model=List[Task],
|
||||
stream=True
|
||||
):
|
||||
print(f"Received task: {task.description} (Priority: {task.priority}, Deadline: {task.deadline})")
|
||||
```
|
||||
|
||||
For more information on streaming, see the [Streaming Basics](../streaming/basics.md) and [Streaming Lists](../streaming/lists.md) guides.
|
||||
|
||||
## List Validation
|
||||
|
||||
You can add validation for both individual items and the entire list:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
|
||||
@field_validator('price')
|
||||
@classmethod
|
||||
def validate_price(cls, v):
|
||||
if v <= 0:
|
||||
raise ValueError("Price must be greater than zero")
|
||||
return v
|
||||
|
||||
class ProductList(BaseModel):
|
||||
products: List[Product] = Field(..., min_items=1)
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_unique_names(self):
|
||||
names = [p.name for p in self.products]
|
||||
if len(names) != len(set(names)):
|
||||
raise ValueError("All product names must be unique")
|
||||
return self
|
||||
|
||||
# Extract list with validation
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "List of products: Headphones ($50), Speakers ($80), Earbuds ($30)"}
|
||||
],
|
||||
response_model=ProductList
|
||||
)
|
||||
```
|
||||
|
||||
For more on validation, see [Field Validation](./field_validation.md) and [Validation Basics](../validation/basics.md).
|
||||
|
||||
## List Constraints
|
||||
|
||||
You can add constraints to lists using Pydantic's Field:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Ingredient(BaseModel):
|
||||
name: str
|
||||
amount: str
|
||||
|
||||
class Recipe(BaseModel):
|
||||
title: str
|
||||
ingredients: List[Ingredient] = Field(
|
||||
...,
|
||||
min_items=2, # Minimum 2 ingredients
|
||||
max_items=10, # Maximum 10 ingredients
|
||||
description="List of ingredients needed for the recipe"
|
||||
)
|
||||
steps: List[str] = Field(
|
||||
...,
|
||||
min_items=1,
|
||||
description="Step-by-step instructions to prepare the recipe"
|
||||
)
|
||||
```
|
||||
|
||||
## Real-world Example: Task Extraction
|
||||
|
||||
Here's a more complete example for extracting a list of tasks from a meeting transcript:
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
from datetime import date
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Assignee(BaseModel):
|
||||
name: str
|
||||
email: Optional[str] = None
|
||||
|
||||
class ActionItem(BaseModel):
|
||||
description: str = Field(..., description="The task that needs to be completed")
|
||||
assignee: Assignee = Field(..., description="The person responsible for the task")
|
||||
due_date: Optional[date] = Field(None, description="The deadline for the task")
|
||||
priority: str = Field(..., description="Priority level: Low, Medium, or High")
|
||||
|
||||
# Extract action items from meeting notes
|
||||
action_items = client.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Meeting Notes - Project Kickoff
|
||||
Date: 2023-05-15
|
||||
|
||||
Attendees: John (john@example.com), Sarah (sarah@example.com), Mike
|
||||
|
||||
Discussion points:
|
||||
1. John will prepare the project timeline by next Friday. This is high priority.
|
||||
2. Sarah needs to contact the client for requirements clarification by Wednesday. Medium priority.
|
||||
3. Mike is responsible for setting up the development environment. Due by tomorrow, high priority.
|
||||
"""}
|
||||
],
|
||||
response_model=List[ActionItem]
|
||||
)
|
||||
|
||||
# Process the extracted action items
|
||||
for item in action_items:
|
||||
due_str = item.due_date.isoformat() if item.due_date else "Not specified"
|
||||
print(f"Task: {item.description}")
|
||||
print(f"Assignee: {item.assignee.name} ({item.assignee.email or 'No email'})")
|
||||
print(f"Due: {due_str}, Priority: {item.priority}")
|
||||
print("---")
|
||||
```
|
||||
|
||||
For a more detailed example, see the [Action Items Extraction](../../examples/action_items.md) example.
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Simple Object Extraction](./simple_object.md) - Extracting single objects
|
||||
- [Nested Structure](./nested_structure.md) - Working with complex nested data
|
||||
- [Streaming Lists](../streaming/lists.md) - Streaming list results
|
||||
- [Lists and Arrays](../../concepts/lists.md) - Concepts related to list extraction
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Nested Structure](./nested_structure.md) for complex data
|
||||
- Explore [Streaming Lists](../streaming/lists.md) for handling large lists
|
||||
- Check out [Field Validation](./field_validation.md) for validation techniques
|
||||
357
참고/instructor-main/docs/learning/patterns/nested_structure.md
Normal file
357
참고/instructor-main/docs/learning/patterns/nested_structure.md
Normal file
@@ -0,0 +1,357 @@
|
||||
---
|
||||
title: Nested Structure Extraction with Instructor
|
||||
description: Learn how to extract complex nested data structures from LLMs using hierarchical Pydantic models.
|
||||
---
|
||||
|
||||
# Simple Nested Structure
|
||||
|
||||
This guide explains how to extract nested structured data using Instructor. Nested structures allow you to represent complex, hierarchical data relationships.
|
||||
|
||||
## Understanding Nested Structures
|
||||
|
||||
Nested structures are objects that contain other objects as fields. They're useful for representing:
|
||||
|
||||
1. Parent-child relationships
|
||||
2. Complex entities with sub-components
|
||||
3. Hierarchical data
|
||||
4. Related data that belongs together
|
||||
|
||||
## Basic Nested Structure Example
|
||||
|
||||
Here's a simple example of extracting a nested structure:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
from typing import List, Optional
|
||||
# Initialize the client
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
# Define nested models
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: str
|
||||
zip_code: str
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
address: Address # Nested structure
|
||||
|
||||
# Extract the nested data
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
John Smith is 35 years old.
|
||||
He lives at 123 Main Street, Boston, MA 02108.
|
||||
"""}
|
||||
],
|
||||
response_model=Person
|
||||
)
|
||||
|
||||
# Access the nested data
|
||||
print(f"Name: {response.name}")
|
||||
print(f"Age: {response.age}")
|
||||
print(f"Address: {response.address.street}, {response.address.city}, "
|
||||
f"{response.address.state} {response.address.zip_code}")
|
||||
```
|
||||
|
||||
## Multiple Levels of Nesting
|
||||
|
||||
You can use multiple levels of nesting for more complex structures:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
from typing import List, Optional
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class EmployeeDetails(BaseModel):
|
||||
department: str
|
||||
position: str
|
||||
start_date: str
|
||||
|
||||
class ContactInfo(BaseModel):
|
||||
phone: str
|
||||
email: str
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: str
|
||||
zip_code: str
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
contact: ContactInfo # First level nesting
|
||||
address: Address # First level nesting
|
||||
employment: Optional[EmployeeDetails] = None # Optional nested structure
|
||||
|
||||
# Extract deeply nested data
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Employee Profile:
|
||||
Name: Jane Doe
|
||||
Age: 32
|
||||
Phone: (555) 123-4567
|
||||
Email: jane.doe@example.com
|
||||
Address: 456 Oak Avenue, Chicago, IL 60601
|
||||
Department: Engineering
|
||||
Position: Senior Developer
|
||||
Start Date: 2021-03-15
|
||||
"""}
|
||||
],
|
||||
response_model=Person
|
||||
)
|
||||
```
|
||||
|
||||
## Nested Lists
|
||||
|
||||
You can combine nesting with lists to represent complex collections:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
from typing import List
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Ingredient(BaseModel):
|
||||
name: str
|
||||
amount: str
|
||||
unit: str
|
||||
|
||||
class Recipe(BaseModel):
|
||||
title: str
|
||||
description: str
|
||||
ingredients: List[Ingredient] # Nested list of ingredients
|
||||
steps: List[str] # List of strings
|
||||
|
||||
# Extract nested list data
|
||||
response = client.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Recipe: Chocolate Chip Cookies
|
||||
|
||||
Description: Classic homemade chocolate chip cookies that are soft in the middle and crispy on the edges.
|
||||
|
||||
Ingredients:
|
||||
- 2 1/4 cups all-purpose flour
|
||||
- 1 teaspoon baking soda
|
||||
- 1 teaspoon salt
|
||||
- 1 cup butter
|
||||
- 3/4 cup white sugar
|
||||
- 3/4 cup brown sugar
|
||||
- 2 eggs
|
||||
- 2 teaspoons vanilla extract
|
||||
- 2 cups chocolate chips
|
||||
|
||||
Instructions:
|
||||
1. Preheat oven to 375°F (190°C)
|
||||
2. Mix flour, baking soda, and salt
|
||||
3. Cream butter and sugars, then add eggs and vanilla
|
||||
4. Gradually add dry ingredients
|
||||
5. Stir in chocolate chips
|
||||
6. Drop by rounded tablespoons onto ungreased baking sheets
|
||||
7. Bake for 9 to 11 minutes or until golden brown
|
||||
8. Cool on wire racks
|
||||
"""}
|
||||
],
|
||||
response_model=Recipe
|
||||
)
|
||||
```
|
||||
|
||||
For more information on working with lists, see the [List Extraction](list_extraction.md) guide.
|
||||
|
||||
## Handling Optional Nested Fields
|
||||
|
||||
Sometimes parts of a nested structure might be missing. Use Optional to handle this:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
from typing import Optional
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class SocialMedia(BaseModel):
|
||||
twitter: Optional[str] = None
|
||||
linkedin: Optional[str] = None
|
||||
instagram: Optional[str] = None
|
||||
|
||||
class ContactInfo(BaseModel):
|
||||
email: str
|
||||
phone: Optional[str] = None
|
||||
social: Optional[SocialMedia] = None # Optional nested structure
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
contact: ContactInfo
|
||||
```
|
||||
|
||||
For more information on optional fields, see the [Optional Fields](optional_fields.md) guide.
|
||||
|
||||
## Nested Structure Validation
|
||||
|
||||
You can add validation to nested structures at any level:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field, field_validator, model_validator
|
||||
import instructor
|
||||
import re
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class EmailContact(BaseModel):
|
||||
email: str
|
||||
|
||||
@field_validator('email')
|
||||
@classmethod
|
||||
def validate_email(cls, v):
|
||||
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||
if not re.match(pattern, v):
|
||||
raise ValueError("Invalid email format")
|
||||
return v
|
||||
|
||||
class Customer(BaseModel):
|
||||
name: str
|
||||
contact: EmailContact # Nested structure with its own validation
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_name_email_match(self):
|
||||
name_part = self.name.lower().split()[0]
|
||||
if name_part not in self.contact.email.lower():
|
||||
print(f"Warning: Email {self.contact.email} may not match name {self.name}")
|
||||
return self
|
||||
```
|
||||
|
||||
For more on validation, see [Field Validation](field_validation.md) and [Validation Basics](../validation/basics.md).
|
||||
|
||||
## Working with Recursive Structures
|
||||
|
||||
For more complex hierarchical data, you can use recursive structures:
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Comment(BaseModel):
|
||||
text: str
|
||||
author: str
|
||||
replies: List["Comment"] = [] # Recursive structure
|
||||
|
||||
# Update the Comment class reference for Pydantic
|
||||
Comment.model_rebuild()
|
||||
|
||||
class Post(BaseModel):
|
||||
title: str
|
||||
content: str
|
||||
author: str
|
||||
comments: List[Comment] = []
|
||||
|
||||
# Extract recursive nested data
|
||||
response = client.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Blog Post: "Python Tips and Tricks"
|
||||
Author: John Smith
|
||||
Content: Here are some helpful Python tips for beginners...
|
||||
|
||||
Comments:
|
||||
1. Alice: "Great post! Very helpful."
|
||||
- Bob: "I agree, I learned a lot."
|
||||
- Alice: "Bob, did you try the last example?"
|
||||
- Charlie: "Thanks for sharing this."
|
||||
2. David: "Could you explain the second tip more?"
|
||||
- John: "Sure, I'll add more details."
|
||||
"""}
|
||||
],
|
||||
response_model=Post
|
||||
)
|
||||
```
|
||||
|
||||
For more advanced recursive structures, see the [Recursive Structures](../../examples/recursive.md) guide.
|
||||
|
||||
## Real-world Example: Organization Structure
|
||||
|
||||
Here's a more complete example extracting an organization structure:
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Employee(BaseModel):
|
||||
name: str
|
||||
title: str
|
||||
|
||||
class Department(BaseModel):
|
||||
name: str
|
||||
head: Employee
|
||||
employees: List[Employee]
|
||||
sub_departments: List["Department"] = []
|
||||
|
||||
# Update for Pydantic's recursive model support
|
||||
Department.model_rebuild()
|
||||
|
||||
class Organization(BaseModel):
|
||||
name: str
|
||||
ceo: Employee
|
||||
departments: List[Department]
|
||||
|
||||
# Extract organization structure
|
||||
response = client.create(
|
||||
model="gpt-4",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Acme Corporation
|
||||
CEO: Jane Smith, Chief Executive Officer
|
||||
|
||||
Departments:
|
||||
|
||||
1. Engineering
|
||||
Head: Bob Johnson, CTO
|
||||
Employees:
|
||||
- Sarah Lee, Senior Engineer
|
||||
- Tom Brown, Software Developer
|
||||
|
||||
Sub-departments:
|
||||
- Frontend Team
|
||||
Head: Lisa Wang, Frontend Lead
|
||||
Employees:
|
||||
- Mike Chen, UI Developer
|
||||
- Ana Garcia, UX Designer
|
||||
|
||||
- Backend Team
|
||||
Head: David Kim, Backend Lead
|
||||
Employees:
|
||||
- James Wright, Database Engineer
|
||||
- Rachel Patel, API Developer
|
||||
|
||||
2. Marketing
|
||||
Head: Michael Davis, CMO
|
||||
Employees:
|
||||
- Jennifer Miller, Marketing Specialist
|
||||
- Robert Chen, Content Creator
|
||||
"""}
|
||||
],
|
||||
response_model=Organization
|
||||
)
|
||||
```
|
||||
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Simple Object Extraction](./simple_object.md) - Extracting basic objects
|
||||
- [List Extraction](./list_extraction.md) - Working with lists of objects
|
||||
- [Optional Fields](./optional_fields.md) - Handling optional data
|
||||
- [Recursive Structures](../../examples/recursive.md) - Building more complex hierarchies
|
||||
- [Field Validation](./field_validation.md) - Adding validation to your fields
|
||||
|
||||
191
참고/instructor-main/docs/learning/patterns/optional_fields.md
Normal file
191
참고/instructor-main/docs/learning/patterns/optional_fields.md
Normal file
@@ -0,0 +1,191 @@
|
||||
---
|
||||
title: Working with Optional Fields in Instructor
|
||||
description: Learn how to use optional fields in Pydantic models to handle missing or uncertain information from LLM outputs.
|
||||
---
|
||||
|
||||
# Optional Fields
|
||||
|
||||
This guide explains how to work with optional fields in your data models. Optional fields allow the model to skip fields when information is unavailable or uncertain.
|
||||
|
||||
## Why Use Optional Fields?
|
||||
|
||||
Optional fields are useful when:
|
||||
|
||||
1. Some information is missing from the input text
|
||||
2. Certain fields are only relevant in specific contexts
|
||||
3. The LLM can't confidently extract all fields
|
||||
4. You want to allow partial success instead of complete failure
|
||||
|
||||
## Basic Optional Fields
|
||||
|
||||
To make a field optional, use Python's `Optional` type and provide a default value:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str # Required field
|
||||
age: Optional[int] = None # Optional field with None default
|
||||
occupation: Optional[str] = None # Optional field with None default
|
||||
```
|
||||
|
||||
Here, `name` is required, while `age` and `occupation` are optional and will default to `None` if not found.
|
||||
|
||||
## Using Default Values
|
||||
|
||||
You can provide meaningful default values for optional fields:
|
||||
|
||||
```python
|
||||
from typing import List
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
price: float
|
||||
currency: str = "USD" # Default value
|
||||
in_stock: bool = True # Default value
|
||||
tags: List[str] = [] # Default empty list
|
||||
```
|
||||
|
||||
## Optional Fields with Validation
|
||||
|
||||
You can add the `Field` class for more control and validation:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class UserProfile(BaseModel):
|
||||
username: str
|
||||
email: str
|
||||
bio: Optional[str] = Field(
|
||||
None, # Default value
|
||||
max_length=200, # Validation applies if present
|
||||
description="User's biography, limited to 200 characters"
|
||||
)
|
||||
```
|
||||
|
||||
## Optional Nested Structures
|
||||
|
||||
Entire nested structures can be optional:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: str
|
||||
zip_code: str
|
||||
|
||||
class Contact(BaseModel):
|
||||
email: str
|
||||
phone: Optional[str] = None
|
||||
address: Optional[Address] = None # Optional nested structure
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
contact: Contact
|
||||
```
|
||||
|
||||
When using nested optional structures, check if they exist before accessing:
|
||||
|
||||
```python
|
||||
# Access nested data safely
|
||||
if person.contact.address:
|
||||
print(f"Address: {person.contact.address.city}")
|
||||
else:
|
||||
print("No address information available")
|
||||
```
|
||||
|
||||
## Using `Maybe` for Uncertain Fields
|
||||
|
||||
Instructor provides a `Maybe` type for uncertain or ambiguous fields:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
from instructor.types import Maybe
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class PersonInfo(BaseModel):
|
||||
name: str
|
||||
age: Maybe[int] = None # Maybe type for uncertain fields
|
||||
```
|
||||
|
||||
Check if a `Maybe` field contains uncertain information:
|
||||
|
||||
```python
|
||||
if person.age and person.age.is_uncertain:
|
||||
print(f"Uncertain age: approximately {person.age.value}")
|
||||
elif person.age:
|
||||
print(f"Age: {person.age.value}")
|
||||
else:
|
||||
print("Age: Unknown")
|
||||
```
|
||||
|
||||
For more about the `Maybe` type, see the [Missing Concepts](../../concepts/maybe.md) page.
|
||||
|
||||
## Handling Optional Values
|
||||
|
||||
Always handle the possibility of `None` values in your code:
|
||||
|
||||
```python
|
||||
# Check for None before using
|
||||
if person.age is not None:
|
||||
drinking_age = "Legal" if person.age >= 21 else "Underage"
|
||||
else:
|
||||
drinking_age = "Unknown"
|
||||
|
||||
# Use conditional expressions
|
||||
price_display = f"${product.price}" if product.price is not None else "Price unavailable"
|
||||
|
||||
# Provide defaults with 'or'
|
||||
display_name = user.nickname or user.username
|
||||
```
|
||||
|
||||
## Validation with Optional Fields
|
||||
|
||||
Optional fields can still have validation when they're present:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel, field_validator
|
||||
import instructor
|
||||
import re
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class ContactInfo(BaseModel):
|
||||
email: str
|
||||
phone: Optional[str] = None
|
||||
|
||||
@field_validator('phone')
|
||||
@classmethod
|
||||
def validate_phone(cls, v):
|
||||
if v is not None and not re.match(r'^\+?[1-9]\d{1,14}$', v):
|
||||
raise ValueError("Invalid phone format")
|
||||
return v
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Simple Object Extraction](./simple_object.md) - Extracting basic objects
|
||||
- [Field Validation](./field_validation.md) - Adding validation to fields
|
||||
- [Nested Structure](./nested_structure.md) - Working with complex data
|
||||
- [Missing Concepts](../../concepts/maybe.md) - Using the Maybe type for uncertain fields
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Field Validation](./field_validation.md)
|
||||
- Explore [Nested Structure](./nested_structure.md) for complex data
|
||||
- Check out [Prompt Templates](./prompt_templates.md) for crafting prompts
|
||||
174
참고/instructor-main/docs/learning/patterns/prompt_templates.md
Normal file
174
참고/instructor-main/docs/learning/patterns/prompt_templates.md
Normal file
@@ -0,0 +1,174 @@
|
||||
---
|
||||
title: Using Prompt Templates with Instructor
|
||||
description: Learn how to create reusable prompt templates for consistent structured output extraction across different use cases.
|
||||
---
|
||||
|
||||
# Prompt Templates
|
||||
|
||||
This guide covers how to use prompt templates with Instructor to create reusable, parameterized prompts for structured data extraction.
|
||||
|
||||
## Why Prompt Templates Matter
|
||||
|
||||
Good prompts are essential for effective structured data extraction. Prompt templates help you:
|
||||
|
||||
1. Create consistent and reusable prompts
|
||||
2. Parameterize prompts with dynamic values
|
||||
3. Separate prompt engineering from application logic
|
||||
4. Standardize prompt patterns for different use cases
|
||||
|
||||
## Basic Prompt Templates
|
||||
|
||||
The simplest form of a prompt template is a string with placeholders for variables:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
occupation: str
|
||||
|
||||
# Define a template with parameters
|
||||
prompt_template = """
|
||||
Extract information about the person mentioned in the following {document_type}:
|
||||
|
||||
{content}
|
||||
|
||||
Please provide their name, age, and occupation.
|
||||
"""
|
||||
|
||||
# Use the template with specific values
|
||||
document_type = "email"
|
||||
content = "Hi team, I'm introducing our new project manager, Sarah Johnson. She's 34 and has been in project management for 8 years."
|
||||
|
||||
prompt = prompt_template.format(
|
||||
document_type=document_type,
|
||||
content=content
|
||||
)
|
||||
|
||||
# Extract structured data using the formatted prompt
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
response_model=Person
|
||||
)
|
||||
```
|
||||
|
||||
## Using f-strings for Simple Templates
|
||||
|
||||
For simple cases, you can use f-strings to create prompt templates:
|
||||
|
||||
```python
|
||||
def extract_person(content, document_type="text"):
|
||||
prompt = f"""
|
||||
Extract information about the person mentioned in the following {document_type}:
|
||||
|
||||
{content}
|
||||
|
||||
Please provide their name, age, and occupation.
|
||||
"""
|
||||
|
||||
return client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
response_model=Person
|
||||
)
|
||||
|
||||
# Use the function
|
||||
person = extract_person(
|
||||
"According to his resume, John Smith (42) works as a software developer.",
|
||||
document_type="resume"
|
||||
)
|
||||
```
|
||||
|
||||
## Template Functions
|
||||
|
||||
For more complex templates, create dedicated template functions:
|
||||
|
||||
```python
|
||||
from typing import List, Optional
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class ProductReview(BaseModel):
|
||||
product_name: str
|
||||
rating: int
|
||||
pros: List[str]
|
||||
cons: List[str]
|
||||
summary: str
|
||||
|
||||
def create_review_extraction_prompt(
|
||||
review_text: str,
|
||||
product_category: str,
|
||||
include_sentiment: bool = False
|
||||
) -> str:
|
||||
sentiment_instruction = """
|
||||
Also include a brief sentiment analysis of the review.
|
||||
""" if include_sentiment else ""
|
||||
|
||||
return f"""
|
||||
Extract product review information from the following {product_category} review:
|
||||
|
||||
{review_text}
|
||||
|
||||
Please identify:
|
||||
- The name of the product being reviewed
|
||||
- The numerical rating (1-5)
|
||||
- A list of pros/positive points
|
||||
- A list of cons/negative points
|
||||
- A brief summary of the review
|
||||
{sentiment_instruction}
|
||||
"""
|
||||
|
||||
# Use the template function
|
||||
review_text = """
|
||||
I recently purchased the UltraSound X300 headphones, and I'm mostly satisfied.
|
||||
The sound quality is amazing and the battery lasts for days. They're also very
|
||||
comfortable to wear for long periods. However, they're a bit pricey at $299, and
|
||||
the Bluetooth occasionally disconnects. Overall, I'd give them 4 out of 5 stars.
|
||||
"""
|
||||
|
||||
prompt = create_review_extraction_prompt(
|
||||
review_text=review_text,
|
||||
product_category="headphone",
|
||||
include_sentiment=True
|
||||
)
|
||||
|
||||
review = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": prompt}
|
||||
],
|
||||
response_model=ProductReview
|
||||
)
|
||||
```
|
||||
|
||||
## Best Practices for Prompt Templates
|
||||
|
||||
1. **Be explicit about the output format**: Clearly specify what fields you need and in what format
|
||||
2. **Use consistent language**: Maintain consistent terminology throughout the template
|
||||
3. **Keep it concise**: Avoid unnecessary verbosity that could confuse the model
|
||||
4. **Parameterize only what varies**: Only make template parameters for parts that need to change
|
||||
5. **Include examples for complex tasks**: Provide few-shot examples for more complex extractions
|
||||
6. **Test with different inputs**: Ensure your template works well with a variety of inputs
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Simple Object Extraction](./simple_object.md) - Extracting basic objects
|
||||
- [List Extraction](./list_extraction.md) - Working with lists of objects
|
||||
- [Optional Fields](./optional_fields.md) - Handling optional data
|
||||
- [Prompting](../../concepts/prompting.md) - General prompting concepts
|
||||
- [Templating](../../concepts/templating.md) - Advanced template techniques
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Explore [Field Validation](./field_validation.md) for ensuring data quality
|
||||
- Try [List Extraction](./list_extraction.md) for extracting multiple items
|
||||
- Learn about [Nested Structure](./nested_structure.md) for complex data
|
||||
145
참고/instructor-main/docs/learning/patterns/simple_object.md
Normal file
145
참고/instructor-main/docs/learning/patterns/simple_object.md
Normal file
@@ -0,0 +1,145 @@
|
||||
---
|
||||
title: Simple Object Extraction Pattern
|
||||
description: Learn the fundamental pattern of extracting simple objects from text using Instructor with type-safe validation.
|
||||
---
|
||||
|
||||
# Simple Object Extraction: LLM Tutorial for Structured Data
|
||||
|
||||
Learn how to extract structured objects from text using LLMs in this comprehensive tutorial. We'll cover the fundamental pattern of transforming unstructured text into validated Python objects using Instructor with GPT-4, Claude, and other language models.
|
||||
|
||||
## Basic LLM Object Extraction Tutorial
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
import instructor
|
||||
# Define your LLM extraction schema
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
occupation: str
|
||||
|
||||
# Extract structured data from LLM
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
person = client.create(
|
||||
model="gpt-3.5-turbo", # Works with GPT-4, Claude, Gemini
|
||||
messages=[
|
||||
{"role": "user", "content": "John Smith is a 35-year-old software engineer."}
|
||||
],
|
||||
response_model=Person # Type-safe LLM extraction
|
||||
)
|
||||
|
||||
print(f"Name: {person.name}")
|
||||
print(f"Age: {person.age}")
|
||||
print(f"Occupation: {person.occupation}")
|
||||
```
|
||||
|
||||
```
|
||||
┌───────────────┐ ┌───────────────┐
|
||||
│ Define Model │ │ Extracted │
|
||||
│ name: str │ Extract │ name: "John" │
|
||||
│ age: int │ ─────────> │ age: 35 │
|
||||
│ occupation: str│ │ occupation: │
|
||||
└───────────────┘ │ "software..." │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
## Enhance LLM Extraction with Field Descriptions
|
||||
|
||||
Guide your LLM with clear field descriptions for more accurate extraction:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Book(BaseModel):
|
||||
title: str = Field(description="The full title of the book")
|
||||
author: str = Field(description="The author's full name")
|
||||
publication_year: int = Field(description="The year the book was published")
|
||||
```
|
||||
|
||||
Field descriptions serve as prompts for the LLM, improving extraction accuracy and reducing errors in your structured outputs.
|
||||
|
||||
## Handle Missing Data in LLM Responses
|
||||
|
||||
Real-world LLM extractions often encounter missing information. Here's how to handle it gracefully:
|
||||
|
||||
```python
|
||||
from typing import Optional
|
||||
from pydantic import BaseModel
|
||||
|
||||
class MovieReview(BaseModel):
|
||||
title: str
|
||||
director: Optional[str] = None # Optional field
|
||||
rating: float
|
||||
```
|
||||
|
||||
Using `Optional` fields ensures your LLM extraction remains robust when dealing with incomplete or partial information.
|
||||
|
||||
## Validate LLM Outputs with Pydantic
|
||||
|
||||
Ensure LLM outputs meet your requirements with built-in validation:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
price: float = Field(gt=0, description="The product price in USD")
|
||||
in_stock: bool
|
||||
```
|
||||
|
||||
Pydantic validation ensures your LLM outputs are not just structured, but also correct and business-rule compliant.
|
||||
|
||||
## Production-Ready LLM Extraction Example
|
||||
|
||||
Here's a complete example showing nested object extraction from LLMs:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: str
|
||||
zip_code: str
|
||||
|
||||
class ContactInfo(BaseModel):
|
||||
name: str
|
||||
email: str
|
||||
phone: Optional[str] = None
|
||||
address: Optional[Address] = None
|
||||
|
||||
# Extract structured data
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
contact = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": """
|
||||
Contact information:
|
||||
Name: Sarah Johnson
|
||||
Email: sarah.j@example.com
|
||||
Phone: (555) 123-4567
|
||||
Address: 123 Main St, Boston, MA 02108
|
||||
"""}
|
||||
],
|
||||
response_model=ContactInfo
|
||||
)
|
||||
|
||||
print(f"Name: {contact.name}")
|
||||
print(f"Email: {contact.email}")
|
||||
```
|
||||
|
||||
## Common LLM Object Extraction Use Cases
|
||||
|
||||
- **Contact Information**: Extract names, emails, phones from unstructured text
|
||||
- **Product Details**: Parse product descriptions into structured catalogs
|
||||
- **Event Information**: Extract dates, locations, attendees from event descriptions
|
||||
- **Entity Recognition**: Identify and structure people, places, organizations
|
||||
|
||||
## Continue Your LLM Tutorial Journey
|
||||
|
||||
- **[List Extraction Tutorial](list_extraction.md)** - Extract multiple objects from LLM responses
|
||||
- **[Nested Structures](nested_structure.md)** - Handle complex hierarchical data from LLMs
|
||||
- **[Advanced Validation](field_validation.md)** - Implement business rules for LLM outputs
|
||||
|
||||
Master these patterns to build production-ready LLM applications with reliable structured outputs!
|
||||
114
참고/instructor-main/docs/learning/streaming/basics.md
Normal file
114
참고/instructor-main/docs/learning/streaming/basics.md
Normal file
@@ -0,0 +1,114 @@
|
||||
---
|
||||
title: Streaming Basics with Instructor
|
||||
description: Learn how to use streaming to receive partial structured responses from LLMs as they are generated.
|
||||
---
|
||||
|
||||
# Streaming Basics
|
||||
|
||||
Streaming allows you to receive parts of a structured response as they're generated, rather than waiting for the complete response.
|
||||
|
||||
## Why Use Streaming?
|
||||
|
||||
Streaming offers several benefits:
|
||||
|
||||
1. **Faster Perceived Response**: Users see results immediately
|
||||
2. **Progressive UI Updates**: Update your interface as data arrives
|
||||
3. **Processing While Generating**: Start using data before the complete response is ready
|
||||
|
||||
```
|
||||
Without Streaming:
|
||||
┌─────────┐ ┌─────────────────────┐
|
||||
│ Request │─── Wait ───>│ Complete Response │
|
||||
└─────────┘ └─────────────────────┘
|
||||
|
||||
With Streaming:
|
||||
┌─────────┐ ┌───────┐ ┌───────┐ ┌───────┐
|
||||
│ Request │───>│Part 1 │───>│Part 2 │───>│Part 3 │─── ...
|
||||
└─────────┘ └───────┘ └───────┘ └───────┘
|
||||
```
|
||||
|
||||
## Simple Example
|
||||
|
||||
Here's how to stream a structured response:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
# Define your data structure
|
||||
class UserProfile(BaseModel):
|
||||
name: str
|
||||
bio: str
|
||||
interests: list[str]
|
||||
|
||||
# Set up client
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
# Enable streaming
|
||||
for partial in client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Generate a profile for Alex Chen"}
|
||||
],
|
||||
response_model=UserProfile,
|
||||
stream=True # This enables streaming
|
||||
):
|
||||
# Print each update as it arrives
|
||||
print("\nUpdate received:")
|
||||
|
||||
# Access available fields
|
||||
if hasattr(partial, "name") and partial.name:
|
||||
print(f"Name: {partial.name}")
|
||||
if hasattr(partial, "bio") and partial.bio:
|
||||
print(f"Bio: {partial.bio[:30]}...")
|
||||
if hasattr(partial, "interests") and partial.interests:
|
||||
print(f"Interests: {', '.join(partial.interests)}")
|
||||
```
|
||||
|
||||
## How Streaming Works
|
||||
|
||||
When streaming with Instructor:
|
||||
|
||||
1. Enable streaming with `stream=True`
|
||||
2. The method returns an iterator of partial responses
|
||||
3. Each partial contains fields that have been completed so far
|
||||
4. You check for fields using `hasattr()` since they appear incrementally
|
||||
5. The final iteration contains the complete response
|
||||
|
||||
## Progress Tracking Example
|
||||
|
||||
Here's a simple way to track progress:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from pydantic import BaseModel
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Report(BaseModel):
|
||||
title: str
|
||||
summary: str
|
||||
conclusion: str
|
||||
|
||||
# Track completed fields
|
||||
completed = set()
|
||||
total_fields = 3 # Number of fields in our model
|
||||
|
||||
for partial in client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Generate a report on climate change"}
|
||||
],
|
||||
response_model=Report,
|
||||
stream=True
|
||||
):
|
||||
# Check which fields are complete
|
||||
for field in ["title", "summary", "conclusion"]:
|
||||
if hasattr(partial, field) and getattr(partial, field) and field not in completed:
|
||||
completed.add(field)
|
||||
percent = (len(completed) / total_fields) * 100
|
||||
print(f"Received: {field} - {percent:.0f}% complete")
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Explore [Streaming Lists](lists.md) for handling collections
|
||||
- Learn about [Validation with Streaming](../validation/basics.md)
|
||||
101
참고/instructor-main/docs/learning/streaming/lists.md
Normal file
101
참고/instructor-main/docs/learning/streaming/lists.md
Normal file
@@ -0,0 +1,101 @@
|
||||
---
|
||||
title: Streaming Lists with Instructor
|
||||
description: Learn how to stream lists of structured objects from LLMs, processing collection items as they are generated for better responsiveness.
|
||||
---
|
||||
|
||||
# Streaming Lists
|
||||
|
||||
This guide explains how to stream lists of structured data with Instructor. Streaming lists allows you to process collection items as they're generated, improving responsiveness for larger outputs.
|
||||
|
||||
## Basic List Streaming
|
||||
|
||||
Here's how to stream a list of structured objects:
|
||||
|
||||
```python
|
||||
from typing import Iterable
|
||||
import instructor
|
||||
from pydantic import BaseModel, Field
|
||||
# Initialize the client
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Book(BaseModel):
|
||||
title: str = Field(..., description="Book title")
|
||||
author: str = Field(..., description="Book author")
|
||||
year: int = Field(..., description="Publication year")
|
||||
|
||||
# Stream a list of books
|
||||
for book in client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "List 5 classic science fiction books"}
|
||||
],
|
||||
response_model=Iterable[Book],
|
||||
):
|
||||
print(f"Received: {book.title} by {book.author} ({book.year})")
|
||||
```
|
||||
|
||||
This example shows how to:
|
||||
1. Define a Pydantic model for each list item
|
||||
2. Use Python's typing system to specify a list
|
||||
3. Process each item as it arrives in the stream
|
||||
|
||||
## Real-world Example: Task Generation
|
||||
|
||||
Here's a practical example of streaming a list of tasks with progress tracking:
|
||||
|
||||
```python
|
||||
from typing import Iterable
|
||||
import instructor
|
||||
from pydantic import BaseModel, Field
|
||||
import time
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
|
||||
class Task(BaseModel):
|
||||
title: str = Field(..., description="Task title")
|
||||
description: str = Field(..., description="Detailed task description")
|
||||
priority: str = Field(..., description="Task priority (High/Medium/Low)")
|
||||
estimated_hours: float = Field(..., description="Estimated hours to complete")
|
||||
|
||||
|
||||
print("Generating project tasks...")
|
||||
start_time = time.time()
|
||||
received_tasks = 0
|
||||
|
||||
for task in client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "Generate a list of 5 tasks for building a personal website",
|
||||
}
|
||||
],
|
||||
response_model=Iterable[Task],
|
||||
stream=True,
|
||||
):
|
||||
received_tasks += 1
|
||||
print(f"\nTask {received_tasks}: {task.title} (Priority: {task.priority})")
|
||||
print(f"Description: {task.description[:100]}...")
|
||||
print(f"Estimated time: {task.estimated_hours} hours")
|
||||
|
||||
# Calculate progress percentage based on expected items
|
||||
progress = (received_tasks / 5) * 100
|
||||
print(f"Progress: {progress:.0f}%")
|
||||
|
||||
elapsed_time = time.time() - start_time
|
||||
print(f"\nAll {received_tasks} tasks generated in {elapsed_time:.2f} seconds")
|
||||
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Streaming Basics](./basics.md) - Fundamentals of streaming structured outputs
|
||||
- [List Extraction](../../learning/patterns/list_extraction.md) - Core concepts for working with lists
|
||||
- [Validation Basics](../../learning/validation/basics.md) - Understanding validation for streaming
|
||||
- [Streaming API](../../concepts/partial.md) - Technical details on the streaming implementation
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Validation](../../learning/validation/basics.md) to ensure your streamed data is valid
|
||||
- Explore [Field Validation](../../learning/validation/field_level_validation.md) for more control
|
||||
- See [Async Support](../../integrations/index.md) for integrating streaming with your specific provider when writing asynchronous code
|
||||
116
참고/instructor-main/docs/learning/validation/basics.md
Normal file
116
참고/instructor-main/docs/learning/validation/basics.md
Normal file
@@ -0,0 +1,116 @@
|
||||
---
|
||||
title: LLM Validation Basics with Instructor
|
||||
description: Master the fundamentals of validating LLM outputs to ensure reliable, business-compliant structured data from GPT-4, Claude, and other models.
|
||||
---
|
||||
|
||||
# LLM Validation Tutorial: Ensure Data Quality with Instructor
|
||||
|
||||
Master the fundamentals of validating LLM outputs in this comprehensive tutorial. Learn how to use Instructor's validation system to ensure GPT-4, Claude, and other language models produce reliable, business-compliant structured data.
|
||||
|
||||
## Why LLM Output Validation is Critical
|
||||
|
||||
When extracting structured data from LLMs, validation ensures:
|
||||
|
||||
1. **Data Integrity**: LLM outputs contain all required fields with correct formats
|
||||
2. **Business Compliance**: Extracted data adheres to your domain rules and constraints
|
||||
3. **Production Reliability**: LLM responses meet quality standards before entering your system
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌──────────────┐ ┌─────────────┐
|
||||
│ LLM │ -> │ Instructor │ -> │ Validated │
|
||||
│ Generates │ │ Validates │ │ Structured │
|
||||
│ Response │ │ Structure │ │ Data │
|
||||
└─────────────┘ └──────────────┘ └─────────────┘
|
||||
│
|
||||
│ If validation fails
|
||||
▼
|
||||
┌─────────────┐
|
||||
│ Retry with │
|
||||
│ Feedback │
|
||||
└─────────────┘
|
||||
```
|
||||
|
||||
## Basic LLM Validation Example
|
||||
|
||||
See how Instructor validates LLM outputs automatically:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
# Define validation rules for LLM extraction
|
||||
class UserProfile(BaseModel):
|
||||
name: str
|
||||
age: int = Field(ge=13, description="User's age in years")
|
||||
|
||||
# Extract and validate LLM output
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo", # Works with GPT-4, Claude, Gemini
|
||||
messages=[
|
||||
{"role": "user", "content": "My name is Jane Smith and I'm 25 years old."}
|
||||
],
|
||||
response_model=UserProfile # Automatic validation
|
||||
)
|
||||
|
||||
print(f"User: {response.name}, Age: {response.age}")
|
||||
```
|
||||
|
||||
Key validation features in this LLM tutorial:
|
||||
- **Constraint Validation**: Age must be ≥ 13 years
|
||||
- **Automatic Retry**: If LLM output fails validation, Instructor retries with error context
|
||||
- **Type Safety**: Ensures LLM returns proper data types
|
||||
|
||||
## Essential LLM Validation Patterns
|
||||
|
||||
Common validation rules for LLM outputs:
|
||||
|
||||
| Validation | Example | What It Does |
|
||||
|------------|---------|-------------|
|
||||
| Type checking | `age: int` | Ensures value is an integer |
|
||||
| Required fields | `name: str` | Field must be present |
|
||||
| Optional fields | `middle_name: Optional[str] = None` | Field can be missing |
|
||||
| Minimum value | `age: int = Field(ge=18)` | Value must be ≥ 18 |
|
||||
| Maximum value | `rating: float = Field(le=5.0)` | Value must be ≤ 5.0 |
|
||||
| String length | `username: str = Field(min_length=3)` | String must be at least 3 chars |
|
||||
|
||||
## How LLM Output Validation Works
|
||||
|
||||
The LLM validation pipeline in Instructor:
|
||||
|
||||
1. **LLM Generation**: Language model produces structured output
|
||||
2. **Schema Matching**: Instructor maps LLM response to your Pydantic model
|
||||
3. **Validation Check**: Pydantic validates against defined constraints
|
||||
4. **Smart Retry**: On failure, errors are sent back to the LLM with context
|
||||
5. **Success or Timeout**: Process continues until valid output or retry limit
|
||||
|
||||
## Enhance LLM Validation with Custom Messages
|
||||
|
||||
Guide LLMs with specific error messages for better corrections:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
price: float = Field(
|
||||
gt=0,
|
||||
description="Product price in USD",
|
||||
json_schema_extra={"error_msg": "Price must be greater than zero"}
|
||||
)
|
||||
```
|
||||
|
||||
## Common LLM Validation Use Cases
|
||||
|
||||
- **Age Verification**: Ensure extracted ages meet minimum requirements
|
||||
- **Price Validation**: Verify LLM-extracted prices are positive numbers
|
||||
- **Email Format**: Validate email addresses from unstructured text
|
||||
- **Date Constraints**: Ensure dates are within valid ranges
|
||||
- **Business Rules**: Enforce domain-specific constraints on LLM outputs
|
||||
|
||||
## Continue Your LLM Validation Journey
|
||||
|
||||
- **[Custom Validators](custom_validators.md)** - Build complex validation logic for LLM outputs
|
||||
- **[Retry Mechanisms](retry_mechanisms.md)** - Configure how Instructor handles validation failures
|
||||
- **[Field-Level Validation](field_level_validation.md)** - Validate individual fields in LLM responses
|
||||
|
||||
Master validation to ensure your LLM applications produce reliable, production-ready data!
|
||||
220
참고/instructor-main/docs/learning/validation/custom_validators.md
Normal file
220
참고/instructor-main/docs/learning/validation/custom_validators.md
Normal file
@@ -0,0 +1,220 @@
|
||||
---
|
||||
title: Custom Validators for LLM Outputs
|
||||
description: Learn to build custom validators for LLM outputs using rule-based and semantic validation techniques with Instructor.
|
||||
---
|
||||
|
||||
# Custom LLM Validators Tutorial: Advanced Data Quality Control
|
||||
|
||||
Learn how to build custom validators for LLM outputs in this advanced tutorial. Master both rule-based and semantic validation techniques to ensure GPT-4, Claude, and other language models produce data that meets your exact requirements.
|
||||
|
||||
## Basic Custom Validator
|
||||
|
||||
Custom validators are functions that validate field values and can be applied using Pydantic's field validators.
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, field_validator
|
||||
import instructor
|
||||
|
||||
# Initialize the client
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Person(BaseModel):
|
||||
name: str
|
||||
age: int
|
||||
|
||||
@field_validator('age')
|
||||
@classmethod
|
||||
def validate_age(cls, value):
|
||||
if value < 0 or value > 120:
|
||||
raise ValueError("Age must be between 0 and 120")
|
||||
return value
|
||||
|
||||
# Extract data with validation
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "The person's name is John and they are 150 years old."}
|
||||
],
|
||||
response_model=Person
|
||||
)
|
||||
```
|
||||
|
||||
If the model returns an age outside the valid range, Instructor will retry the request with specific feedback about the validation failure.
|
||||
|
||||
For more information on how Instructor handles validation and retries, see [Validation Basics](../../concepts/validation.md) and the [Retrying](../../concepts/retrying.md) concepts page.
|
||||
|
||||
## Complex Validation
|
||||
|
||||
You can create more complex validators that check multiple fields or have conditional logic:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, field_validator, model_validator
|
||||
import instructor
|
||||
from typing import List, Optional
|
||||
from datetime import date
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Employee(BaseModel):
|
||||
name: str
|
||||
hire_date: date
|
||||
termination_date: Optional[date] = None
|
||||
skills: List[str]
|
||||
|
||||
@field_validator('skills')
|
||||
@classmethod
|
||||
def validate_skills(cls, skills):
|
||||
if len(skills) < 1:
|
||||
raise ValueError("Employee must have at least one skill")
|
||||
return skills
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_dates(self):
|
||||
if self.termination_date and self.termination_date < self.hire_date:
|
||||
raise ValueError("Termination date cannot be before hire date")
|
||||
return self
|
||||
```
|
||||
|
||||
For more advanced validation approaches, check out [Field-level Validation](../../concepts/fields.md) and the [Validators](../../concepts/reask_validation.md) concepts page.
|
||||
|
||||
## Handling Complex Data Types
|
||||
|
||||
Custom validators can also process more complex data types and perform transformations:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, field_validator
|
||||
import instructor
|
||||
import re
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Contact(BaseModel):
|
||||
name: str
|
||||
email: str
|
||||
phone: str
|
||||
|
||||
@field_validator('email')
|
||||
@classmethod
|
||||
def validate_email(cls, value):
|
||||
pattern = r'^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$'
|
||||
if not re.match(pattern, value):
|
||||
raise ValueError("Invalid email format")
|
||||
return value
|
||||
|
||||
@field_validator('phone')
|
||||
@classmethod
|
||||
def validate_phone(cls, value):
|
||||
# Remove non-digit characters and validate
|
||||
digits_only = re.sub(r'\D', '', value)
|
||||
if len(digits_only) < 10:
|
||||
raise ValueError("Phone number must have at least 10 digits")
|
||||
return digits_only # Return the cleaned version
|
||||
```
|
||||
|
||||
For a practical example of extraction with validation, see the [Contact Information Extraction](../../examples/extract_contact_info.md) example.
|
||||
|
||||
## Using External Services for Validation
|
||||
|
||||
You can also use external services or APIs for validation:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, field_validator
|
||||
import instructor
|
||||
import requests
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Address(BaseModel):
|
||||
street: str
|
||||
city: str
|
||||
state: str
|
||||
zip_code: str
|
||||
|
||||
@field_validator('zip_code')
|
||||
@classmethod
|
||||
def validate_zip_code(cls, value):
|
||||
# Example of validation using an external service (simplified)
|
||||
# In a real app, you might use a postal code validation API
|
||||
if not (value.isdigit() and len(value) == 5):
|
||||
raise ValueError("Zip code must be 5 digits")
|
||||
return value
|
||||
```
|
||||
|
||||
## Semantic Validation with LLMs
|
||||
|
||||
For complex validation scenarios where rule-based validation is difficult, Instructor provides semantic validation capabilities using LLMs via the `llm_validator` function. For a comprehensive guide on this topic, see the dedicated [Semantic Validation](../../concepts/semantic_validation.md) page:
|
||||
|
||||
```python
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel, BeforeValidator
|
||||
import instructor
|
||||
from instructor import llm_validator
|
||||
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class ProductDescription(BaseModel):
|
||||
product_name: str
|
||||
description: Annotated[
|
||||
str,
|
||||
BeforeValidator(
|
||||
llm_validator(
|
||||
"The description must be professional, accurate, and free of hyperbole. "
|
||||
"It should not make unsubstantiated claims or use superlatives excessively.",
|
||||
client=client
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
# This would fail validation because it uses excessive hyperbole
|
||||
try:
|
||||
product = ProductDescription(
|
||||
product_name="SuperClean 3000",
|
||||
description="The absolute BEST cleaning product in the world! Will change your life FOREVER! Makes every other cleaning product completely OBSOLETE!"
|
||||
)
|
||||
except ValueError as e:
|
||||
print(e) # The validation error would explain the issue with the hyperbolic language
|
||||
```
|
||||
|
||||
Semantic validation is particularly useful for validating against criteria that are:
|
||||
|
||||
1. **Subjective** - Such as tone, style, or appropriateness
|
||||
2. **Contextual** - Requiring understanding of relationships between elements
|
||||
3. **Complex** - Where multiple interrelated factors need to be evaluated together
|
||||
4. **Hard to formalize** - When rules would be too numerous or complex to express programmatically
|
||||
|
||||
Unlike rule-based validators that check against predefined criteria, semantic validators leverage LLMs to evaluate content based on natural language instructions. They can understand nuance and context in ways that traditional validation cannot.
|
||||
|
||||
### When to Use Semantic Validation
|
||||
|
||||
Consider using semantic validation when:
|
||||
|
||||
- You need to enforce style guidelines or content policies
|
||||
- Validating natural language content against subjective criteria
|
||||
- Checking for consistency across multiple fields or complex relationships
|
||||
- Traditional validation would require hundreds of individual rules
|
||||
|
||||
Remember that semantic validation requires additional API calls, which adds cost and latency to your application. Use it strategically for high-value validation needs rather than for simple constraints that can be handled with standard validators.
|
||||
|
||||
## Handling Validation Failures
|
||||
|
||||
When validation fails, Instructor can handle it in different ways. Learn more about:
|
||||
|
||||
- [Retry Mechanisms](../../concepts/retrying.md) for automatic retries with feedback
|
||||
- [Self-Correction](../../examples/self_critique.md) for AI model self-correction techniques
|
||||
|
||||
## Best Practices for Custom Validators
|
||||
|
||||
1. **Be specific in error messages**: Provide clear error messages that explain exactly what went wrong
|
||||
2. **Validate early**: Apply validators to individual fields when possible before model-level validation
|
||||
3. **Keep validators focused**: Each validator should have a single responsibility
|
||||
4. **Use type hints**: Proper type hints help both Pydantic and Instructor understand your data better
|
||||
5. **Consider both validation and transformation**: Validators can both validate and transform data
|
||||
6. **Choose appropriate validation type**: Use rule-based validation for simple, objective criteria and semantic validation for complex, subjective, or context-dependent validation
|
||||
7. **Balance cost and benefits**: Consider the additional cost and latency of semantic validation against the value it provides
|
||||
|
||||
For more information on validation in general, check out the [Validation](../../concepts/validation.md) concepts page.
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Fields](../../concepts/fields.md) - Learn about field definitions and properties
|
||||
- [Models](../../concepts/models.md) - Understand model creation and configuration
|
||||
- [Types](../../concepts/types.md) - Explore the different data types you can use
|
||||
|
||||
Custom validators are a powerful way to ensure the data you extract meets your specific requirements, improving the reliability and quality of structured outputs from LLMs.
|
||||
@@ -0,0 +1,128 @@
|
||||
---
|
||||
title: Field-level Validation with Instructor
|
||||
description: Learn how to create specific validation rules for individual fields in your Pydantic models to ensure data quality.
|
||||
---
|
||||
|
||||
# Field-level Validation
|
||||
|
||||
Field-level validation lets you create specific rules for individual fields in your data models. This guide shows how to use field-level validation with Instructor.
|
||||
|
||||
## What is Field-level Validation?
|
||||
|
||||
Field-level validation in Instructor uses Pydantic's validation features to:
|
||||
|
||||
1. Check individual fields with custom rules
|
||||
2. Transform field values (like formatting or cleaning data)
|
||||
3. Apply business rules to specific fields
|
||||
4. Give clear feedback when values are invalid
|
||||
|
||||
Validation happens when your model is being processed, and if it fails, Instructor will retry with better instructions.
|
||||
|
||||
## Basic Field Validation
|
||||
|
||||
You can apply simple validation using Pydantic's Field constraints:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, Field
|
||||
import instructor
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class User(BaseModel):
|
||||
name: str = Field(..., min_length=2, description="User's full name")
|
||||
age: int = Field(..., ge=18, le=120, description="User's age in years")
|
||||
email: str = Field(
|
||||
...,
|
||||
pattern=r"^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$",
|
||||
description="Valid email address"
|
||||
)
|
||||
```
|
||||
|
||||
For more details, see the [Fields](../../concepts/fields.md) concepts page.
|
||||
|
||||
## Custom Field Validators
|
||||
|
||||
For more complex rules, use the `field_validator` decorator:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, field_validator
|
||||
import instructor
|
||||
import re
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
sku: str
|
||||
price: float
|
||||
|
||||
@field_validator('name')
|
||||
@classmethod
|
||||
def validate_name(cls, v):
|
||||
if len(v.strip()) < 3:
|
||||
raise ValueError("Product name must be at least 3 characters long")
|
||||
return v.strip().title() # Clean up and format
|
||||
|
||||
@field_validator('sku')
|
||||
@classmethod
|
||||
def validate_sku(cls, v):
|
||||
pattern = r'^[A-Z]{3}-\d{4}$'
|
||||
if not re.match(pattern, v):
|
||||
raise ValueError("SKU must be in format XXX-0000 (3 uppercase letters, dash, 4 digits)")
|
||||
return v
|
||||
```
|
||||
|
||||
## Validating Multiple Fields Together
|
||||
|
||||
Sometimes one field's validity depends on other fields. Use `model_validator` for this:
|
||||
|
||||
```python
|
||||
from pydantic import BaseModel, model_validator
|
||||
import instructor
|
||||
from datetime import date
|
||||
client = instructor.from_provider("openai/gpt-5-nano")
|
||||
|
||||
class Reservation(BaseModel):
|
||||
check_in: date
|
||||
check_out: date
|
||||
room_type: str
|
||||
guests: int
|
||||
|
||||
@model_validator(mode='after')
|
||||
def validate_dates(self):
|
||||
if self.check_out <= self.check_in:
|
||||
raise ValueError("Check-out date must be after check-in date")
|
||||
|
||||
if self.room_type == "Standard" and self.guests > 2:
|
||||
raise ValueError("Standard rooms can only fit 2 guests")
|
||||
|
||||
return self
|
||||
```
|
||||
|
||||
## How Validation Errors Are Handled
|
||||
|
||||
When validation fails, Instructor adds error details to help the AI fix the problem:
|
||||
|
||||
```
|
||||
The following errors occurred during validation:
|
||||
- product_sku: Product not found
|
||||
- quantity: Quantity must be at least 1
|
||||
|
||||
Please fix these errors and ensure the response is valid.
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Order matters**: Validators run in the order they're defined
|
||||
2. **Clear messages**: Write specific error messages
|
||||
3. **Clean first**: Handle data cleaning before validation
|
||||
4. **Validate early**: Check fields before model-level validation
|
||||
5. **Transform wisely**: Field validators can both check and change values
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Fields](../../concepts/fields.md) - Basic field properties
|
||||
- [Custom Validators](../../concepts/reask_validation.md) - Creating custom validation logic
|
||||
- [Validation Basics](../../concepts/validation.md) - Fundamental validation concepts
|
||||
- [Retry Mechanisms](../../concepts/retrying.md) - How validation retries work
|
||||
- [Fallback Strategies](../../concepts/error_handling.md) - Handling persistent validation failures
|
||||
- [Types](../../concepts/types.md) - Understanding data types in Pydantic models
|
||||
|
||||
206
참고/instructor-main/docs/learning/validation/retry_mechanisms.md
Normal file
206
참고/instructor-main/docs/learning/validation/retry_mechanisms.md
Normal file
@@ -0,0 +1,206 @@
|
||||
# Retry Mechanisms
|
||||
|
||||
Retry mechanisms in Instructor handle validation failures by giving the LLM another chance to generate valid responses. This guide explains how retries work and how to customize them for your use case.
|
||||
|
||||
## How Retries Work
|
||||
|
||||
When validation fails, Instructor:
|
||||
|
||||
1. Captures the validation error(s)
|
||||
2. Formats them as feedback
|
||||
3. Adds the feedback to the prompt context
|
||||
4. Asks the LLM to try again with this new information
|
||||
|
||||
This creates a feedback loop that helps the LLM correct its output until it produces a valid response.
|
||||
|
||||
## Basic Retry Example
|
||||
|
||||
Here's a simple example showing retries in action:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
# Initialize the client with max_retries
|
||||
client = instructor.from_provider(
|
||||
"openai/gpt-4o",
|
||||
max_retries=2 # Will try up to 3 times (initial + 2 retries)
|
||||
)
|
||||
|
||||
class Product(BaseModel):
|
||||
name: str
|
||||
price: float = Field(..., gt=0)
|
||||
|
||||
@field_validator('name')
|
||||
@classmethod
|
||||
def validate_name(cls, v):
|
||||
if len(v) < 3:
|
||||
raise ValueError("Product name must be at least 3 characters")
|
||||
return v
|
||||
|
||||
# This will automatically retry if validation fails
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Product: Pen, Price: -5"}
|
||||
],
|
||||
response_model=Product
|
||||
)
|
||||
```
|
||||
|
||||
In this example, the initial response will likely fail validation because:
|
||||
- The price is negative (violating the `gt=0` constraint)
|
||||
- Instructor will automatically retry with feedback about these issues
|
||||
|
||||
For more details on max_retries configuration, see the [Retrying](../../concepts/retrying.md) concepts page.
|
||||
|
||||
## Customizing Retry Behavior
|
||||
|
||||
You can customize retry behavior when initializing the Instructor client:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
|
||||
# Customize retry behavior
|
||||
client = instructor.from_provider(
|
||||
"openai/gpt-4o",
|
||||
max_retries=3, # Maximum number of retries
|
||||
retry_if_parsing_fails=True, # Retry on JSON parsing failures
|
||||
throw_error=True # Throw an error if all retries fail
|
||||
)
|
||||
```
|
||||
|
||||
### Retry Configuration Options
|
||||
|
||||
| Option | Description | Default |
|
||||
|--------|-------------|---------|
|
||||
| `max_retries` | Maximum number of retry attempts | 0 |
|
||||
| `retry_if_parsing_fails` | Whether to retry if JSON parsing fails | True |
|
||||
| `throw_error` | Whether to throw an error if all retries fail | True |
|
||||
|
||||
## Handling Retry Failures
|
||||
|
||||
When all retries fail, Instructor raises an `InstructorRetryException` that contains comprehensive information about all failed attempts:
|
||||
|
||||
```python
|
||||
from instructor.core.exceptions import InstructorRetryException
|
||||
|
||||
try:
|
||||
response = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[{"role": "user", "content": "Product: Invalid data"}],
|
||||
response_model=Product,
|
||||
max_retries=3
|
||||
)
|
||||
except InstructorRetryException as e:
|
||||
print(f"Failed after {e.n_attempts} attempts")
|
||||
print(f"Total usage: {e.total_usage}")
|
||||
|
||||
# New: Access detailed information about each failed attempt
|
||||
for attempt in e.failed_attempts:
|
||||
print(f"Attempt {attempt.attempt_number}: {attempt.exception}")
|
||||
if attempt.completion:
|
||||
# Analyze the raw completion that failed validation
|
||||
print(f"Raw response: {attempt.completion}")
|
||||
```
|
||||
|
||||
The `InstructorRetryException` now includes:
|
||||
|
||||
- `failed_attempts`: A list of `FailedAttempt` objects containing:
|
||||
- `attempt_number`: The retry attempt number
|
||||
- `exception`: The specific exception that occurred
|
||||
- `completion`: The raw LLM response (when available)
|
||||
- `n_attempts`: Total number of attempts made
|
||||
- `total_usage`: Total token usage across all attempts
|
||||
- `last_completion`: The final failed completion
|
||||
- `messages`: The conversation history
|
||||
|
||||
This comprehensive tracking enables better debugging and analysis of retry patterns.
|
||||
|
||||
For more on handling validation failures, see [Fallback Strategies](../../concepts/error_handling.md).
|
||||
|
||||
## Error Messages and Feedback
|
||||
|
||||
Instructor provides detailed error messages to the LLM during retries:
|
||||
|
||||
```
|
||||
The following errors occurred during validation:
|
||||
- price: ensure this value is greater than 0
|
||||
- name: Product name must be at least 3 characters
|
||||
|
||||
Please fix these errors and ensure the response is valid.
|
||||
```
|
||||
|
||||
This feedback helps the LLM understand exactly what needs to be fixed.
|
||||
|
||||
## Retry Limitations
|
||||
|
||||
While retries are powerful, they have some limitations:
|
||||
|
||||
1. **Retry Budget**: Each retry consumes tokens and time
|
||||
2. **Persistent Errors**: Some errors might not be fixable by the LLM
|
||||
3. **Model Limitations**: Some models may consistently struggle with certain validations
|
||||
|
||||
For complex validation scenarios, consider implementing [Custom Validators](custom_validators.md) or [Field-level Validation](field_level_validation.md).
|
||||
|
||||
## Advanced Retry Pattern: Progressive Validation
|
||||
|
||||
For complex schemas, you can implement a progressive validation pattern:
|
||||
|
||||
```python
|
||||
import instructor
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
# Initialize with moderate retries
|
||||
client = instructor.from_provider(
|
||||
"openai/gpt-4o",
|
||||
max_retries=2
|
||||
)
|
||||
|
||||
# Basic validation first
|
||||
class BasicProduct(BaseModel):
|
||||
name: str
|
||||
price: float = Field(..., gt=0)
|
||||
|
||||
# Advanced validation second
|
||||
class DetailedProduct(BasicProduct):
|
||||
description: str = Field(..., min_length=10)
|
||||
category: str
|
||||
in_stock: bool
|
||||
|
||||
# Two-step extraction with validation
|
||||
try:
|
||||
# First get basic fields
|
||||
basic = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": "Product: Mini Pen, Price: $2.50"}
|
||||
],
|
||||
response_model=BasicProduct
|
||||
)
|
||||
|
||||
# Then get full details with context from the first step
|
||||
detailed = client.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{"role": "user", "content": f"Provide more details about {basic.name} which costs ${basic.price}"}
|
||||
],
|
||||
response_model=DetailedProduct
|
||||
)
|
||||
except Exception as e:
|
||||
# Handle validation failures
|
||||
print(f"Validation failed: {e}")
|
||||
```
|
||||
|
||||
## Related Resources
|
||||
|
||||
- [Retrying](../../concepts/retrying.md) - Core retry concepts
|
||||
- [Validation](../../concepts/validation.md) - Main validation documentation
|
||||
- [Custom Validators](../../concepts/reask_validation.md) - Creating custom validation logic
|
||||
- [Fallback Strategies](../../concepts/error_handling.md) - Handling persistent validation failures
|
||||
- [Self Critique](../../examples/self_critique.md) - Example of model self-correction
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Learn about [Field-level Validation](field_level_validation.md)
|
||||
- Implement [Custom Validators](custom_validators.md)
|
||||
Reference in New Issue
Block a user