Использование примеров ссылок

Качество извлечений часто можно улучшить, предоставляя ссылочные примеры для LLM.

Совет: Хотя этот учебник фокусируется на том, как использовать примеры с вызовом модели, эта техника в целом применима и будет работать также с JSON или на основе подсказок.

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

prompt = ChatPromptTemplate.from_messages(
    [
        (
            "system",
            "Вы - экспертный алгоритм извлечения. "
            "Извлекайте только актуальную информацию из текста. "
            "Если вы не знаете значение запрашиваемого атрибута, верните null для его значения.",
        ),
        MessagesPlaceholder("examples"),  # <-- ПРИМЕРЫ!
        ("human", "{text}"),
    ]
)

Проверьте шаблон:

from langchain_core.messages import (
    HumanMessage,
)

prompt.invoke(
    {"text": "это некоторый текст", "examples": [HumanMessage(content="тестирование 1 2 3")]}
)
ChatPromptValue(messages=[SystemMessage(content="Вы - экспертный алгоритм извлечения. Извлекайте только актуальную информацию из текста. Если вы не знаете значение запрашиваемого атрибута, верните null для его значения."), HumanMessage(content='тестирование 1 2 3'), HumanMessage(content='это некоторый текст')])

Определение схемы

Давайте повторно использовать схему персоны из быстрого старта.

from typing import List, Optional

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


class Person(BaseModel):
    """Информация о человеке."""


    name: Optional[str] = Field(..., description="Имя человека")
    hair_color: Optional[str] = Field(
        ..., description="Цвет волос человека, если известен"
    )
    height_in_meters: Optional[str] = Field(..., description="Рост в метрах")


class Data(BaseModel):
    """Извлеченные данные о людях."""

    people: List[Person]

Определение примеров ссылки

Примеры можно определить как набор пар ввод-вывод.

Каждый пример содержит входной текст input и вывод output, показывающий, что должно быть извлечено из текста.

инфо

Это немного глубоко, так что не стесняйтесь игнорировать, если вы этого не понимаете!

Формат примера должен соответствовать используемому API (например, вызову инструмента или режиму JSON и т. д.).

Здесь отформатированные примеры будут соответствовать ожидаемому формату для вызова API инструмента, поскольку именно это мы используем.

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):
    """Представление примера, состоящего из текстового ввода и ожидаемых вызовов инструментов.

    Для извлечения вызовы инструментов представлены в виде экземпляров модели pydantic.
    """

    input: str  # Это текст примера
    tool_calls: List[BaseModel]  # Экземпляры модели pydantic, которые должны быть извлечены


def tool_example_to_messages(example: Example) -> List[BaseMessage]:
    """Преобразовать пример в список сообщений, которые можно передать в LLM.

    Этот код - это адаптер, который преобразует наш пример в список сообщений,
    которые можно передать в модель чата.

    Список сообщений на каждый пример соответствует:

    1) HumanMessage: содержит содержимое, из которого должно быть извлечено содержимое.
    2) AIMessage: содержит извлеченную информацию из модели
    3) ToolMessage: содержит подтверждение модели о том, что модель правильно запросила инструмент.

    ToolMessage необходим, потому что некоторые модели чата гипероптимизированы для агентов, а не для случая извлечения.
    """
    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": "function",
                "function": {
                    "name": tool_call.__class__.__name__,
                    "arguments": tool_call.json(),
                },
            }
        )
    messages.append(
        AIMessage(content="", additional_kwargs={"tool_calls": openai_tool_calls})
    )
    tool_outputs = example.get("tool_outputs") or [
        "Вы правильно вызвали этот инструмент."
    ] * 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

Теперь давайте определим наши примеры и преобразуем их в формат сообщений.

examples = [
    (
        "Океан обширен и голубой. В нем более 20 000 футов глубины. В нем много рыбы.",
        Человек(имя=None, рост_в_метрах=None, цвет_волос=None),
    ),
    (
        "Фиона путешествовала из Франции в Испанию.",
        Человек(имя="Фиона", рост_в_метрах=None, цвет_волос=None),
    ),
]

messages = []

for текст, вызов_инструмента in examples:
    messages.extend(
        tool_example_to_messages({"input": текст, "tool_calls": [вызов_инструмента]})
    )

Давайте протестируем запрос

prompt.invoke({"text": "это некий текст", "examples": messages})
ChatPromptValue(messages=[SystemMessage(content="Ты - эксперт алгоритма извлечения. Извлекай только актуальную информацию из текста. Если тебе неизвестно значение запрашиваемого атрибута, верни значение null для этого атрибута."), HumanMessage(content="Океан огромен и голубой. В нем более 20 000 футов глубины. Там много рыбы."), 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='Ты правильно вызвал этот инструмент.', tool_call_id='c75e57cc-8212-4959-81e9-9477b0b79126'), HumanMessage(content='Фиона отправилась далеко ото Франции в Испанию.'), 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='Ты правильно вызвал этот инструмент.', tool_call_id='69da50b5-e427-44be-b396-1e56d821c6b0'), HumanMessage(content='Это какой-то текст')])

Создание извлекателя

Здесь мы создадим извлекатель, используя 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: Функция `with_structured_output` находится в бета-версии. В настоящее время ведется активная работа, поэтому API может измениться.
  warn_beta(

Без примеров

Заметьте, что, хотя мы используем gpt-4, он не справляется с очень простым тестовым случаем!

for _ in range(5):
    text = "Солнечная система большая, но у Земли всего 1 луна."
    print(runnable.invoke({"text": text, "examples": []}))
люди=[]
люди=[Человек(имя='земля', цвет_волос=None, рост_в_метрах=None)]
люди=[Человек(имя='земля', цвет_волос=None, рост_в_метрах=None)]
люди=[]
люди=[]

С примерами

Примеры помогают исправить сбой!

for _ in range(5):
    text = "Солнечная система большая, но у Земли всего 1 луна."
    print(runnable.invoke({"text": text, "examples": messages}))
люди=[]
люди=[]
люди=[]
люди=[]
люди=[]
runnable.invoke(
    {
        "text": "Меня зовут Харрисон. У меня черные волосы.",
        "examples": messages,
    }
)
Data(people=[Person(name='Харрисон', hair_color='черный', height_in_meters=None)])