跳过内容

Instructor,最受欢迎的简单结构化输出库

由llms驱动的结构化输出。设计简洁、透明且可控。


Downloads Downloads Downloads

Instructor 使得从 GPT-3.5、GPT-4、GPT-4-Vision 等 LLMs 以及包括 Mistral/MixtralOllamallama-cpp-python 在内的开源模型中获取结构化数据(如 JSON)变得容易。

它以其简洁性、透明性和以用户为中心的设计脱颖而出,建立在Pydantic之上。Instructor 帮助您管理验证上下文,使用Tenacity进行重试,以及流式处理列表部分响应。

给仓库加星 食谱 提示指南

pip install instructor
uv pip install instructor
poetry add instructor

如果你遇到困难,你可以随时运行 instructor docs 来在浏览器中打开文档。它甚至支持搜索特定主题。

instructor docs [QUERY]

新闻通讯

如果您想获取提示、新博客文章和研究的最新信息,请订阅我们的新闻通讯。以下是您可以期待的内容:

  • 关于Instructor功能和版本的更新
  • 关于人工智能和结构化输出的博客文章
  • 来自我们社区的小贴士和技巧
  • 在LLM和结构化输出领域的研究
  • 关于Instructor的AI发展技能信息

订阅我们的新闻通讯以获取人工智能发展的最新动态。我们提供内容让您保持信息灵通,并帮助您在项目中使用Instructor。

入门指南

如果你想查看所有的集成,请查看集成指南

pip install instructor

使用OpenAI的结构化输出响应

您现在可以将OpenAI的结构化输出响应与Instructor结合使用。此功能结合了Instructor的优势与OpenAI的精确采样。

client = instructor.from_openai(OpenAI(), mode=Mode.TOOLS_STRICT)
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

查看更多

pip install "instructor[ollama]"
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

查看更多

pip install "instructor[llama-cpp-python]"
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

查看更多

pip install "instructor[anthropic]"
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

查看更多

pip install "instructor[google-generativeai]"
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

查看更多

pip install "instructor[vertexai]"
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

查看更多

pip install "instructor[groq]"
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

查看更多

pip install "instructor[litellm]"
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

查看更多

pip install "instructor[cohere]"
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

查看更多

pip install "instructor[cerebras]"
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

查看更多

pip install "instructor[fireworks]"
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?

使用钩子

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,你可以看到类型被正确推断出来。

type

处理异步: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()函数将返回正确的用户类型。

async

返回原始完成: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,
)

with_completion

流式部分对象:create_partial

为了处理流,我们仍然支持Iterable[T]Partial[T],但为了简化类型推断,我们还添加了create_iterablecreate_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]

generator

流式可迭代对象: 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)

iterable

模板

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-issuehelp-wanted的问题。可以在这里找到。它们可能是代码改进、客座博客文章或新食谱等任何内容。

许可证

本项目根据MIT许可证的条款进行许可。