참고소스 수정본
This commit is contained in:
48
참고/instructor-main/examples/validators/allm_validator.py
Normal file
48
참고/instructor-main/examples/validators/allm_validator.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import asyncio
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel, BeforeValidator
|
||||
from instructor import llm_validator, patch
|
||||
from openai import AsyncOpenAI
|
||||
|
||||
aclient = AsyncOpenAI()
|
||||
|
||||
patch()
|
||||
|
||||
|
||||
class QuestionAnswerNoEvil(BaseModel):
|
||||
question: str
|
||||
answer: Annotated[
|
||||
str,
|
||||
BeforeValidator(
|
||||
llm_validator("don't say objectionable things", allow_override=True)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
async def main():
|
||||
context = "The according to the devil is to live a life of sin and debauchery."
|
||||
question = "What is the meaning of life?"
|
||||
|
||||
try:
|
||||
qa: QuestionAnswerNoEvil = await aclient.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=QuestionAnswerNoEvil,
|
||||
max_retries=2,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a system that answers questions based on the context. Answer exactly what the question asks using the context.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"using the context: {context}\n\nAnswer the following question: {question}",
|
||||
},
|
||||
],
|
||||
) # type: ignore
|
||||
print(qa)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
39
참고/instructor-main/examples/validators/annotator.py
Normal file
39
참고/instructor-main/examples/validators/annotator.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel, ValidationError
|
||||
from pydantic.functional_validators import AfterValidator
|
||||
|
||||
|
||||
def name_must_contain_space(v: str) -> str:
|
||||
if " " not in v:
|
||||
raise ValueError("name must be a first and last name separated by a space")
|
||||
return v.lower()
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: Annotated[str, AfterValidator(name_must_contain_space)]
|
||||
|
||||
|
||||
# Example 1) Valid input, notice that the name is lowercased
|
||||
person: UserDetail = UserDetail(age=29, name="Jason Liu")
|
||||
print(person.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"age": 29,
|
||||
"name": "jason liu"
|
||||
}
|
||||
"""
|
||||
|
||||
# Example 2) Invalid input, we'll get a validation error
|
||||
# In the future this validation error will be raised by the API and
|
||||
# used by the LLM to generate a better response
|
||||
try:
|
||||
person: UserDetail = UserDetail(age=29, name="Jason")
|
||||
except ValidationError as e:
|
||||
print(e)
|
||||
"""
|
||||
1 validation error for UserDetail
|
||||
name
|
||||
Value error, name must be a first and last name separated by a space [type=value_error, input_value='Jason', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.3/v/value_error
|
||||
"""
|
||||
@@ -0,0 +1,66 @@
|
||||
import instructor
|
||||
from openai import OpenAI
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
from typing import Optional
|
||||
|
||||
# Enables `response_model` and `max_retries` parameters
|
||||
client = instructor.from_openai(OpenAI())
|
||||
|
||||
|
||||
class Validation(BaseModel):
|
||||
is_valid: bool = Field(
|
||||
..., description="Whether the value is valid given the rules"
|
||||
)
|
||||
error_message: Optional[str] = Field(
|
||||
...,
|
||||
description="The error message if the value is not valid, to be used for re-asking the model",
|
||||
)
|
||||
|
||||
|
||||
def validator(values):
|
||||
chain_of_thought = values["chain_of_thought"]
|
||||
answer = values["answer"]
|
||||
resp = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a validator. Determine if the value is valid for the statement. If it is not, explain why.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"Verify that `{answer}` follows the chain of thought: {chain_of_thought}",
|
||||
},
|
||||
],
|
||||
# this comes from instructor.from_openai()
|
||||
response_model=Validation,
|
||||
)
|
||||
if not resp.is_valid:
|
||||
raise ValueError(resp.error_message)
|
||||
return values
|
||||
|
||||
|
||||
class Response(BaseModel):
|
||||
chain_of_thought: str
|
||||
answer: str
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def chain_of_thought_makes_sense(cls, data):
|
||||
return validator(data)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
try:
|
||||
resp = Response(
|
||||
chain_of_thought="1 + 1 = 2", answer="The meaning of life is 42"
|
||||
)
|
||||
print(resp)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
"""
|
||||
1 validation error for Response
|
||||
Value error, The statement 'The meaning of life is 42' does not follow the chain of thought: 1 + 1 = 2.
|
||||
[type=value_error, input_value={'chain_of_thought': '1 +... meaning of life is 42'}, input_type=dict]
|
||||
"""
|
||||
46
참고/instructor-main/examples/validators/citations.py
Normal file
46
참고/instructor-main/examples/validators/citations.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel, ValidationError, ValidationInfo, AfterValidator
|
||||
from openai import OpenAI
|
||||
import instructor
|
||||
|
||||
client = instructor.from_openai(OpenAI())
|
||||
|
||||
|
||||
def citation_exists(v: str, info: ValidationInfo):
|
||||
context = info.context
|
||||
if context:
|
||||
context = context.get("text_chunk")
|
||||
if v not in context:
|
||||
raise ValueError(f"Citation `{v}` not found in text")
|
||||
return v
|
||||
|
||||
|
||||
Citation = Annotated[str, AfterValidator(citation_exists)]
|
||||
|
||||
|
||||
class AnswerWithCitation(BaseModel):
|
||||
answer: str
|
||||
citation: Citation
|
||||
|
||||
|
||||
try:
|
||||
q = "Are blue berries high in protein?"
|
||||
text_chunk = """
|
||||
Blueberries are a good source of vitamin K.
|
||||
They also contain vitamin C, fibre, manganese and other antioxidants (notably anthocyanins).
|
||||
"""
|
||||
|
||||
resp = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=AnswerWithCitation,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"Answer the question `{q}` using the text chunk\n`{text_chunk}`",
|
||||
},
|
||||
],
|
||||
validation_context={"text_chunk": text_chunk},
|
||||
) # type: ignore
|
||||
print(resp.model_dump_json(indent=2))
|
||||
except ValidationError as e:
|
||||
print(e)
|
||||
40
참고/instructor-main/examples/validators/competitors.py
Normal file
40
참고/instructor-main/examples/validators/competitors.py
Normal file
@@ -0,0 +1,40 @@
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel, ValidationError, AfterValidator
|
||||
from openai import OpenAI
|
||||
|
||||
import instructor
|
||||
|
||||
client = instructor.from_openai(OpenAI())
|
||||
|
||||
|
||||
def no_competitors(v: str) -> str:
|
||||
# does not allow the competitors of mcdonalds
|
||||
competitors = ["burger king", "wendy's", "carl's jr", "jack in the box"]
|
||||
for competitor in competitors:
|
||||
if competitor in v.lower():
|
||||
raise ValueError(
|
||||
f"""Let them know that you are work for and are only allowed to talk about mcdonalds.
|
||||
Do not apologize. Do not even mention `{competitor}` since they are a a competitor of McDonalds"""
|
||||
)
|
||||
return v
|
||||
|
||||
|
||||
class Response(BaseModel):
|
||||
message: Annotated[str, AfterValidator(no_competitors)]
|
||||
|
||||
|
||||
try:
|
||||
resp = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=Response,
|
||||
max_retries=2,
|
||||
messages=[
|
||||
{
|
||||
"role": "user",
|
||||
"content": "What is your favourite order at burger king?",
|
||||
},
|
||||
],
|
||||
) # type: ignore
|
||||
print(resp.model_dump_json(indent=2))
|
||||
except ValidationError as e:
|
||||
print(e)
|
||||
42
참고/instructor-main/examples/validators/field_validator.py
Normal file
42
참고/instructor-main/examples/validators/field_validator.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from pydantic import BaseModel, ValidationError, field_validator
|
||||
|
||||
|
||||
class UserDetail(BaseModel):
|
||||
age: int
|
||||
name: str
|
||||
|
||||
@field_validator("name", mode="before")
|
||||
def name_must_contain_space(cls, v):
|
||||
"""
|
||||
This validator will be called after the default validator,
|
||||
and will raise a validation error if the name does not contain a space.
|
||||
then it will set the name to be lower case
|
||||
"""
|
||||
if " " not in v:
|
||||
raise ValueError("name be a first and last name separated by a space")
|
||||
return v.lower()
|
||||
|
||||
|
||||
# Example 1) Valid input, notice that the name is lowercased
|
||||
person = UserDetail(age=29, name="Jason Liu")
|
||||
print(person.model_dump_json(indent=2))
|
||||
"""
|
||||
{
|
||||
"age": 29,
|
||||
"name": "jason liu"
|
||||
}
|
||||
"""
|
||||
|
||||
# Example 2) Invalid input, we'll get a validation error
|
||||
# In the future this validation error will be raised by the API and
|
||||
# used by the LLM to generate a better response
|
||||
try:
|
||||
person = UserDetail(age=29, name="Jason")
|
||||
except ValidationError as e:
|
||||
print(e)
|
||||
"""
|
||||
1 validation error for UserDetail
|
||||
name
|
||||
Value error, must contain a space [type=value_error, input_value='Jason', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.3/v/value_error
|
||||
"""
|
||||
31
참고/instructor-main/examples/validators/just_a_guy.py
Normal file
31
참고/instructor-main/examples/validators/just_a_guy.py
Normal file
@@ -0,0 +1,31 @@
|
||||
from pydantic import BaseModel, ValidationError, field_validator, ValidationInfo
|
||||
|
||||
|
||||
class AnswerWithCitation(BaseModel):
|
||||
answer: str
|
||||
citation: str
|
||||
|
||||
@field_validator("citation")
|
||||
@classmethod
|
||||
def remove_stopwords(cls, v: str, info: ValidationInfo):
|
||||
context = info.context
|
||||
if context:
|
||||
text_chunks = context.get("text_chunk")
|
||||
if v not in text_chunks:
|
||||
raise ValueError(f"Citation `{v}` not found in text chunks")
|
||||
return v
|
||||
|
||||
|
||||
try:
|
||||
AnswerWithCitation.model_validate(
|
||||
{"answer": "Jason is a cool guy", "citation": "Jason is cool"},
|
||||
context={"text_chunk": "Jason is just a guy"},
|
||||
)
|
||||
except ValidationError as e:
|
||||
print(e)
|
||||
"""
|
||||
1 validation error for AnswerWithCitation
|
||||
citation
|
||||
Value error, Citation `Jason is cool`` not found in text chunks [type=value_error, input_value='Jason is cool', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.4/v/value_error
|
||||
"""
|
||||
118
참고/instructor-main/examples/validators/llm_validator.py
Normal file
118
참고/instructor-main/examples/validators/llm_validator.py
Normal file
@@ -0,0 +1,118 @@
|
||||
import instructor
|
||||
|
||||
from openai import OpenAI
|
||||
from instructor import llm_validator
|
||||
from pydantic import BaseModel, ValidationError, BeforeValidator
|
||||
from typing import Annotated
|
||||
|
||||
# Apply the patch to the OpenAI client
|
||||
client = instructor.from_openai(OpenAI())
|
||||
|
||||
|
||||
class QuestionAnswer(BaseModel):
|
||||
question: str
|
||||
answer: str
|
||||
|
||||
|
||||
question = "What is the meaning of life?"
|
||||
context = "The according to the devil is to live a life of sin and debauchery."
|
||||
|
||||
qa: QuestionAnswer = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=QuestionAnswer,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a system that answers questions based on the context. answer exactly what the question asks using the context.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"using the context: {context}\n\nAnswer the following question: {question}",
|
||||
},
|
||||
],
|
||||
) # type: ignore
|
||||
|
||||
print("Before validation with `llm_validator`")
|
||||
print(qa.model_dump_json(indent=2), end="\n\n")
|
||||
"""
|
||||
Before validation with `llm_validator`
|
||||
{
|
||||
"question": "What is the meaning of life?",
|
||||
"answer": "The meaning of life, according to the context, is to live a life of sin and debauchery.",
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
class QuestionAnswerNoEvil(BaseModel):
|
||||
question: str
|
||||
answer: Annotated[
|
||||
str,
|
||||
BeforeValidator(
|
||||
llm_validator("don't say objectionable things", openai_client=client)
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
try:
|
||||
qa = QuestionAnswerNoEvil(
|
||||
question="What is the meaning of life?",
|
||||
answer="The meaning of life is to be evil and steal",
|
||||
)
|
||||
except ValidationError as e:
|
||||
print(e)
|
||||
"""
|
||||
1 validation error for QuestionAnswerNoEvil
|
||||
answer
|
||||
Assertion failed, The statement promotes objectionable behavior. [type=assertion_error, input_value='The meaning of life is to be evil and steal', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.4/v/assertion_error
|
||||
"""
|
||||
|
||||
try:
|
||||
qa: QuestionAnswerNoEvil = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=QuestionAnswerNoEvil,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a system that answers questions based on the context. answer exactly what the question asks using the context.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"using the context: {context}\n\nAnswer the following question: {question}",
|
||||
},
|
||||
],
|
||||
) # type: ignore
|
||||
except Exception as e:
|
||||
print(e, end="\n\n")
|
||||
"""
|
||||
1 validation error for QuestionAnswerNoEvil
|
||||
answer
|
||||
Assertion failed, The statement promotes sin and debauchery, which is objectionable. [type=assertion_error, input_value='The meaning of life is t... of sin and debauchery.', input_type=str]
|
||||
For further information visit https://errors.pydantic.dev/2.3/v/assertion_error
|
||||
"""
|
||||
|
||||
qa: QuestionAnswerNoEvil = client.chat.completions.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=QuestionAnswerNoEvil,
|
||||
max_retries=2,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a system that answers questions based on the context. answer exactly what the question asks using the context.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"using the context: {context}\n\nAnswer the following question: {question}",
|
||||
},
|
||||
],
|
||||
) # type: ignore
|
||||
|
||||
print("After validation with `llm_validator` with `max_retries=2`")
|
||||
print(qa.model_dump_json(indent=2), end="\n\n")
|
||||
"""
|
||||
After validation with `llm_validator` with `max_retries=2`
|
||||
{
|
||||
"question": "What is the meaning of life?",
|
||||
"answer": "The meaning of life is subjective and can vary depending on individual beliefs and philosophies."
|
||||
}
|
||||
"""
|
||||
16
참고/instructor-main/examples/validators/moderation.py
Normal file
16
참고/instructor-main/examples/validators/moderation.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import instructor
|
||||
|
||||
from instructor import openai_moderation
|
||||
|
||||
from typing import Annotated
|
||||
from pydantic import BaseModel, AfterValidator
|
||||
from openai import OpenAI
|
||||
|
||||
client = instructor.from_openai(OpenAI())
|
||||
|
||||
|
||||
class Response(BaseModel):
|
||||
message: Annotated[str, AfterValidator(openai_moderation(client=client))]
|
||||
|
||||
|
||||
response = Response(message="I want to make them suffer the consequences")
|
||||
152
참고/instructor-main/examples/validators/readme.md
Normal file
152
참고/instructor-main/examples/validators/readme.md
Normal file
@@ -0,0 +1,152 @@
|
||||
# Using `llm_validator` with OpenAI's GPT-3.5 Turbo and Pydantic for Text Validation with Output Examples
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines how to use a custom text validation logic (`llm_validator`) with OpenAI's GPT-3.5 Turbo and Pydantic, including the outputs for each operation.
|
||||
|
||||
## Code Explanation
|
||||
|
||||
### Basic Setup
|
||||
|
||||
Import necessary modules and apply patches for compatibility.
|
||||
|
||||
```python
|
||||
from typing_extensions import Annotated
|
||||
from pydantic import (
|
||||
BaseModel,
|
||||
BeforeValidator,
|
||||
)
|
||||
from instructor import llm_validator, patch
|
||||
import openai
|
||||
|
||||
patch()
|
||||
```
|
||||
|
||||
### Defining Response Models
|
||||
|
||||
Define a basic Pydantic model named `QuestionAnswer`.
|
||||
|
||||
```python
|
||||
class QuestionAnswer(BaseModel):
|
||||
question: str
|
||||
answer: str
|
||||
```
|
||||
|
||||
### Generating a Response
|
||||
|
||||
Generate a response from GPT-3.5 Turbo.
|
||||
|
||||
```python
|
||||
question = "What is the meaning of life?"
|
||||
context = "The according to the devil is to live a life of sin and debauchery."
|
||||
|
||||
qa: QuestionAnswer = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=QuestionAnswer,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a system that answers questions based on the context. answer exactly what the question asks using the context.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"using the context: {context}\n\nAnswer the following question: {question}",
|
||||
},
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
Before validation with `llm_validator`:
|
||||
|
||||
```json
|
||||
{
|
||||
"question": "What is the meaning of life?",
|
||||
"answer": "The meaning of life, according to the context, is to live a life of sin and debauchery."
|
||||
}
|
||||
```
|
||||
|
||||
### Adding Custom Validation
|
||||
|
||||
Add custom validation using `llm_validator`.
|
||||
|
||||
```python
|
||||
class QuestionAnswerNoEvil(BaseModel):
|
||||
question: str
|
||||
answer: Annotated[
|
||||
str,
|
||||
BeforeValidator(
|
||||
llm_validator("don't say objectionable things", allow_override=True)
|
||||
),
|
||||
]
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
```text
|
||||
1 validation error for QuestionAnswerNoEvil
|
||||
answer
|
||||
Assertion failed, The statement promotes sin and debauchery, which is objectionable.
|
||||
```
|
||||
|
||||
### Handling Validation Errors
|
||||
|
||||
Catch exceptions raised by the validation.
|
||||
|
||||
```python
|
||||
try:
|
||||
qa: QuestionAnswerNoEvil = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=QuestionAnswerNoEvil,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a system that answers questions based on the context. answer exactly what the question asks using the context.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"using the context: {context}\n\nAnswer the following question: {question}",
|
||||
},
|
||||
],
|
||||
)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
```
|
||||
|
||||
### Retrying Validation
|
||||
|
||||
Allow for retries by setting `max_retries=2`.
|
||||
|
||||
```python
|
||||
qa: QuestionAnswerNoEvil = openai.ChatCompletion.create(
|
||||
model="gpt-3.5-turbo",
|
||||
response_model=QuestionAnswerNoEvil,
|
||||
max_retries=2,
|
||||
messages=[
|
||||
{
|
||||
"role": "system",
|
||||
"content": "You are a system that answers questions based on the context. answer exactly what the question asks using the context.",
|
||||
},
|
||||
{
|
||||
"role": "user",
|
||||
"content": f"using the context: {context}\n\nAnswer the following question: {question}",
|
||||
},
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
#### Output
|
||||
|
||||
After validation with `llm_validator` and `max_retries=2`:
|
||||
|
||||
```json
|
||||
{
|
||||
"question": "What is the meaning of life?",
|
||||
"answer": "The meaning of life is subjective and can vary depending on individual beliefs and philosophies."
|
||||
}
|
||||
```
|
||||
|
||||
## Summary
|
||||
|
||||
This document described how to use `llm_validator` with OpenAI's GPT-3.5 Turbo and Pydantic, including example outputs. This approach allows for controlled and filtered responses.
|
||||
Reference in New Issue
Block a user