Гайд · TNWS AI

Как извлечь поля заявки из русского письма через Pydantic AI и output_type

4 мин—

Пошаговое извлечение темы и номера заявки в Pydantic AI: схема BaseModel, конкретное письмо, проверка полей и ошибки, которые не исправит одна валидация.

Что решаем и кому подходит

Поисковый вопрос: «как извлечь структурированную заявку из письма через Pydantic AI». Получить объект с двумя строго названными полями из письма в поддержку, а не свободный пересказ. В официальном руководстве Pydantic AI для этого используется аргумент output_type с моделью Pydantic. result.output после удачного прогона — экземпляр заданного класса. Возможность модели создавать структурированный вывод зависит от выбранного провайдера и режима; не обещайте одинакового поведения всех моделей. Инструкция опирается на актуальную официальную документацию Pydantic AI, проверенную 20 сентября 2026 года. Примеры предназначены для Python 3.10+ с пакетом pydantic-ai. Используйте модель и учётные данные, доступные именно вашему аккаунту; пример идентификатора из документации не является обещанием доступа. Демонстрационные коды, статусы и суммы вымышлены.

Порядок действий

  1. Создайте схему заявки с номером и темой, определите допустимый формат номера в своей предметной области.
  2. Создайте Agent с проверенным идентификатором модели, инструкцией и output_type=Ticket; ключ провайдера храните в окружении.
  3. Передайте одно вымышленное письмо через run_sync; результатом используйте result.output, а не произвольный текст ответа.
  4. Проверьте тип объекта, извлечённые значения и отдельно соответствие исходному письму; негативным тестом сделайте письмо без номера.

До сетевого запуска установите pydantic-ai и настройте ключ провайдера предусмотренным им способом. Примеры не содержат действующих секретов. Для воспроизводимого теста зафиксируйте версии Python, пакета и выбранной модели; после обновления снова выполните как удачный, так и отрицательный сценарий. Вызов run_sync использован для наглядного скрипта вне уже работающего асинхронного цикла. Там, где проект асинхронный, применяйте документированный await agent.run(...) и управляйте ресурсами на стороне приложения.

Готовый шаблон для запуска

from pydantic import BaseModel, Field
from pydantic_ai import Agent

class Ticket(BaseModel):
    number: str = Field(description="Номер обращения из письма")
    topic: str = Field(description="Краткая тема письма")

agent = Agent("openai:gpt-5.2", output_type=Ticket,
              instructions="Извлеки только прямо указанные данные. Не придумывай номер.")
letter = "Тема: доставка. Здравствуйте! По обращению A-204 не пришла посылка."
result = agent.run_sync(letter)
assert isinstance(result.output, Ticket)
print(result.output.model_dump())

Конкретный вход и ожидаемый результат

Вход: «Тема: доставка. Здравствуйте! По обращению A-204 не пришла посылка». Предметно ожидаются number="A-204" и тема о задержке доставки; словесная формулировка темы может отличаться. Второй вход «Где моя посылка?» не содержит номера: для него нельзя считать успехом аккуратный JSON с выдуманным идентификатором. Выберите политику отсутствующих значений — отдельное поле None, запрос на уточнение или отказ от автоматической записи — до запуска, а не после получения ответа.

Почему этот способ работает

BaseModel устанавливает форму, а не истинность извлечённых фактов. Модель может вернуть синтаксически правильную строку A-240 вместо A-204; схема это пропустит. Для номера заявки добавьте постпроверку по регулярному выражению и сопоставление с буквальным фрагментом входа. Если письмо содержит два разных номера, не выбирайте первый без правила: обозначьте, что главный номер — тот, который связан с текущим запросом, и сохраните неоднозначность для оператора. В рабочем конвейере не отправляйте ответ сразу в CRM после одной лишь проверки типа. Отдельно тестируйте русские буквы, дефисы, пробелы и сообщения с цитатой старой переписки.

Пограничный случай и предметная проверка

Полезный регрессионный набор содержит пять писем: одно стандартное, одно с двумя номерами, одно без номера, одно с кириллическим номером и одно с длинной цитатой предыдущего ответа. Для каждого заранее запишите ожидаемый номер либо явный признак неоднозначности. Проверяйте не буквальное совпадение темы, а наличие заданного смысла и отсутствие неподтверждённых обещаний. Если формат входа изменился после обновления почтового шаблона, запустите этот набор повторно. Точное имя модели в примере взято из документации и не является утверждением о том, что она подключена у каждого читателя.

Критерии приёмки

  1. Вывод имеет тип Ticket, а ключи в сериализации названы number и topic.
  2. Номер A-204 взят из сообщения без перестановки цифр; смысл темы соответствует задержке.
  3. На письме без номера автоматическая обработка не выдаёт несуществующий номер: предусмотрено явное решение оператора.

Проверка успешного выполнения Python-программы не равна проверке верности ответа нейросети. Сопоставьте результат с входом и с доверенным источником данных, затем повторите тест на отрицательном примере. Формулировка текста может отличаться от образца; условия приёмки описывают наблюдаемое поведение, которое действительно нужно пользователю. Если один критерий не соблюдён, не скрывайте сбой повтором без ограничения: сохраните минимальный безопасный воспроизводимый пример и исправьте соответствующий слой — данные, схему, инструмент, инструкцию или обработку ошибки.

Частые ошибки и чего не делать

  • Не подменяйте output_type просьбой «напиши JSON»: текст может оказаться невалидным или содержать лишние поля.
  • Не считайте прохождение Pydantic-валидации доказательством правильности всех фактов.
  • Не записывайте реальные клиентские письма и ключи API в открытые примеры и журналы.

Вопросы и ответы

Можно ли сразу сохранить result.output?

Сначала проверьте предметные правила и затем сериализуйте через model_dump().

Нужен ли номер в каждом письме?

Если номер отсутствует, запросите уточнение или проектируйте необязательное поле; не выдумывайте его.

Одинаково ли работают все модели?

Нет. Поддержку режима вывода проверяют на конкретной модели и провайдере.

Официальные источники

Частые вопросы

Можно ли сразу сохранить `result.output`?

Сначала проверьте предметные правила и затем сериализуйте через `model_dump()`.

Нужен ли номер в каждом письме?

Если номер отсутствует, запросите уточнение или проектируйте необязательное поле; не выдумывайте его.

Одинаково ли работают все модели?

Нет. Поддержку режима вывода проверяют на конкретной модели и провайдере.

Читайте также

Комментарии

Пока тихо. Скажите первое слово