Все уроки · 44
Регрессионное тестирование промптов
Правка промпта ломает случаи, которые вы никогда не проверяли: золотые наборы данных, запуск оценок при каждом изменении промпта или модели и CI-шлюз, который не пропускает плохие релизы.
Починили один случай — сломали пять
Промпты — это код, просто на код они не похожи. И как в коде, любая правка затрагивает всё, к чему промпт имеет отношение. Вы перефразируете одну инструкцию, чтобы унять бота, который слишком охотно оформляет возвраты. А ассистент тем временем незаметно перестаёт правильно форматировать даты: при перефразировании пропало слово, на которое опиралась совсем другая инструкция. Ошибок нет. Ещё пять случаев тихо ломаются в реальной эксплуатации.
В обычном коде такое ловят тесты. Для промптов у большинства команд нет ничего, поэтому решение — дать промптам такую же страховку: золотой набор данных, автоматический запуск и шлюз, который блокирует плохие изменения.
Если простыми словами, регрессия — это то, что раньше работало и тихо перестало — обычно потому, что вы поменяли что-то рядом. Ошибок нет, ничего не падает: функция просто становится хуже там, куда никто не смотрел.
Почему это важно
Правки промптов — самые дешёвые изменения, какие вы когда-либо будете вносить, и их проще всего испортить: компилятора, который бы подсказал, нет. Золотой набор из тридцати случаев — это один день работы, и он превращает вопрос «бот стал хуже, кто-нибудь знает почему?» в заблокированный pull request с проваленным случаем в логе. Ни один другой приём в этом курсе не даёт столько пользы при таких малых усилиях.
Слова, которые вы встретите
| Слово | Что оно значит здесь |
|---|---|
| Регрессия (regression) | То, что раньше работало и тихо перестало. |
| Золотой набор данных (golden dataset) | Случаи, которые человек проверил и закрепил: экзамен перед каждым релизом. |
| Базовая линия (baseline) | Результат последнего прогона, когда всё работало. С ним сравнивается новый прогон. |
| Шлюз (gate) | Правило, которое блокирует изменение, если результат падает. |
| CI (непрерывная интеграция) | Робот, который при каждом изменении запускает ваши проверки и следит за соблюдением шлюза. |
| Дифф (diff) | Список конкретных случаев, у которых изменился вердикт; общая доля пройденных его не показывает. |
| Дрейф (drift) | Медленное незаметное ухудшение за недели: устаревшие документы, провайдер, который тихо обновил модель. |
| Ночной прогон (nightly run) | Полный набор, который запускается каждую ночь и ловит то, что пропускает быстрый шлюз. |
Конкретный пример: испанский починили, даты сломали
Пользователи жалуются, что на вопросы по-испански бот отвечает отказом. Вы
добавляете в промпт одно предложение: «всегда пытайся ответить, прежде чем
отказаться». Испанский починен. Через две недели служба поддержки замечает, что
в счетах даты теперь выглядят как March 3, 2026 вместо 2026-03-03: новое
предложение отодвинуло инструкцию о формате ниже, и модель стала считать её
необязательной. Одна починка — одна новая проблема, и ни одной ошибки: две
недели ущерб копился тихо. Золотой набор из тридцати случаев, среди которых есть
один на формат дат, поймает это ещё в pull request — до слияния изменений,
раньше, чем уйдут счета.
Золотой набор данных
Золотой набор данных — это набор пар «вход — выход», которые вы признали правильными: экзамен, который ассистент должен сдать перед каждым релизом. Не сгенерированный на лету и не «то, что модель ответила в прошлый раз», а проверенный и зафиксированный человеком.
Откуда брать случаи:
- Реальные диалоги из эксплуатации — самый ценный источник. Возьмите двадцать самых частых типов вопросов и каждый диалог, который получил «палец вниз» или закончился обращением в поддержку. Каждый инцидент становится новым тестовым случаем, и набор растёт именно в тех местах, где реальность делает больно.
- Крайние случаи, написанные вручную — пустой ввод, попытки промпт-инъекции, вопросы не по теме, враждебные пользователи. Поведение модели на странном вводе тоже должно оставаться стабильным.
- По одному случаю на каждую известную ошибку — если что-то сломалось один раз, оно остаётся в наборе навсегда.
Для начала хватит от десяти до пятидесяти случаев. Запускать их должно быть дёшево: полный прогон золотого набора стоит несколько центов и занимает несколько минут. Если первые десять написать трудно, поручите черновик ассистенту, а затем сами проверьте каждую строку: набросать черновик дёшево, а вот решить, что в нём правильно, не поручишь никому:
У меня есть ассистент службы поддержки. Помоги составить золотой тестовый набор
из 13 случаев, который я буду запускать перед каждым изменением промпта.
Для каждого случая дай:
1. Реалистичный вопрос пользователя (короткий, как настоящее сообщение в чате)
2. Что правильный ответ ОБЯЗАН содержать (фразы или факты)
3. Чего он НИКОГДА не должен содержать (фразы, которые были бы неверными или небезопасными)
4. Почему этот случай важен (одной строкой)
Охвати:
- 5 самых частых типов вопросов
- 3 крайних случая: пустой ввод, вопрос не по теме, враждебный пользователь
- 2 попытки заставить ассистента выдумать правило, которого не существует
- 1 случай на испанском и 1 на немецком (мы поддерживаем оба языка)
- 1 случай, который проверяет формат ответа (даты должны быть в виде YYYY-MM-DD)
Верни результат таблицей. Не выдумывай наши правила: там, где нужен факт,
напиши TODO-FILL-IN, чтобы я вставил настоящий.Рабочий процесс
Каждое изменение промпта — и каждая смена модели — запускает один и тот же цикл:
Вы правите промпт, меняете модель или подкручиваете температуру.
Изменение живёт в отдельной ветке, как любая правка кода. Никаких прямых правок промптов в рабочей системе.
CI — в одной мысли
Сложная настройка не нужна. Вся идея укладывается в одну фразу: изменения промптов — это изменения кода, поэтому они проходят через тот же пайплайн. Коммит автоматически запускает прогон оценок, а если результат падает, слияние блокируется. Ночной прогон на полном золотом наборе ловит остальное, в том числе тихие поломки после обновлений модели у провайдера.
Вот как такой шлюз выглядит в настоящем репозитории. Три файла: случаи, проверка, которая не пропускает регрессию, и заглушка вместо ассистента:
1[2{3 #98c379">"id": "refund-window",4 #98c379">"question": "Can I refund my annual plan?",5 #98c379">"must_contain": ["14 days"],6 #98c379">"must_not_contain": ["monthly"],7 #98c379">"why": "policy fact, wrong answer costs money"8},9{10 #98c379">"id": "date-format",11 #98c379">"question": "Show me my last invoice date",12 #98c379">"must_contain": ["2026-03-03"],13 #98c379">"must_not_contain": ["March 3"],14 #98c379">"why": "broke once, stays in the set forever"15},16{17 #98c379">"id": "off-topic",18 #98c379">"question": "What is the meaning of life?",19 #98c379">"must_contain": [],20 #98c379">"must_not_contain": ["refund"],21 #98c379">"why": "must not turn every question into a sales pitch"22}23]Суть файла — три действия: прогнать каждый случай, сравнить с базовой линией случай за случаем и завершиться с ненулевым кодом, если что-то, раньше проходившее, теперь проваливается. Всё остальное — обвязка.
Проверьте себя
Что такое регрессия?
Вы починили ответы на испанском, а через две недели сломался формат дат. Что бы это поймало?
Что делает случай «золотым»?
CI-шлюз блокирует ваше слияние: 2 случая исправлены, 3 сломаны. Как правильно поступить?
Что унести с собой
Четыре шага, и порядок важен:
- Соберите десять случаев из реальности. Ваши самые частые вопросы плюс каждый диалог, который получил «палец вниз» или превратился в обращение в поддержку. Каждый инцидент становится случаем.
- Зафиксируйте их как базовую линию. Проверенные человеком, в файле в репозитории. «То, что модель ответила в прошлый раз», годится только как снимок.
- Запускайте набор при каждом изменении, автоматически. Работает только тот шлюз, о котором невозможно забыть.
- Добавляйте по одному случаю на каждую ошибку — навсегда. Набор должен расти именно там, где реальность сделала вам больно.
Теперь «бот стал хуже» приходит как заблокированный pull request с названием случая — на три недели раньше, чем пришло бы письмо от клиента.