Разбор

LLM, способные хорошо следовать инструкциям прошения, могут быть задействованы для вывода информации в заданном формате.

Этот подход зависит от разработки хороших подсказок, а затем разбора вывода LLM для того, чтобы они хорошо извлекали информацию.

Здесь мы будем использовать Клода, который отлично следует инструкциям! См. Anthropic models.

from langchain_anthropic.chat_models import ChatAnthropic

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

Совет: Все те же соображения касательно качества извлечения также применяются к подходу разбора. Ознакомьтесь с руководством по качеству извлечения.

Этот учебник предназначен для того, чтобы быть простым, но в общем случае должен включать примеры ссылок, чтобы извлечь максимальную производительность!

Использование PydanticOutputParser

В следующем примере используется встроенный PydanticOutputParser для разбора вывода модели чата.

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):
    """Информация о человеке."""

    name: str = Field(..., description="Имя человека")
    height_in_meters: float = Field(
        ..., description="Рост человека в метрах."
    )


class People(BaseModel):
    """Идентифицирующая информация о всех людях в тексте."""

    people: List[Person]


parser = PydanticOutputParser(pydantic_object=People)

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Ответьте на запрос пользователя. Оберните вывод в теги `json`\n{format_instructions}",
        ),
        ("human", "{query}"),
    ]
).partial(format_instructions=parser.get_format_instructions())

Давайте посмотрим, какая информация отправляется в модель

query = "Анна 23 года и ростом 1.83 м"
print(prompt.format_prompt(query=query).to_string())
Система: Ответьте на запрос пользователя. Оберните вывод в теги `json`
Вывод должен быть отформатирован как экземпляр JSON, соответствующий следующей схеме JSON.

В качестве примера для схемы {"properties": {"foo": {"title": "Foo", "description": "a list of strings", "type": "array", "items": {"type": "string"}}}, "required": ["foo"]}
объект {"foo": ["bar", "baz"]} является хорошо отформатированным экземпляром схемы. Объект {"properties": {"foo": ["bar", "baz"]}} не является хорошо отформатированным.

Вот схема вывода:
{"description": "Идентифицирующая информация о всех людях в тексте.", "properties": {"people": {"title": "People", "type": "array", "items": {"$ref": "#/definitions/Person"}}}, "required": ["people"], "definitions": {"Person": {"title": "Person", "description": "Информация о человеке.", "type": "object", "properties": {"name": {"title": "Имя", "description": "Имя человека", "type": "string"}, "height_in_meters": {"title": "Рост в метрах", "description": "Рост человека в метрах.", "type": "number"}}, "required": ["name", "height_in_meters"]}}}

Пользователь: Анна 23 года и ростом 1.83 м
chain = prompt | model | parser
chain.invoke({"query": query})
People(people=[Person(name='Anna', height_in_meters=1.83)])

Пользовательский анализ

Создание пользовательского запроса и парсера с помощью LangChain и LCEL очень просто.

Вы можете использовать простую функцию для анализа вывода модели!

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):
    """Информация о человеке."""

    name: str = Field(..., description="Имя человека")
    height_in_meters: float = Field(
        ..., description="Рост человека в метрах."
    )


class People(BaseModel):
    """Идентифицирующая информация обо всех людях в тексте."""

    people: List[Person]


prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Ответьте на запрос пользователя. Выведите ваш ответ в формате JSON, который соответствует указанной схеме: ```json\n{schema}\n```. "
            "Убедитесь, что ответ обернут в теги ```json и ```",
        ),
        ("human", "{query}"),
    ]
).partial(schema=People.schema())


def extract_json(message: AIMessage) -> List[dict]:
    """Извлекает содержимое JSON из строки, где JSON встроен между тегами ```json и ```.

    Параметры:
        text (str): Текст, содержащий содержимое JSON.

    Возвращает:
        list: Список извлеченных строк JSON.
    """
    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"Не удалось разобрать: {message}")
query = "Анна 23 года и её рост 183 сантиметра"
print(prompt.format_prompt(query=query).to_string())
System: Ответьте на запрос пользователя. Выведите ваш ответ в формате JSON, который соответствует указанной схеме: \`\`\`json
{'title': 'People', 'description': 'Идентифицирующая информация обо всех людях в тексте.', 'type': 'object', 'properties': {'people': {'title': 'People', 'type': 'array', 'items': {'$ref': '#/definitions/Person'}}}, 'required': ['people'], 'definitions': {'Person': {'title': 'Person', 'description': 'Информация о человеке.', 'type': 'object', 'properties': {'name': {'title': 'Name', 'description': 'Имя человека', 'type': 'string'}, 'height_in_meters': {'title': 'Height In Meters', 'description': 'Рост человека в метрах.', 'type': 'number'}}, 'required': ['name', 'height_in_meters']}}}
\`\`\`. Убедитесь, что ответ обернут в теги \`\`\`json и \`\`\`
Human: Анна 23 года и её рост 183 сантиметра
chain = prompt | model | extract_json
chain.invoke({"query": query})
[{'people': [{'name': 'Anna', 'height_in_meters': 1.83}]}]

Другие библиотеки

Если вам интересен подход к извлечению с использованием парсера, обратите внимание на библиотеку Kor. Это написано одним из разработчиков LangChain и помогает создать запрос, учитывающий примеры, позволяет контролировать форматы (например, JSON или CSV) и выражает схему в TypeScript. Кажется, что это работает отлично!