Усі уроки · 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 із назвою випадку — на три тижні раніше, ніж прийшов би лист від клієнта.