Parsing

LLMs che siano in grado di seguire bene le istruzioni possono essere incaricati di produrre informazioni in un determinato formato.

Questo approccio si basa sulla progettazione di prompt efficaci e quindi sull'analisi dell'output dei LLMs per far sì che estraggano bene le informazioni.

Qui, useremo Claude che è bravo a seguire le istruzioni! Vedi Modelli Antropici.

from langchain_anthropic.chat_models import ChatAnthropic

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

Suggerimento: Valgono le stesse considerazioni per la qualità dell'estrazione nell'approccio di analisi. Rivedi le linee guida per la qualità dell'estrazione.

Questo tutorial è pensato per essere semplice, ma in generale dovrebbe includere esempi di riferimento per ottenere prestazioni migliori!

Utilizzo di PydanticOutputParser

Nell'esempio seguente viene utilizzato il built-in PydanticOutputParser per analizzare l'output di un modello di 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):
    """Informazioni su una persona."""

    name: str = Field(..., description="Il nome della persona")
    height_in_meters: float = Field(
        ..., description="L'altezza della persona espressa in metri."
    )


class People(BaseModel):
    """Informazioni di identificazione su tutte le persone in un testo."""

    people: List[Person]


parser = PydanticOutputParser(pydantic_object=People)

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Rispondi alla query dell'utente. Incapsula l'output in tag `json`\n{format_instructions}",
        ),
        ("human", "{query}"),
    ]
).partial(format_instructions=parser.get_format_instructions())

Diamo un'occhiata alle informazioni inviate al modello.

query = "Anna ha 23 anni e è alta 1,83 metri"
print(prompt.format_prompt(query=query).to_string())
System: Rispondi alla query dell'utente. Incapsula l'output in tag `json`
L'output dovrebbe essere formattato come un'istanza JSON che rispetti lo schema JSON sotto riportato.

Come esempio, per lo schema {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
l'oggetto {"foo": ["bar", "baz"]} è un'istanza ben formattata dello schema. L'oggetto {"properties": {"foo": ["bar", "baz"]}} non è ben formattato.

Ecco lo schema di output:
{"description": "Informazioni di identificazione su tutte le persone in un testo.", "properties": {"people": {"title": "People", "type": "array", "items": {"$ref": "#/definitions/Person"}}}, "required": ["people"], "definitions": {"Person": {"title": "Person", "description": "Informazioni su una persona.", "type": "object", "properties": {"name": {"title": "Name", "description": "Il nome della persona", "type": "string"}, "height_in_meters": {"title": "Altezza In Metri", "description": "L'altezza della persona espressa in metri.", "type": "number"}}, "required": ["name", "height_in_meters"]}}}

Human: Anna ha 23 anni e è alta 1,83 metri
chain = prompt | model | parser
chain.invoke({"query": query})
People(people=[Person(name='Anna', height_in_meters=1.83)])

Analisi personalizzata

È facile creare un prompt e un parser personalizzati con LangChain e LCEL.

È possibile utilizzare una semplice funzione per analizzare l'output dal modello!

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):
    """Informazioni su una persona."""

    name: str = Field(..., description="Il nome della persona")
    height_in_meters: float = Field(
        ..., description="L'altezza della persona espressa in metri."
    )


class People(BaseModel):
    """Informazioni di identificazione su tutte le persone in un testo."""

    people: List[Person]


prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Rispondi alla richiesta dell'utente. Produci la tua risposta come JSON che  "
            "corrisponde allo schema fornito: ```json\n{schema}\n```. "
            "Assicurati di avvolgere la risposta in tag ```json e ```",
        ),
        ("human", "{query}"),
    ]
).partial(schema=People.schema())


def extract_json(message: AIMessage) -> List[dict]:
    """Estrae il contenuto JSON da una stringa in cui il JSON è incorporato tra i tag ```json e ```.

    Parametri:
        text (str): Il testo che contiene il contenuto JSON.

    Returns:
        list: Una lista di stringhe JSON estratte.
    """
    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"Impossibile analizzare: {message}")
query = "Anna ha 23 anni ed è alta 1,83 metri"
print(prompt.format_prompt(query=query).to_string())
System: Rispondi alla richiesta dell'utente. Produci la tua risposta come JSON che  corrisponde allo schema fornito: \`\`\`json
{'title': 'People', 'description': 'Informazioni di identificazione su tutte le persone in un testo.', 'type': 'object', 'properties': {'people': {'title': 'People', 'type': 'array', 'items': {'$ref': '#/definitions/Person'}}}, 'required': ['people'], 'definitions': {'Person': {'title': 'Person', 'description': 'Informazioni su una persona.', 'type': 'object', 'properties': {'name': {'title': 'Name', 'description': 'Il nome della persona', 'type': 'string'}, 'height_in_meters': {'title': 'Height In Meters', 'description': "L'altezza della persona espressa in metri.", 'type': 'number'}}, 'required': ['name', 'height_in_meters']}}}
\`\`\`. Assicurati di avvolgere la risposta in \`\`\`json e \`\`\`
Human: Anna ha 23 anni ed è alta 1,83 metri
chain = prompt | model | extract_json
chain.invoke({"query": query})
[{'people': [{'name': 'Anna', 'height_in_meters': 1.83}]}]

Altre librerie

Se stai cercando di estrarre usando un approccio di analisi, dai un'occhiata alla libreria Kor. È scritta da uno dei manutentori di LangChain e aiuta a creare un prompt che tiene conto degli esempi, consente di controllare i formati (ad esempio, JSON o CSV) ed esprime lo schema in TypeScript. Sembra funzionare molto bene!