Пятничный деплой завершился штатно. Мы выкатили минорное обновление внутренней системы аналитики. Никаких изменений в бизнес-логике, только правка одной строчки в базе данных: продакт-менеджер попросил сделать ответы бота «чуть более вежливыми». В системный промпт добавили безобидную фразу: «Всегда начинай ответ с дружелюбного приветствия».
Через двадцать минут посыпались алерты. Парсер JSON на бэкенде захлебнулся ошибками валидации. Оказалось, что языковая модель восприняла инструкцию буквально и начала генерировать ответы вида: «Привет! Вот ваш ответ: {"status": "ok", ..}». Сломалась вся интеграция, зависли десятки пользовательских сессий.
Этот инцидент стал для нашей команды точкой невозврата. Мы осознали суровую реальность: системный промпт — это больше не конфигурационный параметр и не текстовое описание. Это полноценный исходный код, который компилируется в реальном времени внутри черного ящика нейросети. И относиться к нему нужно с той же инженерной паранойей, с которой мы относимся к ядру операционной системы.
Иллюзия контроля и хрупкость абстракций
Традиционное программирование держится на строгой детерминированности. Вызов функции sort всегда сортирует массив. Синтаксическое дерево строится по жестким правилам. Если разработчик забыл точку с запятой, компилятор ударит его по рукам на этапе сборки.
С большими языковыми моделями (LLM) мы потеряли эту роскошь. Естественный язык оказался самым нестабильным интерфейсом программирования из всех существующих. Вы пишете инструкцию на русском или английском, ожидая однозначного исполнения, но модель интерпретирует семантику слов через призму своих весов. Замена слова «проанализируй» на «оцени» может радикально изменить дерево принятия решений внутри трансформера.
Мы столкнулись с феноменом хрупкости промптов. Разработчики тратили часы на подбор идеальных формулировок, добивались стабильного вывода, а затем OpenAI или Anthropic выкатывали минорное обновление весов модели, и вся конструкция рассыпалась. Промпт, который работал вчера, сегодня начинал галлюцинировать или игнорировать жесткие ограничения.
Стало очевидно, что хранить такие критически важные куски логики в базе данных или менять их через админку «на лету» — это архитектурное самоубийство. Нам требовался жесткий контроль версий.
Git как единственный источник правды
Мы приняли радикальное решение: полностью выпилить редактирование промптов из пользовательского интерфейса админ-панели. Все системные промпты переехали в репозиторий с кодом бэкенда, в выделенную директорию prompts/.
Каждый промпт теперь представляет собой обычный Markdown-файл. Изменение промпта проходит через стандартный инженерный процесс: создание ветки, коммит, Pull Request и обязательное ревью кода.
Когда разработчик предлагает изменить системный промпт, в Pull Request мы видим конкретный diff. Мы обсуждаем не абстрактные идеи, а конкретные строки. Зачем здесь добавлено слово «строго»? Почему мы переместили описание формата вывода в самый конец файла? Из-за эффекта «lost in the middle» модели лучше запоминают инструкции, находящиеся в начале и в конце контекста, поэтому критические правила форматирования должны завершать файл.
Версионирование в Git дало нам возможность мгновенного отката. Если новая версия промпта начинает вести себя неадекватно в продакшене, мы делаем git revert и деплоим предыдущую версию за пару минут. Никаких судорожных попыток вспомнить текст до правок маркетолога.
Более того, привязка промптов к коммитам позволила внедрить автоматическое тестирование (evals). CI/CD пайплайн прогоняет измененный промпт через сотню тестовых сценариев и замеряет метрики качества ответов. Если accuracy падает ниже заданного порога, Pull Request блокируется автоматически.
Шаблонизация и динамический контекст
Статичный текст решает лишь половину проблемы. В реальных приложениях системный промпт редко бывает абсолютно неизменным. Нам нужно передавать в него текущую дату, ID пользователя, схему базы данных или специфичные бизнес-правила, зависящие от тарифа клиента.
Сначала мы пытались использовать простую конкатенацию строк, но быстро поняли, что это путь к хаосу. Код бэкенда обрастал уродливыми конструкциями сборки текста. Читать и ревьюить такой код было невозможно.
Мы внедрили полноценный шаблонизатор для наших Markdown-промптов. Теперь системный промпт выглядит как классический view-компонент:
Ты — финансовый аналитик компании.
Текущая дата: {{ current_date }}
Доступные таблицы для SQL-запросов:
{% for table in allowed_tables %}
- {{ table.name }}: {{ table.description }}
{% endfor %}
Этот подход четко разделил ответственность. Разработчики бэкенда готовят DTO (Data Transfer Object) с переменными, а инженеры проектируют логику внутри Markdown-файла. При ревью мы видим только изменения в логике самого промпта.
Интеграция через RouterAPI
Когда промпт живет в репозитории как текстовый шаблон, возникает вопрос его доставки до модели. Индустрия пришла к удивительно простому стандарту. Системный промпт не требует сложных API-вызовов или отдельного транспортного слоя. Он передается как часть единого массива сообщений.
В нашей инфраструктуре мы используем RouterAPI — единый шлюз для доступа к различным LLM. RouterAPI полностью поддерживает стандартный формат OpenAI, что делает интеграцию наших Git-версионированных промптов тривиальной задачей.
При формировании запроса бэкенд рендерит содержимое Markdown-файла, подставляет переменные и помещает результат нулевым элементом в массив messages.
{
"model": "gpt-4o",
"messages": [
{
"role": "system",
"content": "Ты — строгий валидатор SQL-запросов. Отвечай только валидным JSON.."
},
{
"role": "user",
"content": "SELECT * FROM users WHERE age > 20"
}
],
"temperature": 0.1
}
Этот подход инкапсулирует весь контекст сессии. Роль system дает модели четкий сигнал: это не пользовательский ввод, который может содержать попытки джейлбрейка (prompt injection), это базовые правила игры, установленные разработчиком.
RouterAPI прозрачно проксирует этот массив до конечного провайдера. Нам не нужно писать адаптеры для разных API. Мы берем оттестированный системный промпт и отправляем его первым сообщением.
Новая инженерная дисциплина
Мы перестали относиться к нейросетям как к волшебным оракулам. Работа с LLM превратилась в рутинную инженерную задачу. Системный промпт стал новым языком программирования — высокоуровневым, вероятностным, местами капризным, но поддающимся контролю.
Перенос промптов в Git, внедрение код-ревью для текстовых инструкций, использование шаблонизаторов и стандартизация доставки через массив messages в RouterAPI позволили нам стабилизировать систему. Мы больше не боимся деплоев. Мы знаем, что если модель сойдет с ума, у нас есть четкий git blame, понятный diff и кнопка отката. Текст окончательно стал исходным кодом.