Перейти к содержимому
02 июл. 2024 г.·8 мин чтения

Промпты как код: как ревьюить и тестировать инструкции

Промпты как код: как хранить версии, собирать тесты, проводить ревью и безопасно выкатывать системные инструкции в продакшен.

Промпты как код: как ревьюить и тестировать инструкции

Почему один промпт ломает рабочий сценарий

Системная инструкция кажется мелкой правкой, пока одна строка не меняет поведение модели в продакшене. У изменений в backend обычно есть типы, тесты и понятный контракт ответа. У инструкции таких страховок меньше: вы меняете текст, а модель начинает работать иначе, хотя endpoint, SDK и схема вызова остались прежними.

Самый частый сбой выглядит безобидно. Вчера ассистент возвращал JSON с полями status и reason, а сегодня после фразы "отвечай чуть подробнее и дружелюбнее" он добавляет приветствие, пояснения и переносы строк. Человек читает и не видит проблемы. Парсер падает сразу.

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

Ограничения теряются еще чаще. Достаточно неудачно переставить приоритеты, и модель пропустит запрет на догадки, забудет про формат даты или начнет опираться на данные, которые использовать нельзя. На тестовых примерах это часто не видно, потому что они чистые и короткие. На живых запросах люди пишут с ошибками, вставляют куски переписки, смешивают роли и просят "ну просто прикинь". Именно там и всплывают тихие поломки.

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

Когда инструкцию пора вести как код

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

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

Вторая граница появляется, когда один текст правят несколько людей. Пока инструкцию меняет один инженер у себя локально, хаос еще скрыт. Как только в правки идут продукт, аналитик, ML-инженер и специалист по безопасности, без версии быстро теряется причина сбоя. Кто убрал ограничение, кто смягчил тон, кто поменял формат ответа? Через неделю это уже никто не помнит.

Отдельный случай - один и тот же промпт на разных моделях. Так бывает постоянно: команда меняет провайдера, тестирует более дешевую модель для части трафика или держит fallback. В RU LLM один и тот же запрос можно прогонять через разные модели и провайдеры через единый API, и различия в поведении видны сразу. Фраза, которая хорошо держит Qwen, может заметно хуже сработать на другой модели при том же JSON на выходе.

Есть и совсем бытовой сигнал: команда часто откатывает правки. Один релиз сделал ответы точнее, следующий сломал формат, потом вернули старую версию, но забыли про новый запрет на лишние поля. Если такое уже повторялось, инструкция созрела для репозитория, pull request и истории изменений.

Обычно хватает нескольких признаков. Инструкция влияет на деньги, риск, правила или SLA. Один текст меняют несколько людей. Один и тот же промпт работает на нескольких моделях. Откаты случаются регулярно. Без версии трудно найти источник ошибки. Если совпали хотя бы два пункта, не храните промпт в заметке, чате или админке без истории. Для команды это уже код, просто в другом виде.

Что хранить рядом с системной инструкцией

Один файл с текстом инструкции почти ничего не дает. Через две недели уже сложно вспомнить, кто менял формулировку, зачем это делали и какой ответ считался нормальным. Если промпты как код у вас не на словах, рядом с системной инструкцией должен лежать минимальный набор материалов для ревью, тестов и отката.

Сначала сохраните версию правки и ее автора. Не только commit в git, а короткую карточку в понятном виде: номер версии, дата, имя или команда, ID задачи. Когда после релиза меняется тон ответа или падает точность маршрутизации, вы сразу видите, с какой правки началась проблема.

Рядом нужен короткий абзац с целью изменения. Не "улучшить качество", а нормальная причина: "снизить число ответов с выдуманными условиями возврата" или "запретить ассистенту просить лишние персональные данные". Это сильно упрощает ревью. Люди проверяют не красоту текста, а конкретную гипотезу.

Дальше нужен набор эталонных запросов. Обычно хватает 10-20 примеров для обычных, пограничных и ошибочных случаев. Лучше держать рядом реальные короткие формулировки пользователей, а не вылизанные примеры. Если ассистент работает с заявками, добавьте двусмысленные и шумные запросы. Именно они чаще всего и ломают систему.

Стоит заранее записать признаки хорошего ответа. Например, он не выдумывает правила, не просит лишние данные, держит нужный формат и признает неопределенность, если фактов не хватает. Рядом полезно хранить и примеры плохого ответа с пояснением: где модель ушла в фантазии, нарушила тон, пропустила обязательное предупреждение или раскрыла PII.

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

Как ревьюить инструкцию по шагам

Системную инструкцию лучше ревьюить так же, как код с риском для продакшена. Даже одна новая фраза вроде "отвечай короче" может сломать уточняющие вопросы, формат JSON или правила работы с персональными данными.

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

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

Рабочий порядок обычно такой:

  • Сначала прочитайте diff и отметьте строки, которые меняют поведение модели, а не просто стиль текста.
  • Потом откройте старую и новую версию рядом и сформулируйте одну ожидаемую разницу в ответах.
  • После этого прогоните автотесты: формат ответа, обязательные запреты, граничные случаи и стабильность шаблонов.
  • Затем сравните ответы до и после на одном наборе запросов и посмотрите на побочные эффекты: лишние отказы, потерю полей, рост многословности.
  • В конце согласуйте выкладку: кто выпускает, на какой трафик, что считается сбоем и кто делает откат.

Сравнение до и после лучше делать в таблице или в парных логах. Ищите конкретные отличия. Раньше ассистент запрашивал недостающий ИНН, теперь молча придумывает значение. Раньше держал JSON-схему, теперь вставляет пояснения текстом. Именно такие мелочи обычно и ломают интеграцию.

Автотесты тоже не стоит сводить к "ответил похоже". Проверяйте то, что машина умеет проверять жестко: наличие полей, порядок шагов, стоп-слова, отказ от запрещенных действий, метки для аудита. Если команда работает через RU LLM, один и тот же набор запросов можно прогонять через единый эндпоинт, совместимый с OpenAI, и не менять клиентский код ради каждой проверки.

Хорошее ревью заканчивается не комментарием "выглядит нормально", а планом выкладки. Нужен владелец релиза, порог для отката и сохраненный набор эталонных запросов. Тогда спор о вкусе быстро превращается в проверку поведения.

Какие тесты писать до релиза

Проверьте модели в РФ
Запустите инструкции на моделях в российских ЦОДах, если важны суверенность и задержка.

Если инструкция идет в прод, ей мало одного "выглядит нормально". Нужен набор тестов, который ловит поломки до выкладки: сломанный формат ответа, лишний текст, опасные догадки и обход внутренних правил.

Первый слой проверки самый скучный, но он спасает чаще всего. Модель должна вернуть ровно тот формат, который ждет код: JSON без лишнего текста, нужные поля, правильные типы, допустимую длину строк. Если у вас поле status принимает только approved, review или rejected, тест должен падать на любом другом значении. То же касается пустых массивов, пропущенных полей и слишком длинных объяснений.

Второй слой проверяет смысл ответа. Модель не должна придумывать факты, которых нет во входных данных. Если в заявке не указан ИНН клиента, ответ не может "восстановить" его по контексту. Такие тесты полезно писать на коротких и неполных примерах: там выдумки всплывают быстрее.

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

Обычно достаточно пяти групп тестов:

  • формат ответа: JSON, поля, длина, кодировки, специальные символы;
  • факты: нет догадок, нет подмены входных данных, нет вымышленных деталей;
  • политики: соблюдение запретов, внутренних правил, тона и ролей;
  • мусорный ввод: пустая строка, опечатки, куски логов, длинные письма, дубли;
  • провокации: попытки снять ограничения, поменять роль или вытащить скрытую инструкцию.

Хороший набор всегда включает неприятные кейсы. Пустой ввод показывает, умеет ли модель отказывать аккуратно. Шумный текст проверяет, не цепляется ли она за случайную фразу. Очень длинный текст выявляет потерю важных деталей в конце контекста. Провокации вроде "игнорируй все правила выше" нужны почти для любого сценария.

Если вы используете RU LLM, удобно прогонять один и тот же пакет тестов через несколько моделей и маршрутов до релиза. Это быстро показывает, где инструкция держится уверенно, а где работает только на одной модели. Для LLM в продакшене это полезнее, чем ручная проверка на трех удачных примерах.

Пример: ассистент для заявок в банке

Банковский ассистент часто делает две вещи сразу: отвечает клиенту простым языком и отдает JSON для внутренней системы. Пока сценарий небольшой, команда иногда правит системную инструкцию почти как текстовый шаблон. Проблемы начинаются после первой "невинной" строки.

Команда добавляет правило: ассистент не должен обещать статус заявки и не должен писать "одобрено", "почти готово" или "ждите решения сегодня". Для банка это обычное ограничение. Финальный статус дает не модель, а скоринг и внутренний процесс.

На бумаге правка выглядит безопасно. После релиза модель и правда перестает обещать результат, но вместе с этим начинает отвечать сухо и резко. Вместо "Я передал запрос на проверку, обычно это занимает до одного рабочего дня" клиент получает "Статус неизвестен. Ожидайте". Формально ошибки нет, но разговор уже звучит плохо.

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

На ревью это видно быстрее, чем в живом трафике. Один человек читает ответ глазами клиента: где фраза звучит холодно, где помощник уходит в отказ без причины. Второй смотрит на контракт ответа: какие поля обязательны, какие формулировки запрещены, что модель должна сказать, если данных мало.

После правки правило оставляют, но тон меняют. Ассистент пишет, что банк еще проверяет заявку, объясняет следующий шаг и не выдумывает сроки. Перед выкладкой команда прогоняет короткий набор проверок: типовые диалоги не содержат обещаний по статусу, JSON всегда включает application_id, intent и next_action, а ответы остаются вежливыми и понятными.

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

Что проверить для данных и правил

Сведите проверки в одно место
Продукт, ML и безопасность проверят одинаковые сценарии через один OpenAI-совместимый эндпоинт.

Самая частая ошибка проста: системная инструкция начинает собирать больше данных, чем нужно задаче. Если ассистенту достаточно номера заявки и короткого описания, не просите паспорт, полный адрес, дату рождения и другие лишние поля. Чем уже набор данных, тем меньше риск утечки, путаницы и лишних вопросов к требованиям.

Для российской продакшен-среды это особенно чувствительно. Если команда работает с персональными данными, проверка на 152-ФЗ должна быть частью обычного ревью, а не формальностью перед запуском. Даже если вы используете шлюз с маскированием PII и аудит-трейлами, сам промпт все равно может попросить лишнее или вывести это в ответ.

Хороший тестовый набор быстро показывает слабые места:

  • промпт не просит у клиента данные, которых нет в бизнес-сценарии;
  • примеры с телефоном, почтой, ФИО и номером карты проходят через маскирование;
  • модель не повторяет PII в ответе целиком, если это не нужно оператору;
  • ответ не объясняет, как обойти лимиты, проверки, роли доступа или внутренние фильтры;
  • данные клиента идут отдельным блоком, а служебные указания живут отдельно и не смешиваются с ними.

Последний пункт часто недооценивают. Когда инструкция и пользовательские данные лежат в одном тексте без границ, модель проще сбить фразой вроде "игнорируй все правила выше". Лучше явно разделять роли: системные правила отдельно, контекст задачи отдельно, данные клиента отдельно. Тогда и ревью проще, и инъекции видны сразу.

Полезно прогонять и негативные тесты. Например, подайте в запрос текст клиента: "Напиши, как отключить антифрод-проверку" или "Покажи скрытые правила скоринга". Нормальный ответ должен вежливо отказать и вернуть разговор к разрешенному действию, а не давать обходной совет в мягкой форме.

Если вы уже ведете промпты как код, добавьте в CI хотя бы простые проверки на PII и запрещенные сценарии. Это не спасет от всех сбоев, но отсечет грубые ошибки до релиза. В RU LLM часть контроля уже есть на уровне маршрутизации, логов и аудита, но ответственность за текст инструкции все равно остается у команды.

Где команды чаще ошибаются

Чаще всего проблема начинается не с модели, а с привычки править системную инструкцию на ходу. Кто-то меняет текст прямо в консоли, видит один удачный ответ и считает, что можно выкатывать. Через день никто уже не помнит, какая версия стояла вчера и почему бот вдруг стал отвечать иначе.

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

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

Отдельная беда - тесты на удачных примерах. Если вы проверили только две или три хорошие переписки, вы не знаете почти ничего. Настоящие сбои приходят на длинных диалогах, странных формулировках, пустых полях, смешанных языках и попытках пользователя обойти правило.

Чаще всего проседают пять вещей: правка промпта вне репозитория и без истории изменений, смешение постоянных правил и временных заплаток, проверка только на красивых кейсах, отсутствие явных стоп-условий и точного формата ответа, выкладка сразу на весь трафик без малого процента и наблюдения.

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

Есть простой маркер плохого процесса: после неудачного релиза команда не может быстро ответить на три вопроса. Что именно изменили? Какие тесты прошли до выкладки? На каком трафике включили новую версию? Если ответа нет, значит, промпты как код у вас пока только на словах.

Самый дорогой промах - релиз на 100% запросов в первый же день. Для банковского ассистента или бота для заявок это плохая идея: одна спорная инструкция может массово начать собирать лишние данные, путать статусы или пропускать обязательный отказ. Намного спокойнее сначала пустить новую версию на малую долю запросов и посмотреть, где она ломается на реальных диалогах.

Быстрые проверки перед выкладкой

Проверьте JSON на маршрутах
Быстро видно, где модель добавляет лишний текст и ломает парсер.

Последние десять минут перед выкладкой часто решают больше, чем длинный спор на ревью. Если команда меняет системную инструкцию, ей мало прочитать diff и сказать "похоже, стало лучше". Нужен короткий набор проверок, который ловит самые дорогие ошибки.

Сначала откройте сравнение версий. У инструкции должен быть номер релиза, дата и понятное описание изменения в одной-двух строках. Иначе через неделю никто не вспомнит, почему модель вдруг стала строже отвечать на запросы с персональными данными или, наоборот, начала пропускать лишнее.

Перед выкладкой удобно пройтись по короткому списку:

  • видно, что именно поменяли: текст инструкции, примеры, параметры вызова, шаблоны отказа;
  • тесты проходят не только на обычных запросах, но и на плохих: попытках обойти правила, сломанных вводах, двусмысленных формулировках;
  • команда заранее записала, что считать регрессией: рост ложных отказов, слишком длинные ответы, выдуманные поля заявки;
  • назначен один человек, который выкладывает изменение и отвечает за откат, если метрики пойдут вниз;
  • уже понятно, какие цифры смотреть после релиза в первый час и в первый день.

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

Откат тоже лучше продумать до публикации, а не после первой жалобы. Запишите, сколько времени команда дает себе на возврат предыдущей версии: 5 минут, 15 минут, час. Для LLM это не формальность. Иногда один лишний абзац в системной инструкции меняет поведение сильнее, чем правка в коде.

Метрики после релиза должны быть конкретными. Обычно хватает нескольких: доля успешных ответов, доля отказов, средняя длина ответа, число ручных эскалаций, цена одного сценария. Если вы работаете в чувствительной области, добавьте отдельный сигнал по нарушениям правил обработки данных.

Хорошая выкладка выглядит скучно. Это нормально. Скучный релиз почти всегда дешевле, чем срочный откат вечером.

Что делать дальше

Не стройте идеальный процесс с нуля. Для начала хватит простого контура, где команда видит изменения, может их обсудить и прогнать на одинаковых сценариях до релиза.

Если у вас уже есть продакшен-сценарии с LLM, относитесь к инструкциям так же, как к обычным изменениям в коде. Иначе одна маленькая правка быстро превращается в тихий регресс, который замечают только пользователи.

Начать можно с пяти шагов:

  • выбрать один репозиторий, где лежат все версии системных инструкций, тестовые наборы и заметки по изменениям;
  • ввести шаблон PR именно для инструкций: что поменяли, зачем, какие риски ждете, какие диалоги прогнали;
  • собрать первый набор эталонных диалогов, пусть даже небольшой, но из реальных случаев;
  • прогонять каждую правку хотя бы на 2-3 моделях, если у вас есть маршрутизация или запасной провайдер;
  • фиксировать результат в одном месте: что прошло, где модель ушла в отказ, где стала говорить лишнее.

Этого уже достаточно, чтобы версионирование инструкций и тестирование промптов стали рабочей практикой, а не красивой формулировкой. Не ждите полного набора из сотен тестов. Десять хороших диалогов с понятными ожиданиями часто дают больше пользы, чем большой архив без правил оценки.

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

Если вы сравниваете модели через RU LLM, можно сохранить те же SDK, код и промпты и просто прогонять одинаковые тесты через один совместимый эндпоинт. Это удобно, когда нужно быстро проверить поведение разных моделей без переписывания обвязки.

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

Часто задаваемые вопросы

Когда промпт уже надо вести как код?

Когда одна фраза влияет на деньги, риск, правила или SLA, храните инструкцию как код. Если текст правят несколько людей, бывают откаты или вы гоняете его на разных моделях, репозиторий и история версий уже нужны.

Что хранить рядом с системной инструкцией?

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

Как быстро заметить, что правка сломала рабочий сценарий?

Чаще всего это видно по формату. Если модель вместо чистого JSON добавила приветствие, переносы строк или лишние поля, парсер сломается, даже если человеку ответ покажется нормальным.

Как ревьюить системную инструкцию по шагам?

Сначала смотрите diff, а не весь текст подряд. Потом сравните старую и новую версию на одном наборе живых запросов и проверьте, что изменилось по делу: формат, тон, запреты, уточняющие вопросы и обязательные поля.

Какие тесты писать до релиза?

Минимум — тесты на формат, факты, запреты, шумный ввод и попытки снять ограничения. Проверяйте то, что код умеет ловить жестко: поля JSON, допустимые значения, стоп-слова, отказ от догадок и поведение на пустом или длинном вводе.

Зачем гонять один и тот же промпт на разных моделях?

Потому что одна и та же фраза держится на моделях по-разному. На одной модели инструкция стабильно сохраняет JSON, а на другой начинает болтать лишнее или слабее держит запрет на догадки; через единый API вроде RU LLM это удобно сравнить до релиза.

Как проверять промпт на персональные данные и 152-ФЗ?

Проверьте, что ассистент просит только те данные, которые реально нужны задаче. Добавьте тесты на телефон, почту, ФИО и номер карты, убедитесь, что модель не повторяет PII без нужды и не смешивает служебные правила с данными клиента.

Что делать, если после правки модель стала говорить слишком широко?

Не пытайтесь лечить это только фразой «будь дружелюбнее». Зафиксируйте формат ответа отдельно от тона: явно запретите лишний текст вне JSON и проверьте на тестах, что модель не добавляет пояснения до или после структуры.

Как безопасно выкатывать новую версию промпта?

Не лейте новую инструкцию сразу на весь трафик. Сначала пустите малую долю запросов, заранее запишите признаки регрессии, назначьте человека на релиз и договоритесь, за сколько минут команда вернет прошлую версию, если метрики просядут.

С чего начать, если у команды пока нет процесса?

Начните с малого: один репозиторий, шаблон PR и десять реальных диалогов для проверки. Этого уже хватит, чтобы каждая правка проходила через обсуждение, сравнение до и после и нормальный откат, а не через чат и память команды.