钩子¶
Hooks 提供了一种强大的机制,用于在 Instructor 库的完成和解析过程中拦截和处理事件。它们允许您在 API 交互的各个阶段添加自定义行为、日志记录或错误处理。
概述¶
Instructor 中的 Hooks 系统基于 Hooks 类,该类管理事件的注册和触发。它支持多个预定义的事件,这些事件对应于完成和解析过程的不同阶段。
支持的钩子事件¶
completion:kwargs¶
当提供完成参数时,会触发此钩子。它接收传递给完成函数的所有参数。这些参数将包含model、messages、tools,在任何response_model或validation_context参数转换为各自的值之后。
completion:response¶
当接收到完成响应时,会触发此钩子。它从完成API接收原始响应对象。
completion:error¶
当在完成过程中发生错误时,此钩子会在任何重试尝试之前发出,并且响应被解析为pydantic模型。
parse:error¶
当在将响应解析为pydantic模型时发生错误时,会触发此钩子。如果响应无效或pydantic模型与响应不兼容,可能会发生这种情况。
completion:last_attempt¶
当进行最后一次重试尝试时,会触发此钩子。
实现细节¶
Hooks系统在instructor/hooks.py文件中实现。Hooks类处理钩子事件的注册和触发。您可以参考此文件以了解钩子是如何在底层工作的。使用Hooks的重试逻辑在instructor/retry.py文件中实现。这展示了在完成过程中出现错误后重试时如何使用Hooks。
注册钩子¶
你可以使用Instructor客户端的on方法或Hooks实例来注册钩子。以下是一个示例:
import instructor
import openai
import pprint
client = instructor.from_openai(openai.OpenAI())
def log_completion_kwargs(*args, **kwargs):
pprint.pprint({"args": args, "kwargs": kwargs})
client.on("completion:kwargs", log_completion_kwargs)
resp = client.chat.completions.create(
model="gpt-3.5-turbo",
messages=[{"role": "user", "content": "Hello, world!"}],
response_model=str,
)
print(resp)
#> Hello, world!
发射事件¶
事件会在适当的时候由Instructor库自动发出。在大多数情况下,您不需要手动发出事件。
移除钩子¶
你可以使用off方法来移除特定的钩子:
清除钩子¶
要删除特定事件或所有事件的所有钩子:
# Clear hooks for a specific event
client.clear("completion:kwargs")
# Clear all hooks
client.clear()
示例:日志记录和调试¶
这里有一个全面的示例,展示了如何使用钩子进行日志记录和调试:
import instructor
import openai
import pydantic
def log_completion_kwargs(kwargs) -> None:
print("## Completion kwargs:")
print(kwargs)
"""
{
"messages": [
{
"role": "user",
"content": "Extract the user name and age from the following text: 'John is 20 years old'",
}
],
"model": "gpt-4o-mini",
"tools": [
{
"type": "function",
"function": {
"name": "User",
"description": "Correctly extracted `User` with all the required parameters with correct types",
"parameters": {
"properties": {
"name": {"title": "Name", "type": "string"},
"age": {"title": "Age", "type": "integer"},
},
"required": ["age", "name"],
"type": "object",
},
},
}
],
"tool_choice": {"type": "function", "function": {"name": "User"}},
}
"""
def log_completion_response(response) -> None:
print("## Completion response:")
#> ## Completion response:
"""
{
'id': 'chatcmpl-AWl4Mj5Jrv7m7JkOTIiHXSldQIOFm',
'choices': [
{
'finish_reason': 'stop',
'index': 0,
'logprobs': None,
'message': {
'content': None,
'refusal': None,
'role': 'assistant',
'audio': None,
'function_call': None,
'tool_calls': [
{
'id': 'call_6oQ9WXxeSiVEV71B9IYtsbIE',
'function': {
'arguments': '{"name":"John","age":-1}',
'name': 'User',
},
'type': 'function',
}
],
},
}
],
'created': 1732370794,
'model': 'gpt-4o-mini-2024-07-18',
'object': 'chat.completion',
'service_tier': None,
'system_fingerprint': 'fp_0705bf87c0',
'usage': {
'completion_tokens': 10,
'prompt_tokens': 87,
'total_tokens': 97,
'completion_tokens_details': {
'audio_tokens': 0,
'reasoning_tokens': 0,
'accepted_prediction_tokens': 0,
'rejected_prediction_tokens': 0,
},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0},
},
}
"""
print(response.model_dump())
"""
{
'id': 'chatcmpl-AWl4Mxdq0BUGRlVCA61z8YOIVga7F',
'choices': [
{
'finish_reason': 'stop',
'index': 0,
'logprobs': None,
'message': {
'content': None,
'refusal': None,
'role': 'assistant',
'audio': None,
'function_call': None,
'tool_calls': [
{
'id': 'call_EJIEr27Mb6sdbplnYw4iBWlm',
'function': {
'arguments': '{"name":"John","age":10}',
'name': 'User',
},
'type': 'function',
}
],
},
}
],
'created': 1732370794,
'model': 'gpt-4o-mini-2024-07-18',
'object': 'chat.completion',
'service_tier': None,
'system_fingerprint': 'fp_0705bf87c0',
'usage': {
'completion_tokens': 9,
'prompt_tokens': 87,
'total_tokens': 96,
'completion_tokens_details': {
'audio_tokens': 0,
'reasoning_tokens': 0,
'accepted_prediction_tokens': 0,
'rejected_prediction_tokens': 0,
},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0},
},
}
"""
def log_completion_error(error) -> None:
print("## Completion error:")
print({"error": error})
def log_parse_error(error) -> None:
print("## Parse error:")
#> ## Parse error:
print(error)
"""
1 validation error for User
age
Value error, Age cannot be negative [type=value_error, input_value=-10, input_type=int]
For further information visit https://errors.pydantic.dev/2.8/v/value_error
"""
# Create an Instructor client
client = instructor.from_openai(openai.OpenAI())
client.on("completion:kwargs", log_completion_kwargs)
client.on("completion:response", log_completion_response)
client.on("completion:error", log_completion_error)
client.on("parse:error", log_parse_error)
# Define a model with a validator
class User(pydantic.BaseModel):
name: str
age: int
@pydantic.field_validator("age")
def check_age(cls, v: int) -> int:
if v < 0:
raise ValueError("Age cannot be negative")
return v
try:
# Use the client to create a completion
user = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
#> ## Parse error:
"role": "user",
"content": "Extract the user name and age from the following text: 'John is -1 years old'",
"""
1 validation error for User
age
Value error, Age cannot be negative [type=value_error, input_value=-1, input_type=int]
For further information visit https://errors.pydantic.dev/2.9/v/value_error
"""
}
],
response_model=User,
max_retries=1,
)
except Exception as e:
print(f"Error: {e}")
user = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": "Extract the user name and age from the following text: 'John is 10 years old'",
}
],
response_model=User,
max_retries=1,
)
print(user)
#> name='John' age=10
"""
Error: 1 validation error for User
age
Value error, Age cannot be negative [type=value_error, input_value=-1, input_type=int]
For further information visit https://errors.pydantic.dev/2.9/v/value_error
"""
这个示例演示:
- 为不同事件定义钩子处理程序。
- 与Instructor客户端注册钩子。
- 使用带验证器的 Pydantic 模型。
- 发起一个完成请求,将触发各种钩子。
钩子将在过程的不同阶段记录信息,帮助调试和理解数据流。
最佳实践¶
-
错误处理:始终在您的钩子处理程序中包含错误处理,以防止异常中断主执行流程。如果在钩子处理程序中引发异常,我们将自动发出警告。
-
性能:保持钩子处理程序轻量级,以避免影响主应用程序的性能。
-
模块化:使用钩子来分离关注点。例如,使用钩子进行日志记录、监控或自定义业务逻辑,而不会使主代码变得混乱。
-
一致性:在所有钩子中使用相同的命名约定和模式,以提高可维护性。
-
文档: 记录每个钩子处理程序的目的和预期的输入/输出,以便于协作和维护。
通过有效利用钩子,您可以在使用Instructor库和语言模型时创建更灵活、可调试和可维护的应用程序。