Usar Exemplos de Referência

A qualidade das extrações muitas vezes pode ser melhorada fornecendo exemplos de referência para o LLM.

Dica: Embora este tutorial se concentre em como usar exemplos com uma ferramenta chamada modelo, essa técnica é geralmente aplicável e também funcionará com técnicas baseadas em JSON ou mais prompts.

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Você é um algoritmo de extração especialista. "
            "Extraia apenas informações relevantes do texto. "
            "Se você não souber o valor de um atributo solicitado para extração, retorne nulo para o valor do atributo.",
        ),
        MessagesPlaceholder("exemplos"),  # <-- EXEMPLOS!
        ("humano", "{texto}"),
    ]
)

Teste o modelo:

from langchain_core.messages import (
    HumanMessage,
)

prompt.invoke(
    {"texto": "este é um texto", "exemplos": [HumanMessage(content="testando 1 2 3")]}
)
ChatPromptValue(messages=[SystemMessage(content="Você é um algoritmo de extração especialista. Extraia apenas informações relevantes do texto. Se você não souber o valor de um atributo solicitado para extração, retorne nulo para o valor do atributo."), HumanMessage(content='testando 1 2 3'), HumanMessage(content='este é um texto')])

Definir o Esquema

Vamos reutilizar o esquema da pessoa do início rápido.

from typing import List, Optional

from langchain_core.pydantic_v1 import BaseModel, Field
from langchain_openai import ChatOpenAI


class Person(BaseModel):
    """Informações sobre uma pessoa."""

    nome: Optional[str] = Field(..., description="O nome da pessoa")
    cor_do_cabelo: Optional[str] = Field(
        ..., description="A cor dos olhos da pessoa, se conhecida"
    )
    altura_em_metros: Optional[str] = Field(..., description="Altura em metros")


class Data(BaseModel):
    """Dados extraídos sobre pessoas."""

    pessoas: List[Person]

Definir exemplos de referência

Exemplos podem ser definidos como uma lista de pares de entrada-saída.

Cada exemplo contém um texto de entrada e uma saída de exemplo mostrando o que deve ser extraído do texto.

info

Isso é um pouco técnico, então sinta-se à vontade para ignorar se não entender!

O formato do exemplo precisa corresponder à API usada (por exemplo, chamada de ferramenta ou modo JSON etc.).

Aqui, os exemplos formatados corresponderão ao formato esperado para a chamada da ferramenta API, pois é isso que estamos usando.

import uuid
from typing import Dict, List, TypedDict

from langchain_core.messages import (
    AIMessage,
    BaseMessage,
    HumanMessage,
    SystemMessage,
    ToolMessage,
)
from langchain_core.pydantic_v1 import BaseModel, Field


class Example(TypedDict):
    """Uma representação de um exemplo composto por texto de entrada e chamadas de ferramentas esperadas.

    Para extração, as chamadas de ferramentas são representadas como instâncias do modelo pydantic.
    """

    input: str  # Este é o texto do exemplo
    tool_calls: List[BaseModel]  # Instâncias de modelo pydantic que devem ser extraídas


def tool_example_to_messages(example: Example) -> List[BaseMessage]:
    """Converte um exemplo em uma lista de mensagens que podem ser inseridas em um LLM.

    Este código é um adaptador que converte nosso exemplo em uma lista de mensagens
    que podem ser inseridas em um modelo de chat.

    A lista de mensagens por exemplo corresponde a:

    1) HumanMessage: contém o conteúdo do qual o conteúdo deve ser extraído.
    2) AIMessage: contém as informações extraídas do modelo
    3) ToolMessage: contém confirmação ao modelo de que a ferramenta foi solicitada corretamente.

    O ToolMessage é necessário porque alguns dos modelos de chat são hiperotimizados para agentes
    em vez de para um caso de uso de extração.
    """
    messages: List[BaseMessage] = [HumanMessage(content=example["input"])]
    openai_tool_calls = []
    for tool_call in example["tool_calls"]:
        openai_tool_calls.append(
            {
                "id": str(uuid.uuid4()),
                "type": "função",
                "função": {
                    "nome": tool_call.__class__.__name__,
                    "argumentos": tool_call.json(),
                },
            }
        )
    messages.append(
        AIMessage(content="", additional_kwargs={"tool_calls": openai_tool_calls})
    )
    tool_outputs = example.get("tool_outputs") or [
        "Você chamou corretamente esta ferramenta."
    ] * len(openai_tool_calls)
    for output, tool_call in zip(tool_outputs, openai_tool_calls):
        messages.append(ToolMessage(content=output, tool_call_id=tool_call["id"]))
    return messages

A seguir, vamos definir nossos exemplos e depois convertê-los para o formato de mensagem.

exemplos = [
    (
        "O oceano é vasto e azul. Tem mais de 20.000 pés de profundidade. Há muitos peixes nele.",
        Pessoa(nome=None, altura_em_metros=None, cor_do_cabelo=None),
    ),
    (
        "Fiona viajou longe da França para a Espanha.",
        Pessoa(nome="Fiona", altura_em_metros=None, cor_do_cabelo=None),
    ),
]


mensagens = []

for texto, chamada_de_ferramenta in exemplos:
    mensagens.extend(
        tool_example_to_messages({"input": texto, "tool_calls": [chamada_de_ferramenta]})
    )

Vamos testar o prompt

prompt.invoke({"texto": "este é um texto", "exemplos": mensagens})
ChatPromptValue(messages=[SystemMessage(content="Você é um algoritmo de extração especialista. Apenas extraia informações relevantes do texto. Se você não souber o valor de um atributo a ser extraído, retorne nulo para o valor do atributo."), HumanMessage(content="O oceano é vasto e azul. Tem mais de 20.000 pés de profundidade. Há muitos peixes nele."), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'c75e57cc-8212-4959-81e9-9477b0b79126', 'type': 'function', 'function': {'name': 'Person', 'arguments': '{"name": null, "hair_color": null, "height_in_meters": null}'}}]}), ToolMessage(content='Você chamou corretamente esta ferramenta.', tool_call_id='c75e57cc-8212-4959-81e9-9477b0b79126'), HumanMessage(content='Fiona viajou longe da França para a Espanha.'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': '69da50b5-e427-44be-b396-1e56d821c6b0', 'type': 'function', 'function': {'name': 'Person', 'arguments': '{"name": "Fiona", "hair_color": null, "height_in_meters": null}'}}]}), ToolMessage(content='Você chamou corretamente esta ferramenta.', tool_call_id='69da50b5-e427-44be-b396-1e56d821c6b0'), HumanMessage(content='este é algum texto')])

Criar um extrator

Aqui, vamos criar um extrator usando gpt-4.

llm = ChatOpenAI(
    model="gpt-4-0125-preview",
    temperature=0,
)


runnable = prompt | llm.with_structured_output(
    schema=Data,
    method="function_calling",
    include_raw=False,
)
/Users/harrisonchase/workplace/langchain/libs/core/langchain_core/_api/beta_decorator.py:86: LangChainBetaWarning: A função `with_structured_output` está em beta. Está sendo trabalhada ativamente, então a API pode mudar.
  warn_beta(

Sem exemplos

Observe que mesmo usando gpt-4, está falhando com um caso de teste muito simples!

for _ in range(5):
    text = "O sistema solar é grande, mas a terra só tem 1 lua."
    print(runnable.invoke({"text": text, "examples": []}))
pessoas=[]
pessoas=[Pessoa(nome='terra', cor_do_cabelo=None, altura_em_metros=None)]
pessoas=[Pessoa(nome='terra', cor_do_cabelo=None, altura_em_metros=None)]
pessoas=[]
pessoas=[]

Com exemplos

Exemplos de referência ajudam a corrigir a falha!

for _ in range(5):
    text = "O sistema solar é grande, mas a terra só tem 1 lua."
    print(runnable.invoke({"text": text, "examples": mensagens}))
pessoas=[]
pessoas=[]
pessoas=[]
pessoas=[]
pessoas=[]
runnable.invoke(
    {
        "text": "Meu nome é Harrison. Meu cabelo é preto.",
        "examples": mensagens,
    }
)
Data(pessoas=[Pessoa(nome='Harrison', cor_do_cabelo='preto', altura_em_metros=None)])