Instructor,最受欢迎的简单结构化输出库¶
由llms驱动的结构化输出。设计简洁、透明且可控。
Instructor 使得从 GPT-3.5、GPT-4、GPT-4-Vision 等 LLMs 以及包括 Mistral/Mixtral、Ollama 和 llama-cpp-python 在内的开源模型中获取结构化数据(如 JSON)变得容易。
它以其简洁性、透明性和以用户为中心的设计脱颖而出,建立在Pydantic之上。Instructor 帮助您管理验证上下文,使用Tenacity进行重试,以及流式处理列表和部分响应。
如果你遇到困难,你可以随时运行 instructor docs 来在浏览器中打开文档。它甚至支持搜索特定主题。
新闻通讯¶
如果您想获取提示、新博客文章和研究的最新信息,请订阅我们的新闻通讯。以下是您可以期待的内容:
- 关于Instructor功能和版本的更新
- 关于人工智能和结构化输出的博客文章
- 来自我们社区的小贴士和技巧
- 在LLM和结构化输出领域的研究
- 关于Instructor的AI发展技能信息
订阅我们的新闻通讯以获取人工智能发展的最新动态。我们提供内容让您保持信息灵通,并帮助您在项目中使用Instructor。
入门指南¶
如果你想查看所有的集成,请查看集成指南。
使用OpenAI的结构化输出响应
您现在可以将OpenAI的结构化输出响应与Instructor结合使用。此功能结合了Instructor的优势与OpenAI的精确采样。
import instructor
from pydantic import BaseModel
from openai import OpenAI
# Define your desired output structure
class ExtractUser(BaseModel):
name: str
age: int
# Patch the OpenAI client
client = instructor.from_openai(OpenAI())
# Extract structured data from natural language
res = client.chat.completions.create(
model="gpt-4o-mini",
response_model=ExtractUser,
messages=[{"role": "user", "content": "John Doe is 30 years old."}],
)
assert res.name == "John Doe"
assert res.age == 30
from openai import OpenAI
from pydantic import BaseModel, Field
from typing import List
import instructor
class ExtractUser(BaseModel):
name: str
age: int
client = instructor.from_openai(
OpenAI(
base_url="http://localhost:11434/v1",
api_key="ollama",
),
mode=instructor.Mode.JSON,
)
resp = client.chat.completions.create(
model="llama3",
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
response_model=ExtractUser,
)
assert resp.name == "Jason"
assert resp.age == 25
import llama_cpp
import instructor
from llama_cpp.llama_speculative import LlamaPromptLookupDecoding
from pydantic import BaseModel
llama = llama_cpp.Llama(
model_path="../../models/OpenHermes-2.5-Mistral-7B-GGUF/openhermes-2.5-mistral-7b.Q4_K_M.gguf",
n_gpu_layers=-1,
chat_format="chatml",
n_ctx=2048,
draft_model=LlamaPromptLookupDecoding(num_pred_tokens=2),
logits_all=True,
verbose=False,
)
create = instructor.patch(
create=llama.create_chat_completion_openai_v1,
mode=instructor.Mode.JSON_SCHEMA,
)
class ExtractUser(BaseModel):
name: str
age: int
user = create(
messages=[
{
"role": "user",
"content": "Extract `Jason is 30 years old`",
}
],
response_model=ExtractUser,
)
assert user.name == "Jason"
assert user.age == 30
import instructor
from anthropic import Anthropic
from pydantic import BaseModel
class ExtractUser(BaseModel):
name: str
age: int
client = instructor.from_anthropic(Anthropic())
# note that client.chat.completions.create will also work
resp = client.messages.create(
model="claude-3-5-sonnet-20240620",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
response_model=ExtractUser,
)
assert isinstance(resp, ExtractUser)
assert resp.name == "Jason"
assert resp.age == 25
import instructor
import google.generativeai as genai
from pydantic import BaseModel
class ExtractUser(BaseModel):
name: str
age: int
client = instructor.from_gemini(
client=genai.GenerativeModel(
model_name="models/gemini-1.5-flash-latest",
),
mode=instructor.Mode.GEMINI_JSON,
)
# note that client.chat.completions.create will also work
resp = client.messages.create(
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
response_model=ExtractUser,
)
assert isinstance(resp, ExtractUser)
assert resp.name == "Jason"
assert resp.age == 25
import instructor
import vertexai # type: ignore
from vertexai.generative_models import GenerativeModel # type: ignore
from pydantic import BaseModel
vertexai.init()
class ExtractUser(BaseModel):
name: str
age: int
client = instructor.from_vertexai(
client=GenerativeModel("gemini-1.5-pro-preview-0409"),
mode=instructor.Mode.VERTEXAI_TOOLS,
)
# note that client.chat.completions.create will also work
resp = client.create(
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
response_model=ExtractUser,
)
assert isinstance(resp, ExtractUser)
assert resp.name == "Jason"
assert resp.age == 25
import instructor
from groq import Groq
from pydantic import BaseModel
client = instructor.from_groq(Groq())
class ExtractUser(BaseModel):
name: str
age: int
resp = client.chat.completions.create(
model="llama3-70b-8192",
response_model=ExtractUser,
messages=[{"role": "user", "content": "Extract Jason is 25 years old."}],
)
assert resp.name == "Jason"
assert resp.age == 25
import instructor
from litellm import completion
from pydantic import BaseModel
class ExtractUser(BaseModel):
name: str
age: int
client = instructor.from_litellm(completion)
resp = client.chat.completions.create(
model="claude-3-opus-20240229",
max_tokens=1024,
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
response_model=ExtractUser,
)
assert isinstance(resp, ExtractUser)
assert resp.name == "Jason"
assert resp.age == 25
import instructor
from pydantic import BaseModel
from cohere import Client
class ExtractUser(BaseModel):
name: str
age: int
client = instructor.from_cohere(Client())
resp = client.chat.completions.create(
response_model=ExtractUser,
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
)
assert resp.name == "Jason"
assert resp.age == 25
from cerebras.cloud.sdk import Cerebras
import instructor
from pydantic import BaseModel
import os
client = Cerebras(
api_key=os.environ.get("CEREBRAS_API_KEY"),
)
client = instructor.from_cerebras(client)
class ExtractUser(BaseModel):
name: str
age: int
resp = client.chat.completions.create(
model="llama3.1-70b",
response_model=ExtractUser,
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
)
assert resp.name == "Jason"
assert resp.age == 25
from fireworks.client import Fireworks
import instructor
from pydantic import BaseModel
import os
client = Fireworks(
api_key=os.environ.get("FIREWORKS_API_KEY"),
)
client = instructor.from_fireworks(client)
class ExtractUser(BaseModel):
name: str
age: int
resp = client.chat.completions.create(
model="accounts/fireworks/models/llama-v3p2-1b-instruct",
response_model=ExtractUser,
messages=[
{
"role": "user",
"content": "Extract Jason is 25 years old.",
}
],
)
assert resp.name == "Jason"
assert resp.age == 25
为什么使用Instructor?¶
-
具有完整提示控制的简单API
Instructor 提供了一个简单的 API,让您完全拥有和控制您的提示。这使得您可以对您的 LLM 交互进行精细的定制和优化。
-
多语言支持
使用类型提示和验证简化从LLMs中提取结构化数据。
-
重新询问和验证
当验证失败时自动重新询问模型,确保高质量的输出。利用Pydantic的验证进行强大的错误处理。
-
流式支持
轻松流式传输部分结果和可迭代对象,允许在应用程序中进行实时处理并提高响应性。
-
由类型提示驱动
利用Pydantic进行模式验证、提示控制、减少代码量以及IDE集成。
-
简化的LLM交互
支持 OpenAI, Anthropic, Google, Vertex AI, Mistral/Mixtral, Ollama, llama-cpp-python, Cohere, LiteLLM.
使用钩子¶
Instructor 包含一个钩子系统,允许您在语言模型交互过程中管理事件。钩子允许您在不同阶段拦截、记录和处理事件,例如在提供完成参数或收到响应时。该系统基于 Hooks 类,该类处理事件的注册和触发。您可以使用钩子来添加自定义行为,如日志记录或错误处理。以下是一个简单的示例,演示如何使用钩子:
import instructor
from openai import OpenAI
from pydantic import BaseModel
class UserInfo(BaseModel):
name: str
age: int
# Initialize the OpenAI client with Instructor
client = instructor.from_openai(OpenAI())
# Define hook functions
def log_kwargs(**kwargs):
print(f"Function called with kwargs: {kwargs}")
def log_exception(exception: Exception):
print(f"An exception occurred: {str(exception)}")
client.on("completion:kwargs", log_kwargs)
client.on("completion:error", log_exception)
user_info = client.chat.completions.create(
model="gpt-4o-mini",
response_model=UserInfo,
messages=[
{"role": "user", "content": "Extract the user name: 'John is 20 years old'"}
],
)
"""
{
'args': (),
'kwargs': {
'messages': [
{
'role': 'user',
'content': "Extract the user name: 'John is 20 years old'",
}
],
'model': 'gpt-3.5-turbo',
'tools': [
{
'type': 'function',
'function': {
'name': 'UserInfo',
'description': 'Correctly extracted `UserInfo` 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': 'UserInfo'}},
},
}
"""
print(f"Name: {user_info.name}, Age: {user_info.age}")
#> Name: John, Age: 20
此示例演示:1. 一个预执行钩子,用于记录传递给函数的所有kwargs。2. 一个异常钩子,用于记录执行期间发生的任何异常。
钩子提供了对函数输入和任何错误的宝贵洞察,增强了调试和监控能力。
了解更多关于钩子的信息 :octicons-arrow-right:
正确的类型推断¶
这是讲师的梦想,但由于OpenAI的修补,我无法使打字功能正常工作。现在,有了新的客户端,我们可以使打字功能正常工作!我们还添加了一些create_*方法,以便更容易创建可迭代对象和部分对象,并访问原始完成。
调用 create¶
import openai
import instructor
from pydantic import BaseModel
class User(BaseModel):
name: str
age: int
client = instructor.from_openai(openai.OpenAI())
user = client.chat.completions.create(
model="gpt-4-turbo-preview",
messages=[
{"role": "user", "content": "Create a user"},
],
response_model=User,
)
现在如果你使用一个IDE,你可以看到类型被正确推断出来。

处理异步:await create¶
这也将正确地与异步客户端一起工作。
import openai
import instructor
from pydantic import BaseModel
client = instructor.from_openai(openai.AsyncOpenAI())
class User(BaseModel):
name: str
age: int
async def extract():
return await client.chat.completions.create(
model="gpt-4-turbo-preview",
messages=[
{"role": "user", "content": "Create a user"},
],
response_model=User,
)
请注意,仅仅因为我们返回了create方法,extract()函数将返回正确的用户类型。

返回原始完成:create_with_completion¶
你也可以返回原始的完成对象
import openai
import instructor
from pydantic import BaseModel
client = instructor.from_openai(openai.OpenAI())
class User(BaseModel):
name: str
age: int
user, completion = client.chat.completions.create_with_completion(
model="gpt-4-turbo-preview",
messages=[
{"role": "user", "content": "Create a user"},
],
response_model=User,
)

流式部分对象:create_partial¶
为了处理流,我们仍然支持Iterable[T]和Partial[T],但为了简化类型推断,我们还添加了create_iterable和create_partial方法!
import openai
import instructor
from pydantic import BaseModel
client = instructor.from_openai(openai.OpenAI())
class User(BaseModel):
name: str
age: int
user_stream = client.chat.completions.create_partial(
model="gpt-4-turbo-preview",
messages=[
{"role": "user", "content": "Create a user"},
],
response_model=User,
)
for user in user_stream:
print(user)
#> name=None age=None
#> name=None age=None
#> name=None age=None
#> name=None age=None
#> name=None age=25
#> name=None age=25
#> name=None age=25
#> name=None age=25
#> name=None age=25
#> name=None age=25
#> name='John Doe' age=25
# name=None age=None
# name='' age=None
# name='John' age=None
# name='John Doe' age=None
# name='John Doe' age=30
现在注意到推断的类型是 Generator[User, None]

流式可迭代对象: create_iterable¶
当我们想要提取多个对象时,我们会得到一个可迭代的对象。
import openai
import instructor
from pydantic import BaseModel
client = instructor.from_openai(openai.OpenAI())
class User(BaseModel):
name: str
age: int
users = client.chat.completions.create_iterable(
model="gpt-4-turbo-preview",
messages=[
{"role": "user", "content": "Create 2 users"},
],
response_model=User,
)
for user in users:
print(user)
#> name='John Doe' age=30
#> name='Jane Doe' age=28
# User(name='John Doe', age=30)
# User(name='Jane Smith', age=25)

模板¶
Instructor 支持使用 Jinja 进行模板化,这使您可以创建动态提示。当您希望用数据填充提示的部分内容时,这非常有用。以下是一个简单的示例:
import openai
import instructor
from pydantic import BaseModel
client = instructor.from_openai(openai.OpenAI())
class User(BaseModel):
name: str
age: int
# Create a completion using a Jinja template in the message content
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "user",
"content": """Extract the information from the
following text: {{ data }}`""",
},
],
response_model=User,
context={"data": "John Doe is thirty years old"},
)
print(response)
#> User(name='John Doe', age=30)
了解更多关于模板的信息 :octicons-arrow-right:
验证¶
你也可以使用Pydantic来验证你的输出,并在失败时让llm重试。查看我们关于重试和验证上下文的文档。
import instructor
from openai import OpenAI
from pydantic import BaseModel, ValidationError, BeforeValidator
from typing_extensions import Annotated
from instructor import llm_validator
# 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]
"""
贡献¶
如果你想帮忙,可以查看一些标记为good-first-issue或help-wanted的问题。可以在这里找到。它们可能是代码改进、客座博客文章或新食谱等任何内容。
许可证¶
本项目根据MIT许可证的条款进行许可。