验证与 Reasking¶
与其将 AI 中的“自我批评”或“自我反思”视为新概念,不如将它们视为带有清晰错误消息的验证错误,系统可以使用这些错误消息进行自我修正。
Pydantic¶
Pydantic 为 Python 提供了可定制且富有表现力的验证框架。Instructor 利用 Pydantic 的验证框架,为基于代码和基于 LLM 的验证提供统一的开发者体验,并提供基于验证错误纠正 LLM 输出的重问(reasking)机制。要了解更多信息,请查看关于验证器的 Pydantic 文档。
好的 LLM 验证就是好的验证
如果您想查看更多关于验证器的示例,请查看我们的博客文章好的 LLM 验证就是好的验证。
基于代码的验证示例¶
首先使用 typing_extensions
中的 Annotation
类定义一个带有验证器的 Pydantic 模型。
使用 Pydantic 的内置验证强制执行命名规则
from pydantic import BaseModel, ValidationError
from typing_extensions import Annotated
from pydantic import AfterValidator
def name_must_contain_space(v: str) -> str:
if " " not in v:
raise ValueError("Name must contain a space.")
return v.lower()
class UserDetail(BaseModel):
age: int
name: Annotated[str, AfterValidator(name_must_contain_space)]
try:
person = UserDetail(age=29, name="Jason")
except ValidationError as e:
print(e)
"""
1 validation error for UserDetail
name
Value error, Name must contain a space. [type=value_error, input_value='Jason', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/value_error
"""
基于代码的验证输出¶
正如我们所见,当 name 属性不包含空格时,Pydantic 会引发验证错误。这是一个简单的示例,但它展示了如何使用 Pydantic 来验证模型的属性。
基于 LLM 的验证示例¶
基于 LLM 的验证也可以接入到同一个 Pydantic 模型中。在这里,如果 answer 属性包含违反“不要说令人反感的内容”规则的内容,Pydantic 将会引发验证错误。
import instructor
from openai import OpenAI
from instructor import llm_validator
from pydantic import BaseModel, ValidationError, BeforeValidator
from typing_extensions import Annotated
# Apply the patch to the OpenAI client
client = instructor.from_openai(OpenAI())
class QuestionAnswer(BaseModel):
question: str
answer: Annotated[
str,
BeforeValidator(llm_validator("don't say objectionable things", client=client)),
]
try:
qa = QuestionAnswer(
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 QuestionAnswer
answer
Assertion failed, The statement promotes objectionable behavior by encouraging evil and stealing. [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.9/v/assertion_error
"""
基于 LLM 的验证输出¶
需要注意的是,这里的错误消息是由 LLM 而非代码生成的,因此对于重新向模型提问(re-asking)会很有帮助。
1 validation error for QuestionAnswer
answer
Assertion failed, The statement is objectionable. (type=assertion_error)
使用 Reasking 逻辑纠正输出¶
验证器是确保输出某些属性的绝佳工具。当您使用 patch()
方法与 openai
客户端时,可以使用 max_retries
参数来设置重新向模型提问以纠正输出的次数。
它是抵御两种形式不良输出的绝佳防御层:
- Pydantic 验证错误(基于代码或基于 LLM)
- JSON 解码错误(当模型返回不良响应时)
步骤 1:使用验证器定义响应模型¶
请注意,字段验证器要求 name 为大写,但用户输入是小写。如果 name 不是大写,验证器将引发一个 ValueError
。
import openai
import instructor
from pydantic import BaseModel, field_validator
# Apply the patch to the OpenAI client
client = instructor.from_openai(openai.OpenAI())
class UserDetails(BaseModel):
name: str
age: int
@field_validator("name")
@classmethod
def validate_name(cls, v):
if v.upper() != v:
raise ValueError("Name must be in uppercase.")
return v
步骤 2:使用带有重试的客户端¶
在这里,UserDetails
模型作为 response_model
传入,并且 max_retries
设置为 2。
import instructor
import openai
from pydantic import BaseModel
client = instructor.from_openai(openai.OpenAI(), mode=instructor.Mode.TOOLS)
class UserDetails(BaseModel):
name: str
age: int
model = client.chat.completions.create(
model="gpt-3.5-turbo",
response_model=UserDetails,
max_retries=2,
messages=[
{"role": "user", "content": "Extract jason is 25 years old"},
],
)
print(model.model_dump_json(indent=2))
"""
{
"name": "Jason",
"age": 25
}
"""
幕后发生了什么?¶
在幕后,instructor.from_openai()
方法将 max_retries
参数添加到 openai.ChatCompletion.create()
方法中。如果 UserDetails
中的 name
属性未能通过大写验证,max_retries
参数将触发最多 2 次重试。
from pydantic import ValidationError
try:
...
except ValidationError as e:
kwargs["messages"].append(response.choices[0].message)
kwargs["messages"].append(
{
"role": "user",
"content": f"Please correct the function call; errors encountered:\n{e}",
}
)
高级验证技术¶
目前文档尚不完整,但我们有一些正在努力改进文档的高级验证技术,例如模型级验证和使用验证上下文。请查看我们关于验证引文的示例,其中涵盖了
- 验证包含所有属性的整个对象,而不是一次验证一个属性
- 使用一些“上下文”来验证对象:在此示例中,我们使用
context
来检查引文是否在原始文本中存在。
优化 Token 使用量¶
Pydantic 在抛出错误时会自动在错误消息中包含一个 URL,以便用户可以了解更多关于所抛出特定错误的信息。有些用户可能希望移除此 URL,因为它增加了额外的 Token,否则可能不会为验证过程带来太多价值。
我们创建了一个小的辅助函数,您可以在下方使用它来移除错误消息中的此 URL
from instructor.utils import disable_pydantic_error_url
from pydantic import BaseModel, ValidationError
from typing_extensions import Annotated
from pydantic import AfterValidator
disable_pydantic_error_url() # (1)!
def name_must_contain_space(v: str) -> str:
if " " not in v:
raise ValueError("Name must contain a space.")
return v.lower()
class UserDetail(BaseModel):
age: int
name: Annotated[str, AfterValidator(name_must_contain_space)]
try:
person = UserDetail(age=29, name="Jason")
except ValidationError as e:
print(e)
"""
1 validation error for UserDetail
name
Value error, Name must contain a space. [type=value_error, input_value='Jason', input_type=str]
For further information visit https://errors.pydantic.dev/2.9/v/value_error
"""
- 我们通过将环境变量
PYDANTIC_ERRORS_INCLUDE_URL
设置为0
来禁用此错误。这仅在脚本执行期间有效,函数调用结束后,原始行为将恢复。
要点总结¶
通过整合这些高级验证技术,我们不仅提高了 LLM 生成内容的质量和可靠性,也为构建更自主、更有效的系统铺平了道路。