Análise

LLMs capazes de seguir bem as instruções podem ser encarregados de produzir informações em um formato específico.

Essa abordagem depende do design de boas instruções e, em seguida, analisa a saída dos LLMs para extrair informações de forma eficiente.

Aqui, vamos usar o Claude, que é ótimo em seguir instruções! Veja modelos antropicos.

from langchain_anthropic.chat_models import ChatAnthropic

model = ChatAnthropic(model_name="claude-3-sonnet-20240229", temperature=0)

Dica: Todas as mesmas considerações para qualidade de extração se aplicam à abordagem de análise. Reveja as diretrizes para qualidade de extração.

Este tutorial tem a intenção de ser simples, mas geralmente deve incluir exemplos de referência para obter melhor desempenho!

Usando PydanticOutputParser

O exemplo a seguir usa o PydanticOutputParser integrado para analisar a saída de um modelo de chat.

from typing import List, Optional

from langchain.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator


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

    name: str = Field(..., description="O nome da pessoa")
    height_in_meters: float = Field(
        ..., description="A altura da pessoa expressa em metros."
    )


class People(BaseModel):
    """Informações de identificação de todas as pessoas em um texto."""

    people: List[Person]


parser = PydanticOutputParser(pydantic_object=People)

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Responda à consulta do usuário. Envie a saída entre tags `json`\n{format_instructions}",
        ),
        ("human", "{query}"),
    ]
).partial(format_instructions=parser.get_format_instructions())

Vamos ver quais informações são enviadas para o modelo.

query = "Anna tem 23 anos e tem 1,83 metros de altura"
print(prompt.format_prompt(query=query).to_string())
Sistema: Responda à consulta do usuário. Envie a saída entre tags `json`
A saída deve ser formatada como uma instância JSON que esteja de acordo com o esquema JSON abaixo.

Como exemplo, para o esquema {"properties": {"foo": {"title": "Foo", "description": "uma lista de strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
o objeto {"foo": ["bar", "baz"]} é uma instância bem formatada do esquema. O objeto {"properties": {"foo": ["bar", "baz"]}} não está bem formatado.

Aqui está o esquema de saída:
{"description": "Informações de identificação de todas as pessoas em um texto.", "properties": {"people": {"title": "Pessoas", "type": "array", "items": {"$ref": "#/definitions/Person"}}}, "required": ["people"], "definitions": {"Person": {"title": "Pessoa", "description": "Informações sobre uma pessoa.", "type": "object", "properties": {"name": {"title": "Nome", "description": "O nome da pessoa", "type": "string"}, "height_in_meters": {"title": "Altura em Metros", "description": "A altura da pessoa expressa em metros.", "type": "number"}}, "required": ["name", "height_in_meters"]}}}

Usuário: Anna tem 23 anos e tem 1,83 metros de altura
chain = prompt | model | parser
chain.invoke({"query": query})
People(people=[Person(name='Anna', height_in_meters=1.83)])

Análise Personalizada

É fácil criar um prompt e um analisador personalizado com LangChain e LCEL.

Você pode usar uma função simples para analisar a saída do modelo!

import json
import re
from typing import List, Optional

from langchain_anthropic.chat_models import ChatAnthropic
from langchain_core.messages import AIMessage
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.pydantic_v1 import BaseModel, Field, validator


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

    name: str = Field(..., description="O nome da pessoa")
    height_in_meters: float = Field(
        ..., description="A altura da pessoa expressa em metros."
    )


class People(BaseModel):
    """Informações de identificação sobre todas as pessoas em um texto."""

    people: List[Person]


prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Responda à consulta do usuário. Produza sua resposta como JSON que corresponda ao esquema fornecido: ```json\n{schema}\n```. "
            "Certifique-se de envolver a resposta em tags ```json e ```",
        ),
        ("human", "{query}"),
    ]
).partial(schema=People.schema())


def extract_json(message: AIMessage) -> List[dict]:
    """Extrai o conteúdo JSON de uma string onde o JSON está incorporado entre as tags ```json e ```.

    Parâmetros:
        text (str): O texto contendo o conteúdo JSON.

    Retorna:
        list: Uma lista de strings JSON extraídas.
    """
    text = message.content
    pattern = r"```json(.*?)```"

    matches = re.findall(pattern, text, re.DOTALL)

    try:
        return [json.loads(match.strip()) for match in matches]
    except Exception:
        raise ValueError(f"Falha ao analisar: {message}")
query = "Anna tem 23 anos e mede 1,83 metros de altura"
print(prompt.format_prompt(query=query).to_string())
System: Responda à consulta do usuário. Produza sua resposta como JSON que corresponda ao esquema fornecido: \`\`\`json
{'title': 'People', 'description': 'Informações de identificação sobre todas as pessoas em um texto.', 'type': 'object', 'properties': {'people': {'title': 'Pessoas', 'type': 'array', 'items': {'$ref': '#/definitions/Person'}}}, 'required': ['people'], 'definitions': {'Person': {'title': 'Pessoa', 'description': 'Informações sobre uma pessoa.', 'type': 'object', 'properties': {'name': {'title': 'Nome', 'description': 'O nome da pessoa', 'type': 'string'}, 'height_in_meters': {'title': 'Altura Em Metros', 'description': 'A altura da pessoa expressa em metros.', 'type': 'number'}}, 'required': ['name', 'height_in_meters']}}}
\`\`\`. Certifique-se de envolver a resposta em \`\`\`json e \`\`\`
Humano: Anna tem 23 anos e mede 1,83 metros de altura
chain = prompt | model | extract_json
chain.invoke({"query": query})
[{'people': [{'name': 'Anna', 'height_in_meters': 1.83}]}]

Outras Bibliotecas

Se estiver procurando por uma abordagem de extração usando um analisador, confira a biblioteca Kor. Foi escrita por um dos mantenedores do LangChain e ajuda a criar um prompt que leva em conta exemplos, permite controlar formatos (por exemplo, JSON ou CSV) e expressa o esquema em TypeScript. Parece funcionar muito bem!