Девушка пересылает боту переписку с бойфрендом. Модель видит сигналы опасности (эмоциональное насилие, изоляция) и отвечает номером телефона доверия. Заботливо. Ответственно. Одна проблема: это детская горячая линия. Модель галлюцинировала контакт кризисной помощи.
В промпте написано «НЕ придумывай контактные данные». Не помогает. Желание быть полезной в модели сильнее любой инструкции. Это не проблема промптинга. Это проблема архитектуры.
Ловушка одного прохода
Типичная архитектура LLM-продукта: пользовательский ввод поступает в модель, выход модели уходит пользователю. Если нужно одновременно проанализировать входные данные и выдать результат в определённом голосе, тоне или формате, обе задачи кладутся в один промпт.
Здесь всё и ломается.
Анализ требует точности и структуры. Голос требует свободы и эмпатии. Это конфликтующие задачи, конкурирующие за один и тот же бюджет токенов. Результат: модель вплетает галлюцинации в убедительный текст, и вы не можете программно отличить выдуманное от настоящего.

Первый рефлекс: подключить модель помощнее. Reasoning-модели, больше токенов. Я проверяла на своих кейсах. Дорогие модели галлюцинируют реже, но и цена за них немаленькая. Недорогие думающие модели все равно галлюцинируют, если промпт требует сложного поведения. К тому же, на сложных кейсах с несколькими участниками и многослойным подтекстом даже лучшие модели путают персонажей, неправильно приписывают намерения и додумывают ситуацию. Проблема не в мощности модели. Проблема в том, что один вызов делает две принципиально разные работы.
Это обобщается далеко за пределы чат-ботов. Любой LLM-продукт, где модель одновременно решает что сказать и как это сказать, касаясь данных, которым пользователь будет доверять, имеет эту уязвимость. Цены, юридические ссылки, дозировки лекарств, адреса, номера телефонов. Если модель может это придумать, а у пользователя нет причин сомневаться, вы сидите на пороховой бочке.
Паттерн: Triage-and-Voice
Решение архитектурное: разделить работу с LLM на два прохода, с бэкендом как слоем принятия решений между ними.
Проход 1 — Triage. Модель делает структурный анализ. Никакого голоса, никакой личности, никакого текста для пользователя. Чистое извлечение сигнала в машиночитаемый формат (JSON). Что происходит? Кто участники? Какие их цели? Какие риски? В какую категорию это попадает?
Backend gate. Код бэкенда смотрит на результат triage и принимает решение о маршрутизации. Обнаружена кризисная ситуация → подставить верифицированные данные, переключить на кризисный промпт. Сработало бизнес-правило → модифицировать то, что получит следующий проход. Именно здесь рождается надёжность: в детерминированном коде после вероятностной генерации.
Проход 2 — Voice. Модель получает результат triage, отфильтрованный и обогащённый бэкендом, и создаёт ответ для пользователя в нужном голосе, тоне и формате. Она не анализирует. Она не решает. Она формулирует.

Модель не касается критичных данных напрямую. Вот что возвращает первый проход на обычном кейсе:
{ "mood": "anxiety", "is_crisis": false, "crisis_type": null // 20+ fields: participants, motives, quotes, children }
А вот — кризисный:
{ "mood": "red_flag", "is_crisis": true, "crisis_type": "violence" // ... }
Бэкенд действует по флагу:
if (triage.IsCrisis) { var contacts = await db.GetVerifiedCrisisContacts(triage.CrisisType); prompt = crisisPromptTemplate.WithContacts(contacts); }
Модель во втором проходе не придумывает номер телефона — её задача озвучить серьёзность ситуации и направить к контакту, который подставил бэкенд. Десятки прогонов кризисных кейсов на DeepSeek V3.2, ни одной галлюцинации контактных данных.
Почему это работает: три свойства
Разделение ответственности. Модель-аналитик не тратит токены на стиль. Голосовая модель не тратит токены на анализ. Каждый проход делает одно. Качество растёт по обеим осям. Я измеряла это на 40 eval-кейсах.
Кешируемый анализ. Мой продукт — Telegram-бот для анализа переписок через линзы разных персонажей. Пользователь хочет увидеть свою ситуацию с разных сторон. Triage выполняется один раз. При повторном запросе перезапускается только Voice. Дешевле, быстрее, и пользователь получает вторую перспективу за секунды вместо ожидания полного повторного анализа. Один проход с голосом персонажа занимал 50–90 секунд. С разделением: первый ответ 30–45 секунд, последующие 15–20.
Безопасность через backend gate. Ключевой инсайт: между двумя проходами сидит детерминированный код. Не ещё один промпт. Не guardrail модель. Код, который вы контролируете, который вы можете тестировать, который ведёт себя одинаково каждый раз. Контакты кризисных служб берутся из базы данных. Задача модели — знать когда. Задача бэкенда — знать что.
Где применим Triage-and-Voice
Это паттерн не только для чат-ботов. Это архитектурный паттерн LLM-продуктов. Он применим когда:
Модель должна анализировать ввод И формировать пользовательский вывод
Вывод содержит данные, которым пользователь будет доверять
Нужно обрабатывать edge cases (кризис, юридические, финансовые) иначе, чем happy path
Нужно менять подачу без повторного запуска анализа
Нужен детерминированный чекпоинт между «что модель думает» и «что видит пользователь»
Принцип: модель ставит флаг, код принимает решение, модель говорит. Каждый раз, когда вы ловите себя на том, что пишете в промпте «НЕ придумывай [критичные данные]», это сигнал разделить процессинг ввода.
Неудобная правда о наивной архитектуре LLM-продуктов
Если ваш LLM-продукт делает один вызов, в котором модель одновременно рассуждает о входных данных и формирует финальный ответ, вы выполняете анализ и презентацию как одну атомарную операцию без чекпоинта между ними.
Вы не можете валидировать то, что модель «решила», до того как пользователь это увидит. Не можете кешировать аналитическую работу отдельно от презентации. Не можете маршрутизировать edge cases в другую логику обработки. Не можете подставить верифицированные данные в нужный момент.
Вы доверяете вероятностной системе быть правой именно в том, что важнее всего.
Triage-and-Voice — это не про добавление сложности. Это про добавление точки контроля между тем, что модель думает, и тем, что получает пользователь. Один дополнительный LLM-вызов. Один backend gate. Разница между «модель так сказала» и «мы это проверили».
Паттерн Triage-and-Voice я вывела из реальных инцидентов в проекте «Между строк» (три Telegram-бота для анализа переписок). В мае буду рассказывать про другие технические решения для LLM-продуктов на митапе SPB DotNet.
