跳到内容

在 Instructor 中使用联合类型

本指南解释了如何在 Instructor 中使用联合类型,它允许您处理语言模型可能产生的多种响应类型。当您需要 LLM 在不同的响应格式或动作类型之间进行选择时,联合类型特别有用。

Union vs. union

原始 union.md 页面中的内容已整合到此更全面的指南中。该页面展示了一个使用联合类型处理多种动作类型的基本示例。

基本联合类型

联合类型允许您指定字段可以是几种类型之一

from typing import Union
from pydantic import BaseModel


class Response(BaseModel):
    value: Union[str, int]  # Can be either string or integer

判别联合类型

使用判别联合类型处理不同的响应类型

from typing import Literal, Union
from pydantic import BaseModel
import instructor
from openai import OpenAI


class UserQuery(BaseModel):
    type: Literal["user"]
    username: str


class SystemQuery(BaseModel):
    type: Literal["system"]
    command: str


Query = Union[UserQuery, SystemQuery]

# Usage with Instructor
client = instructor.from_openai(OpenAI())

response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    response_model=Query,
    messages=[{"role": "user", "content": "Parse: user lookup jsmith"}],
)

可选字段

将 Union 与 Optional 结合使用来处理可空字段

from typing import Optional
from pydantic import BaseModel


class User(BaseModel):
    name: str
    email: Optional[str] = None  # Same as Union[str, None]

最佳实践

  1. 类型提示:使用适当的类型提示来提高清晰度并改善 IDE 支持
  2. 判别字段:为复杂的联合类型添加判别字段(如 type),以帮助 LLM 正确选择
  3. 验证:为联合字段添加验证器,以确保数据有效
  4. 文档:在模型中使用文档字符串清晰地记录预期的类型
  5. 字段名称:使用描述性字段名称来引导模型的输出
  6. 示例:在您的 Pydantic 模型中包含示例,以帮助 LLM 理解预期的格式

常见模式

多种响应类型

from typing import Union, Literal
from pydantic import BaseModel


class SuccessResponse(BaseModel):
    status: Literal["success"]
    data: dict


class ErrorResponse(BaseModel):
    status: Literal["error"]
    message: str


Response = Union[SuccessResponse, ErrorResponse]

嵌套联合类型

from typing import Union, List
from pydantic import BaseModel


class TextContent(BaseModel):
    type: Literal["text"]
    text: str


class ImageContent(BaseModel):
    type: Literal["image"]
    url: str


class Message(BaseModel):
    content: List[Union[TextContent, ImageContent]]

使用联合类型进行动态动作选择

您可以使用联合类型编写“代理”,这些代理通过选择一个输出类来动态选择动作。例如,在一个搜索和查找函数中

from pydantic import BaseModel
from typing import Union


class Search(BaseModel):
    query: str

    def execute(self):
        # Implementation for search
        return f"Searching for: {self.query}"


class Lookup(BaseModel):
    key: str

    def execute(self):
        # Implementation for lookup
        return f"Looking up key: {self.key}"


class Action(BaseModel):
    action: Union[Search, Lookup]

    def execute(self):
        return self.action.execute()

通过这种模式,LLM 可以根据用户的输入决定是执行搜索还是查找。

import instructor
from openai import OpenAI

client = instructor.from_openai(OpenAI())

# Let the LLM decide what action to take
result = client.chat.completions.create(
    model="gpt-4",
    response_model=Action,
    messages=[
        {
            "role": "system",
            "content": "You're an assistant that helps search or lookup information.",
        },
        {"role": "user", "content": "Find information about climate change"},
    ],
)

# Execute the chosen action
print(result.execute())  # Likely outputs: "Searching for: climate change"

与 Instructor 集成

使用联合类型进行验证

from instructor import patch
from openai import OpenAI

client = patch(OpenAI())


def validate_response(response: Response) -> bool:
    if isinstance(response, ErrorResponse):
        return len(response.message) > 0
    return True


result = client.chat.completions.create(
    model="gpt-3.5-turbo",
    response_model=Response,
    validation_hook=validate_response,
    messages=[{"role": "user", "content": "Process this request"}],
)

使用联合类型进行流式处理

def stream_content():
    response = client.chat.completions.create(
        model="gpt-3.5-turbo",
        response_model=Message,
        stream=True,
        messages=[{"role": "user", "content": "Generate mixed content"}],
    )
    for partial in response:
        if partial.content:
            for item in partial.content:
                if isinstance(item, TextContent):
                    print(f"Text: {item.text}")
                elif isinstance(item, ImageContent):
                    print(f"Image: {item.url}")

错误处理

处理联合类型验证错误

from pydantic import ValidationError

try:
    response = Response(status="invalid", data={"key": "value"})  # Invalid status
except ValidationError as e:
    print(f"Validation error: {e}")

类型检查

使用 isinstance() 进行运行时类型检查

def process_response(response: Response):
    if isinstance(response, SuccessResponse):
        # Handle success case
        process_data(response.data)
    elif isinstance(response, ErrorResponse):
        # Handle error case
        log_error(response.message)

有关联合类型的更多信息,请查阅 Pydantic 文档中关于联合类型的部分

```from typing import Literal, Union from pydantic import BaseModel import instructor from openai import OpenAI

class Action(BaseModel): """基础动作类。"""

type: str

class SendMessage(BaseModel): type: Literal["send_message"] message: str recipient: str

class MakePayment(BaseModel): type: Literal["make_payment"] amount: float recipient: str

Action = Union[SendMessage, MakePayment]

在 Instructor 中的使用

client = instructor.patch(OpenAI()) response = client.chat.completions.create( model="gpt-3.5-turbo", response_model=Action, messages=[{"role": "user", "content": "Send a payment of $50 to John."}], ) ], )

```from typing import Literal, Union
from pydantic import BaseModel
import instructor
from openai import OpenAI


class SearchAction(BaseModel):
    type: Literal["search"]
    query: str


class EmailAction(BaseModel):
    type: Literal["email"]
    to: str
    subject: str
    body: str


Action = Union[SearchAction, EmailAction]

# The model can choose which action to take
client = instructor.patch(OpenAI())
response = client.chat.completions.create(
    model="gpt-3.5-turbo",
    response_model=Action,
    messages=[{"role": "user", "content": "Find me information about climate change."}],
)
  ],
)

```from typing import Literal, Union from pydantic import BaseModel import instructor from openai import OpenAI

class TextResponse(BaseModel): type: Literal["text"] content: str

class ImageResponse(BaseModel): type: Literal["image"] url: str caption: str

Response = Union[TextResponse, ImageResponse]

已修补的客户端

client = instructor.patch(OpenAI()) response = client.chat.completions.create( model="gpt-3.5-turbo", response_model=Response, messages=[{"role": "user", "content": "Tell me a joke about programming."}], ) ], )

```from typing import Union
from pydantic import BaseModel


class Response(BaseModel):
    """A more complex example showing nested Union fields."""

    result: Union[str, int, float, bool]
 bool]

```from typing import Dict, List, Union, Any from pydantic import BaseModel

class Response(BaseModel): """一个更复杂的示例,展示了嵌套的 Union 字段。"""

data: Dict[str, Union[str, int, List[Any]]]

Any]]] ```