Разбор
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. Кажется, что это работает отлично!