跳到正文

2024

Logfire

介绍

Logfire 是由 Pydantic 的创建者们推出的一个全新的可观测性平台。它几乎无缝地集成了 Pydantic、HTTPx 和 Instructor 等您喜爱的众多库。在本文中,我们将展示如何使用 Logfire 与 Instructor 来深入了解整个应用程序的性能。

我们将详细介绍以下示例

  1. 使用 Instructor 分类诈骗邮件
  2. 使用 llm_validator 执行简单验证
  3. 使用 GPT4V 从信息图中提取数据到 markdown 表格

基于字符串初始化的统一提供商接口

Instructor 现在提供了一种简化的方式,可以使用单个一致的接口初始化任何受支持的 LLM 提供商。这种方法使得在不同 LLM 提供商之间切换比以往任何时候都更容易,同时保持您所依赖的结构化输出功能。

问题

随着 LLM 提供商数量的增长,初始化和使用不同客户端库的复杂性也随之增加。每个提供商都有其自身的初始化模式、API 结构和特性。这导致代码在不同提供商之间不可移植,并且在您想尝试新模型时需要进行大量的重构。

解决方案:基于字符串的初始化

我们引入了一个新的统一接口,允许您使用简单的字符串格式初始化任何受支持的提供商

import instructor
from pydantic import BaseModel

class UserInfo(BaseModel):
    name: str
    age: int

# Initialize any provider with a single consistent interface
client = instructor.from_provider("openai/gpt-4")
client = instructor.from_provider("anthropic/claude-3-sonnet")
client = instructor.from_provider("google/gemini-pro")
client = instructor.from_provider("mistral/mistral-large")

from_provider 函数接受格式为 "provider/model-name" 的字符串,并处理使用正确模型设置相应客户端的所有细节。这提供了几个主要优势

  • 简化初始化:无需手动创建特定于提供商的客户端
  • 一致接口:相同的语法适用于所有提供商
  • 减少依赖暴露:您无需在应用程序代码中导入特定的提供商库
  • 轻松实验:只需更改一行代码即可在提供商之间切换

支持的提供商

基于字符串的初始化目前支持生态系统中的所有主要提供商

  • OpenAI:"openai/gpt-4", "openai/gpt-4o", "openai/gpt-3.5-turbo"
  • Anthropic:"anthropic/claude-3-opus-20240229", "anthropic/claude-3-sonnet-20240229", "anthropic/claude-3-haiku-20240307"
  • Google Gemini:"google/gemini-pro", "google/gemini-pro-vision"
  • Mistral:"mistral/mistral-small-latest", "mistral/mistral-medium-latest", "mistral/mistral-large-latest"
  • Cohere:"cohere/command", "cohere/command-r", "cohere/command-light"
  • Perplexity:"perplexity/sonar-small-online", "perplexity/sonar-medium-online"
  • Groq:"groq/llama2-70b-4096", "groq/mixtral-8x7b-32768", "groq/gemma-7b-it"
  • Writer:"writer/palmyra-instruct", "writer/palmyra-instruct-v2"
  • AWS Bedrock:"bedrock/anthropic.claude-v2", "bedrock/amazon.titan-text-express-v1"
  • Cerebras:"cerebras/cerebras-gpt", "cerebras/cerebras-gpt-2.7b"
  • Fireworks:"fireworks/llama-v2-70b", "fireworks/firellama-13b"
  • Vertex AI:"vertexai/gemini-pro", "vertexai/text-bison"
  • Google GenAI:"genai/gemini-pro", "genai/gemini-pro-vision"

每个提供商都将使用合理的默认值进行初始化,但您也可以传递额外的关键字参数来自定义配置。有关模型特定详细信息,请查阅每个提供商的文档。

异步支持

统一接口完全支持同步和异步客户端

# Synchronous client (default)
client = instructor.from_provider("openai/gpt-4")

# Asynchronous client
async_client = instructor.from_provider("anthropic/claude-3-sonnet", async_client=True)

# Use like any other async client
response = await async_client.chat.completions.create(
    response_model=UserInfo,
    messages=[{"role": "user", "content": "Extract information about John who is 30 years old"}]
)

模式选择

您还可以指定要与提供商一起使用的结构化输出模式

import instructor
from instructor import Mode

# Override the default mode for a provider
client = instructor.from_provider(
    "anthropic/claude-3-sonnet", 
    mode=Mode.ANTHROPIC_TOOLS
)

# Use JSON mode instead of the default tools mode
client = instructor.from_provider(
    "mistral/mistral-large", 
    mode=Mode.MISTRAL_STRUCTURED_OUTPUTS
)

# Use reasoning tools instead of regular tools for Anthropic
client = instructor.from_provider(
    "anthropic/claude-3-opus", 
    mode=Mode.ANTHROPIC_REASONING_TOOLS
)

如果未指定,每个提供商将使用其推荐的默认模式

  • OpenAI:Mode.OPENAI_FUNCTIONS
  • Anthropic:Mode.ANTHROPIC_TOOLS
  • Google Gemini:Mode.GEMINI_JSON
  • Mistral:Mode.MISTRAL_TOOLS
  • Cohere:Mode.COHERE_TOOLS
  • Perplexity:Mode.JSON
  • Groq:Mode.GROQ_TOOLS
  • Writer:Mode.WRITER_JSON
  • Bedrock:Mode.ANTHROPIC_TOOLS(适用于 Bedrock 上的 Claude)
  • Vertex AI:Mode.VERTEXAI_TOOLS

您可以根据您的具体需求和模型功能随时自定义此设置。

错误处理

from_provider 函数包含强大的错误处理功能,可帮助您快速识别和修复问题

# Missing dependency
try:
    client = instructor.from_provider("anthropic/claude-3-sonnet")
except ImportError as e:
    print("Error: Install the anthropic package first")
    # pip install anthropic

# Invalid provider format
try:
    client = instructor.from_provider("invalid-format")
except ValueError as e:
    print(e)  # Model string must be in format "provider/model-name"

# Unsupported provider
try:
    client = instructor.from_provider("unknown/model")
except ValueError as e:
    print(e)  # Unsupported provider: unknown. Supported providers are: ...

该函数验证提供商字符串格式,检查提供商是否受支持,并确保已安装必要的软件包。

环境变量

与原生客户端库一样,from_provider 遵守为每个提供商设置的环境变量

# Set environment variables 
import os
os.environ["OPENAI_API_KEY"] = "your-openai-key"
os.environ["ANTHROPIC_API_KEY"] = "your-anthropic-key" 
os.environ["MISTRAL_API_KEY"] = "your-mistral-key"

# No need to pass API keys directly
client = instructor.from_provider("openai/gpt-4")

故障排除

以下是使用统一提供商接口时的一些常见问题和解决方案

模型未找到错误

如果您收到 404 错误,请检查您使用的模型名称格式是否正确

Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-3-haiku'}}

对于 Anthropic 模型,请务必包含版本日期:- ✅ 正确:anthropic/claude-3-haiku-20240307 - ❌ 错误:anthropic/claude-3-haiku

特定于提供商的参数

某些提供商需要特定的 API 调用参数

# Anthropic requires max_tokens
anthropic_client = instructor.from_provider(
    "anthropic/claude-3-haiku-20240307", 
    max_tokens=400  # Required for Anthropic
)

# Use models with vision capabilities for multimodal content
gemini_client = instructor.from_provider(
    "google/gemini-pro-vision"  # Required for image processing
)

工作示例

这是一个完整的示例,演示了使用多个提供商的 automodel 功能

import os
import asyncio
import instructor
from pydantic import BaseModel, Field

class UserInfo(BaseModel):
    """User information extraction model."""
    name: str = Field(description="The user's full name")
    age: int = Field(description="The user's age in years")
    occupation: str = Field(description="The user's job or profession")

async def main():
    # Test OpenAI
    openai_client = instructor.from_provider("openai/gpt-3.5-turbo")
    openai_result = openai_client.chat.completions.create(
        response_model=UserInfo,
        messages=[{"role": "user", "content": "Jane Doe is a 28-year-old data scientist."}]
    )
    print(f"OpenAI result: {openai_result.model_dump()}")

    # Test Anthropic with async client
    if os.environ.get("ANTHROPIC_API_KEY"):
        anthropic_client = instructor.from_provider(
            model="anthropic/claude-3-haiku-20240307",
            async_client=True,
            max_tokens=400  # Required for Anthropic
        )
        anthropic_result = await anthropic_client.chat.completions.create(
            response_model=UserInfo,
            messages=[{"role": "user", "content": "John Smith is a 35-year-old software engineer."}]
        )
        print(f"Anthropic result: {anthropic_result.model_dump()}")

if __name__ == "__main__":
    asyncio.run(main())

结论

基于字符串的初始化是使 Instructor 更加用户友好和灵活的重要一步。它降低了使用多个提供商的学习曲线,并使得尝试不同模型变得前所未有的容易。

优点包括: - 使用一致接口简化初始化 - 自动选择合适的默认模式 - 支持同步和异步客户端 - 清晰的错误消息,快速识别问题 - 遵守特定于提供商的环境变量 - 在整个 LLM 生态系统中全面的模型选择

无论您是构建新应用程序还是迁移现有应用程序,统一提供商接口都提供了一种更清晰、更易于维护的方式来处理 LLM 生态系统中的结构化输出。

立即使用 instructor.from_provider() 尝试,并在我们的仓库中查看 完整示例代码

宣布 instructor=1.0.0 发布

在过去的 10 个月里,我们秉承“易于尝试,易于删除”的原则构建了 instructor。我们通过使用 instructor 包修补 openai 客户端,并添加了诸如 response_modelmax_retriesvalidation_context 之类的新参数来实现这一点。因此,我坚信 instructor 是从 llm api 中获取结构化数据的最佳方式

但因此,我们在使类型注解良好工作同时在开发时为您提供更多控制方面遇到了一些阻碍。我很高兴发布 1.0.0 版本,该版本清理了与类型注解相关的 API,同时不影响易用性。

多语言摘要任务中的语言匹配

当要求语言模型总结文本时,存在生成的摘要最终变成英文的风险,即使源文本是其他语言。这很可能是因为指令是用英文提供的,这使得模型偏向于英文输出。

在本文中,我们探讨了如何确保生成的摘要语言与源文本语言匹配的技术。我们利用 Pydantic 进行数据验证,并使用 langdetect 库进行语言识别。

使用 Anthropic 进行结构化输出

特别感谢 Shreya 对 anthropic 支持的贡献。截至目前,除流式传输支持外,所有功能均已可用。

对于那些渴望尝试的用户,只需使用 ANTHROPIC_JSON 修补客户端,即可利用 anthropic 客户端进行请求。

pip install instructor[anthropic]

缺失的功能

需要承认的是,我们知道目前缺少部分流式传输功能以及对 XML 更好的重问支持。我们正在努力解决,并很快会推出。

from pydantic import BaseModel
from typing import List
import anthropic
import instructor

# Patching the Anthropics client with the instructor for enhanced capabilities
anthropic_client = instructor.from_openai(
    create=anthropic.Anthropic().messages.create,
    mode=instructor.Mode.ANTHROPIC_JSON
)

class Properties(BaseModel):
    name: str
    value: str

class User(BaseModel):
    name: str
    age: int
    properties: List[Properties]

user_response = anthropic_client(
    model="claude-3-haiku-20240307",
    max_tokens=1024,
    max_retries=0,
    messages=[
        {
            "role": "user",
            "content": "Create a user for a model with a name, age, and properties.",
        }
    ],
    response_model=User,
)  # type: ignore

print(user_response.model_dump_json(indent=2))
"""
{
    "name": "John",
    "age": 25,
    "properties": [
        {
            "key": "favorite_color",
            "value": "blue"
        }
    ]
}

我们正在遇到深度嵌套类型方面的挑战,并热切邀请社区进行测试、提供反馈并提出必要的改进建议,以帮助我们增强对 anthropic 客户端的支持。

简单的合成数据生成

人们一直在使用 instructor 来生成合成数据,而不是提取数据本身。我们甚至可以使用 J-Schemo 额外字段来提供特定示例,以控制如何生成数据。

请考虑以下示例。我们可能会生成非常简单的名称。

from typing import Iterable
from pydantic import BaseModel
import instructor
from openai import OpenAI


# Define the UserDetail model
class UserDetail(BaseModel):
    name: str
    age: int


# Patch the OpenAI client to enable the response_model functionality
client = instructor.from_openai(OpenAI())


def generate_fake_users(count: int) -> Iterable[UserDetail]:
    return client.chat.completions.create(
        model="gpt-3.5-turbo",
        response_model=Iterable[UserDetail],
        messages=[
            {"role": "user", "content": f"Generate a {count} synthetic users"},
        ],
    )


for user in generate_fake_users(5):
    print(user)
    #> name='Alice' age=25
    #> name='Bob' age=30
    #> name='Charlie' age=35
    #> name='David' age=40
    #> name='Eve' age=22

利用简单示例

我们可以通过利用 Pydantics 配置将示例设置为提示的一部分。我们可以直接在 JSON 方案本身中设置示例。

from typing import Iterable
from pydantic import BaseModel, Field
import instructor
from openai import OpenAI


# Define the UserDetail model
class UserDetail(BaseModel):
    name: str = Field(examples=["Timothee Chalamet", "Zendaya"])
    age: int


# Patch the OpenAI client to enable the response_model functionality
client = instructor.from_openai(OpenAI())


def generate_fake_users(count: int) -> Iterable[UserDetail]:
    return client.chat.completions.create(
        model="gpt-3.5-turbo",
        response_model=Iterable[UserDetail],
        messages=[
            {"role": "user", "content": f"Generate a {count} synthetic users"},
        ],
    )


for user in generate_fake_users(5):
    print(user)
    #> name='John Doe' age=25
    #> name='Jane Smith' age=30
    #> name='Michael Johnson' age=22
    #> name='Emily Davis' age=28
    #> name='David Brown' age=35

通过将名人姓名作为示例纳入,我们已转向生成包含知名人物的合成数据,摆脱了先前使用的简单单词名称。

利用复杂示例

为了更有效地生成具有更多细微差别的合成示例,让我们升级到“gpt-4-turbo-preview”模型,使用模型级示例而不是属性级示例

import instructor

from typing import Iterable
from pydantic import BaseModel, ConfigDict
from openai import OpenAI


# Define the UserDetail model
class UserDetail(BaseModel):
    """Old Wizards"""

    name: str
    age: int

    model_config = ConfigDict(
        json_schema_extra={
            "examples": [
                {"name": "Gandalf the Grey", "age": 1000},
                {"name": "Albus Dumbledore", "age": 150},
            ]
        }
    )


# Patch the OpenAI client to enable the response_model functionality
client = instructor.from_openai(OpenAI())


def generate_fake_users(count: int) -> Iterable[UserDetail]:
    return client.chat.completions.create(
        model="gpt-4-turbo-preview",
        response_model=Iterable[UserDetail],
        messages=[
            {"role": "user", "content": f"Generate `{count}` synthetic examples"},
        ],
    )


for user in generate_fake_users(5):
    print(user)
    #> name='Merlin' age=1000
    #> name='Saruman the White' age=700
    #> name='Radagast the Brown' age=600
    #> name='Elminster Aumar' age=1200
    #> name='Mordenkainen' age=850

利用描述

通过调整 Pydantic 模型中的描述,我们可以巧妙地影响生成的合成数据的性质。这种方法可以对输出进行更细致的控制,确保生成的数据更符合我们的期望或要求。

例如,在我们的 UserDetail 模型中,将“花哨的法文名字”指定为 name 字段的描述,可以指导生成过程产生符合此特定标准的名称,从而生成一个既多样化又针对特定语言特征的数据集。

import instructor

from typing import Iterable
from pydantic import BaseModel, Field
from openai import OpenAI


# Define the UserDetail model
class UserDetail(BaseModel):
    name: str = Field(description="Fancy French sounding names")
    age: int


# Patch the OpenAI client to enable the response_model functionality
client = instructor.from_openai(OpenAI())


def generate_fake_users(count: int) -> Iterable[UserDetail]:
    return client.chat.completions.create(
        model="gpt-3.5-turbo",
        response_model=Iterable[UserDetail],
        messages=[
            {"role": "user", "content": f"Generate `{count}` synthetic users"},
        ],
    )


for user in generate_fake_users(5):
    print(user)
    #> name='Jean Luc' age=30
    #> name='Claire Belle' age=25
    #> name='Pierre Leclair' age=40
    #> name='Amelie Rousseau' age=35
    #> name='Etienne Lefevre' age=28

开源和本地 LLM 的结构化输出

Instructor 扩展了其对语言模型的能力。它始于通过 OpenAI SDK 进行 API 交互,使用 Pydantic 进行结构化数据验证。现在,Instructor 支持多种模型和平台。

JSON 模式的集成提高了对视觉模型和开源替代方案的适应性。这使得它能够支持从 GPTMistralOllamaHugging Face 上的模型,使用 llama-cpp-python

Instructor 现在可与基于云的 API 和本地模型配合使用,以进行结构化数据提取。开发人员可以参考我们的修补指南,了解如何在不同模型中使用 JSON 模式。

要了解有关 Instructor 和 Pydantic 的知识,我们提供了一门关于引导语言模型生成结构化输出的课程。

以下部分展示了 Instructor 与平台和本地设置集成以在 AI 项目中进行结构化输出的示例。

与 Langsmith 的无缝支持

一个常见的误解是 LangChain 的 LangSmith 只与 LangChain 的模型兼容。实际上,LangSmith 是一个统一的 DevOps 平台,用于开发、协作、测试、部署和监控 LLM 应用程序。在本博客中,我们将探讨如何将 LangSmith 与 instructor 一起用于增强 OpenAI 客户端。

Weights and Biases 的免费课程

我刚刚发布了一个关于 wits and biases 的免费课程。它涵盖了教程中的材料。您可以在 wandb.courses 上查看,它是免费且向所有人开放的,时长不到一小时!

点击图片访问课程