Гайд · TNWS AI

Как отклонить фактически неверный ответ Pydantic AI через output_validator

4 мин—

Выходной валидатор Pydantic AI на примере суммы заказа: проверка суммы по каталогу, ModelRetry, границы повторов и критерий приёмки.

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

Поисковый вопрос: «как отклонить неверный результат Pydantic AI через output_validator». Валидировать не форму JSON, а проверяемое условие: сумма заказа обязана равняться произведению количества на цену из вашего каталога. Документация описывает @agent.output_validator и исключение ModelRetry: валидатор может вернуть результат либо потребовать новую попытку. Это не замена расчёту на стороне сервера, если сумму можно вычислить детерминированно; учебный пример показывает именно точку проверки ответа модели. Инструкция опирается на актуальную официальную документацию Pydantic AI, проверенную 20 сентября 2026 года. Примеры предназначены для Python 3.10+ с пакетом pydantic-ai. Используйте модель и учётные данные, доступные именно вашему аккаунту; пример идентификатора из документации не является обещанием доступа. Демонстрационные коды, статусы и суммы вымышлены.

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

  1. Определите схему с полями количества, цены и итоговой суммы; задайте цену в примере только как вымышленный тестовый факт.
  2. Зарегистрируйте выходной валидатор, который сравнивает итог с точным произведением и при несовпадении поднимает ModelRetry.
  3. Ограничьте бюджет повторных попыток, чтобы некорректные ответы не вызывали бесконечный цикл.
  4. Перед сохранением заявки проверьте числовые поля на тестах с верной и неверной суммой; в производственном коде рассчитывайте оплачиваемую сумму в доверенном сервисе.

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

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

from pydantic import BaseModel
from pydantic_ai import Agent, ModelRetry, RunContext

class Estimate(BaseModel):
    quantity: int
    unit_price: int
    total: int

agent = Agent("openai:gpt-5.2", output_type=Estimate,
              retries={"output": 2},
              instructions="Верни количество, цену за штуку и итог по условию.")

@agent.output_validator
def check_total(ctx: RunContext[None], output: Estimate) -> Estimate:
    if output.quantity < 0 or output.unit_price < 0:
        raise ModelRetry("Количество и цена не должны быть отрицательными.")
    if output.total != output.quantity * output.unit_price:
        raise ModelRetry("Пересчитай итог как quantity * unit_price.")
    return output

result = agent.run_sync("Учебный заказ: 3 штуки по 120 условных единиц.")
print(result.output)

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

Вход — вымышленный заказ на три единицы по 120 условных единиц за штуку. Принятый итог равен 360. Объект {quantity:3, unit_price:120, total:350} имеет правильные типы, но предметная проверка обязана отклонить его. Это не сценарий автоматического списания денег и не информация о цене продукта: число выбрано исключительно для арифметического теста. Реальную цену следует брать из сервера каталога, а не из сообщения пользователя или модели.

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

Валидатор получает уже распознанный вывод. Поэтому здесь одновременно видны два слоя: сначала Pydantic проверяет типы, после чего ваша функция проверяет отношение между полями. Сообщение ModelRetry попадает в механизм повторной генерации, однако он ограничен и может закончиться ошибкой; приложение обязано показать контролируемый отказ. Нельзя полагаться на то, что модель после подсказки всегда исправится. Если вычисляемое значение участвует в расчётах с деньгами, используйте Decimal и установленную финансовую политику округления, а окончательный платёжный итог получайте от вашего доверенного сервера. Для потоковых ответов документация отдельно предписывает учитывать ctx.partial_output, чтобы не валидировать недописанное значение как окончательное.

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

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

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

  1. Верный набор 3 × 120 принимается только с итогом 360.
  2. Ошибочный итог вызывает повтор либо исчерпание ограниченного бюджета, а не тихую запись 350.
  3. Отрицательные значения не проходят прикладное правило.

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

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

  • Не заменяйте контроль арифметики одним int в схеме: неверное число всё равно имеет тип int.
  • Не ставьте огромный бюджет retries, чтобы скрыть проблему данных или промпта.
  • Не принимайте сумму модели как источник истины для платежа.

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

Вызов валидатора обязателен?

При нормальном завершении run валидатор проверяет выданный объект.

Всегда ли ModelRetry исправляет ответ?

Нет; после исчерпания бюджета будет ошибка, которую нужно обработать.

Работает ли это с потоковой выдачей?

Да, но частичные значения требуют проверки ctx.partial_output.

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

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

Вызов валидатора обязателен?

При нормальном завершении run валидатор проверяет выданный объект.

Всегда ли ModelRetry исправляет ответ?

Нет; после исчерпания бюджета будет ошибка, которую нужно обработать.

Работает ли это с потоковой выдачей?

Да, но частичные значения требуют проверки `ctx.partial_output`.

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

Комментарии

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