跳到内容

Pydantic

为 RAG 流水线构建基于 LLM 的重排器

您是否正在为检索增强生成(RAG)流水线中不相关的搜索结果而苦恼?

想象一下,拥有一种强大的工具,能够智能地重新评估和重新排序您的搜索结果,显著提高它们与用户查询的相关性。

在这篇博客文章中,我们将展示如何使用 Instructor 和 Pydantic 创建一个基于 LLM 的重排器。这种方法将

  • 提高搜索结果的准确性
  • 利用大型语言模型(LLM)的力量
  • 利用结构化输出实现精确的信息检索

通过本教程的学习,您将能够实现一个 LLM 重排器,用于标记您的合成数据以微调传统的重排器,或者为您的 RAG 系统构建一个评估流水线。让我们开始吧!

使用 Instructor 和 Pydantic 构建成对 LLM 评估器

在这篇博客文章中,我们将探讨如何使用 Instructor 和 Pydantic 创建一个成对 LLM 评估器。该评估器将评估问题与文本片段之间的相关性,展示了结构化输出在语言模型交互中的实际应用。

简介

评估文本相关性是自然语言处理和信息检索中的常见任务。通过利用大型语言模型(LLM)和结构化输出,我们可以创建一个系统来判断问题与给定文本之间的相似性或相关性。

使用语言模型确保时间戳格式一致性

Gemini 可以理解语言模型输出中的时间戳,但它们可能不一致。视频内容时间戳在 HH:MM:SS 和 MM:SS 格式之间变化,导致解析错误和计算问题。本文介绍了一种处理视频片段和电影时间戳的技术,避免了格式问题。

我们将 Pydantic 的数据验证与自定义解析相结合,以实现一致的时间戳处理。您将学习处理任何格式的时间戳,减少视频内容工作流程中的错误。这有点像我们通过添加一个简单字段来确保多语言摘要中的语言匹配的方式。

本文提供了一种使用 Pydantic 改进语言模型项目中时间戳处理的解决方案。此方法解决了格式不一致问题,并实现了时间戳处理。

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

在要求语言模型总结文本时,即使源文本是另一种语言,生成的摘要也可能最终是英文的。这很可能是因为指令是以英文提供的,导致模型偏向于输出英文。

在这篇文章中,我们探讨确保生成的摘要语言与源文本语言一致的技术。我们利用 Pydantic 进行数据验证,并利用 langdetect 库进行语言识别。

简单合成数据生成

人们使用 Instructor 的目的是生成合成数据,而不是提取数据本身。我们甚至可以使用 J-Schema 的额外字段来提供特定示例,以控制数据的生成方式。

考虑下面的示例。我们很可能只会生成非常简单的名字。

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

利用简单示例

我们可能希望通过利用 Pydantic 的配置将示例设置为提示的一部分。我们可以直接在 JSON Schema 本身中设置示例。

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

使用 Pydantic 验证 LLM 引用

确保信息准确性至关重要。这篇博客文章探讨了 Pydantic 强大而灵活的验证器如何通过引用验证来增强数据准确性。

我们将首先使用简单的子字符串检查来验证引用。然后,我们将使用 instructor 本身来驱动一个 LLM 来验证引用并将答案与给定引用对齐。最后,我们将探讨如何利用这些技术生成一个包含准确回复的数据集。

好的 LLM 验证就是好的验证

如果您的验证逻辑可以像人类一样学习和适应,但又能以软件的速度运行,会怎么样?这就是验证的未来,而且它已经到来。

验证是可靠软件的基础。但传统方法是静态的、基于规则的,无法适应新的挑战。本文探讨了如何使用 PydanticInstructor 等 Python 库将动态的、机器学习驱动的验证引入您的软件堆栈。我们使用符合如下结构的验证函数来验证这些输出。

def validation_function(value):
    if condition(value):
        raise ValueError("Value is not valid")
    return mutation(value)