AI-готовые боты: как спроектировать сценарий и удержать качество без бесконечных промптов
Покажем, как построить цепочку обработки запросов и проверки качества: правила, контекст, эскалация и минимизация галлюцинаций. Отлично подойдёт тем, кто хочет практичный бот для бизнеса.
Содержание
AI-готовые боты: как спроектировать сценарий и удержать качество без бесконечных промптов
AI-готовые боты (вроде “собери бота за час”, “подключи модель и работай”) обычно хорошо решают первую задачу: быстро дать пользователю ответ. Но в бизнес-кейсе вскоре проявляется другая реальность — качество деградирует не из‑за “плохой модели”, а из‑за отсутствия проектирования. Бот начинает отвечать «как умеет», а не «как надо»: непоследовательно трактует запросы, теряет контекст, выдумывает условия, путает статусы, не следует политике компании и не понимает, когда нужно эскалировать на человека.
Проблема решается не бесконечными промптами, а архитектурой сценария: цепочкой обработки запросов, правилами и валидацией, контролем контекста, маршрутизацией и эскалацией. Ниже разберём практический подход, который можно внедрить почти в любой чат-ассистент: от “простого” бота в мессенджере до более сложной системы с инструментами, базой знаний и модерацией.
Почему бесконечные промпты не спасают
Когда разработчик видит проблему качества, первое искушение — «добавим ещё инструкций в промпт». Но это редко работает по двум причинам:
-
Инструкции конкурируют друг с другом. Чем больше “политик” и “правил”, тем сильнее риск внутреннего конфликта: модель не знает, что важнее, и начинает выбирать то, что выглядит убедительно в моменте.
-
Промпт не является системой контроля. Он описывает поведение, но не гарантирует соблюдение ограничений. Если модель ошибается, промпт не остановит её — он лишь пытается убедить.
-
Контекст разрастается и становится шумом. Бот начинает тащить историю диалога в модель без нормализации. В результате модель может “утонуть” в прошлых данных и пропустить важные факты (например, идентификатор заказа, регион, ограничения по возврату).
-
Отсутствует механизм проверки результата. Вы можете попросить «не галлюцинировать», но если нет валидации (формальные проверки, сверка с источниками, шаблоны ответов, правила), ошибки остаются незамеченными.
С точки зрения инженерии, промпт — это лишь один из компонентов. Качество удерживается не текстом инструкций, а контуром обратной связи: проверка → исправление → эскалация.
Базовая архитектура “AI-готового” бота: цепочка обработки запроса
Ниже — схема, которую удобно взять за основу. Её логика проста: модель делает черновик, а система проверяет и корректирует, пока результат не станет безопасным для бизнеса.
Основные компоненты
1) Входной слой (pre-processing)
- Нормализация текста (очистка, исправление опечаток, выделение сущностей).
- Явное извлечение структуры: тема запроса, язык, намерение (intent), ключевые параметры (ID, даты, город).
2) Контекст и профилирование (context policy)
- Определение, какой контекст нужен: личные данные пользователя, правила компании, релевантные документы.
- Фильтрация: не тащим в модель лишнюю историю, храним краткое “резюме” состояния с контролем качества.
3) Маршрутизация (routing)
- Куда направить запрос: в базу знаний, в инструмент “проверка статуса заказа”, в подбор товара, к оператору.
- Выбор подхода: “ответить прямо”, “нужны уточнения”, “эскалировать”.
4) Генерация (draft)
- Модель формирует ответ на основе:
- extracted intent/slots,
- релевантного контекста,
- шаблона структуры ответа (если применимо).
5) Проверка и валидация (quality gates)
- Техническая проверка: корректный формат, отсутствие “мусорного” текста, соблюдение лимитов.
- Семантическая проверка: соответствие фактам из источников.
- Политики: юридические ограничения, запрет на персональные данные, требования к формулировкам.
6) Редактура и финализация (post-processing)
- Сжатие, уточнение, добавление недостающих полей.
- Если валидация не пройдена — повторный проход или эскалация.
7) Наблюдаемость (observability)
- Логи намерений, версий контекста, результатов проверок.
- Метрики: доля эскалаций, доля исправлений, “accept rate” (сколько ответов прошли без правок), частота галлюцинаций (по разметке).
Принцип: “модель — это мотор, а не тормоз”
Хороший бот всегда имеет минимум один “тормоз”:
- либо детерминированные правила,
- либо внешний инструмент/источник правды,
- либо отдельный слой проверки.
Проектирование сценария как конечного автомата (с состояниями)
Чтобы сценарий не превращался в набор промптов, полезно формализовать его.
Определите состояния
Примеры состояний для бизнеса:
GREETING— приветствие и первичная классификация запроса.COLLECT_SLOTS— сбор параметров (например, номер заказа).ANSWER_WITH_KB— ответ по базе знаний.ANSWER_WITH_TOOL— ответ по результату инструмента (статус заказа, доставка).CLARIFY— уточнение из-за неоднозначности.ESCALATE— передача оператору/колл-центру.NO_MATCH— запрос не покрывается политикой/каталогом.
Состояние должно жить не в промпт-инструкциях, а в явной модели данных: в базе/в объекте session.
Храните “снимок состояния”, а не всю историю диалога
Вместо того чтобы каждый раз кормить модель весь диалог, храните:
intent(классификация),slots(извлечённые сущности),kb_sources(какие документы/статьи подтянули),decision_trace(что выбрано: ответ напрямую/уточнение/эскалация),- краткое
user_context_summary(1–3 предложения максимум).
Это снижает шум и стабилизирует поведение.
Правила: как удерживать качество без “надувания” промпта
Правила нужно разделять по уровню абстракции.
1) Жёсткие правила (hard constraints)
Примеры:
- «Нельзя запрашивать паспортные данные в чате».
- «Если клиент просит возврат, нужно уточнить номер заказа и дату покупки».
- «Если нет идентификатора заказа — нельзя обещать срок».
Такие правила реализуются детерминированно: через код, regex/NER, функции валидации.
2) Мягкие правила (soft constraints)
Примеры:
- «Отвечай кратко, в пределах N предложений».
- «Используй тон бренда: нейтральный, без канцелярита».
Их можно задавать в промпте, но они не должны заменять валидацию.
3) Политики источников (source policy)
Это критично против галлюцинаций: модель должна знать, откуда она может брать факты.
Практика:
- В ответе разрешены факты только из
kb_sources/инструментальных ответов. - Если ответ требует факта, а источника нет — бот уточняет или эскалирует.
Контекст: как правильно “приклеить” знания к ответу
Одна из главных причин ошибок — неверно подобранный или невалидный контекст. Контекст нужно проектировать.
Шаг 1. Подтягивайте контекст точечно
Схема:
- Классифицируем intent.
- Извлекаем ключевые параметры.
- Ищем документы/статьи/регламенты по параметрам.
- Отдаём модели только top‑K релевантных фрагментов.
Важно: храните не просто документ, а фрагмент с метаданными (раздел, дата обновления, доверие).
Шаг 2. В контексте явно помечайте “цитируемость”
Например, добавьте в структуру фрагмента поле:
source_idconfidencesnippet_text
Тогда верификатор проще проверит: утверждение должно быть поддержано snippet_text.
Шаг 3. Устраивайте контекстный бюджет
Если вы даёте модели слишком много текста, она теряет фокус. Для бизнес-ботов обычно работает правило:
- 1–2 кратких фрагмента по теме + 1 политика/шаблон ответа (или 0, если ответ стандартный).
- Остальное — по необходимости уточнений.
Эскалация: где бот должен остановиться
Эскалация — не “красивая кнопка”, а инженерная обязанность.
Модель эскалации на практике
Вводим три триггера:
- Недостаток данных
- Не удалось извлечь ключевой слот (например, номер заказа).
- Пользователь запросил действие, требующее параметра.
- Низкая уверенность
- Классификатор intent выдал низкий score.
- Retrieval вернул нерелевантные документы (или confidence низкая).
- Несовместимость с политикой
- Запрос выходит за рамки регламента (например, юридические обещания без условий).
- Нужна “человеческая экспертиза”.
Формула: эскалация лучше галлюцинации
Пусть бот отвечает:
- «Я не могу подтвердить… в текущих данных. Давайте уточним X». или
- «С вопросом по возврату в вашем случае лучше подключить оператора. Я передам контекст».
Самое важное — эскалация должна сохранять состояние и контекст, чтобы оператор не начинал с нуля.
Проверка качества: “quality gates” вместо веры в промпт
Чтобы остановить галлюцинации, нужен контур проверки. Его можно строить в несколько уровней.
Уровень A: формальные проверки (deterministic)
Примеры:
- Ответ содержит требуемые поля (номер заказа, дата, ссылка).
- Нет запретных токенов/шаблонов (например, “паспорт”, “карта”, “ПИН”).
- Формат ответа соответствует шаблону.
Уровень B: сверка с источниками (grounding checks)
Если у вас retrieval/KB:
- Проверяйте, что ключевые утверждения есть в
kb_sources. - Если нет — либо перепишите ответ, либо эскалируйте.
Уровень C: семантическая самопроверка (LLM-judge)
Можно использовать модель как “судью”, но аккуратно:
- Судья не должен иметь больше контекста, чем рабочая модель (иначе он “подскажет”).
- Судья должен оценивать конкретные пункты: “есть ли противоречия”, “подтверждено ли источниками”, “выполнены ли правила”.
Критично: judge — это не истина, но в контуре с логированием он отлично снижает риск.
Минимизация галлюцинаций: конкретные техники
Галлюцинации возникают не только при отсутствии знаний. Они появляются, когда модель:
- заполняет пробелы собственными “разумными догадками”;
- смешивает разные источники;
- неправильно интерпретирует намерение.
Вот практические меры.
1) Извлекайте “слоты” и требуйте их прежде чем обещать
Если запрос: “Где мой заказ?”, то без order_id ответ должен быть уточняющим.
Типичный паттерн:
- Сначала извлечь слот.
- Если слот отсутствует —
COLLECT_SLOTS. - Только после
order_id—ANSWER_WITH_TOOL.
2) Разделяйте режимы “ответить” и “обсудить”
Иногда пользователю нужно объяснение, а иногда — точное действие. Для бизнеса полезно:
- в режиме “точное действие” использовать только инструменты/источники;
- в режиме “объяснение” можно давать обобщения, но помечать границы (“обычно”, “как правило”).
3) Давайте модели шаблон ответа
Шаблон снижает вариативность и упрощает проверку.
Пример структуры:
Короткий ответЧто нужно от вас (если необходимо)Ссылка на правило/документ (если есть)Следующий шаг
4) “Анти-предсказание” через отказ
В промпт можно включить правило:
- «Если нет подтверждения в источниках — не придумывай и предложи уточнение».
Но это работает только вместе с quality gates.
Практический пример: end-to-end пайплайн на псевдокоде
Ниже — пример логики (упрощённо), как это может выглядеть в коде. Он не привязан к конкретному фреймворку, но отражает подход.
Скелет обработки запроса
def handle_message(session, user_text):
# 1) Pre-processing + intent/slots
parsed = parse_intent_and_slots(user_text)
session.intent = parsed.intent
session.slots = merge_slots(session.slots, parsed.slots)
# 2) Routing decision
if requires_order_id(session.intent) and not session.slots.get("order_id"):
session.state = "COLLECT_SLOTS"
return collect_order_id_response(session)
# 3) Retrieval / context
sources = []
if session.state in {"ANSWER_WITH_KB", "GREETING"} or needs_kb(session.intent):
sources = retrieve_kb_fragments(session.intent, session.slots)
if not sources or low_confidence(sources):
session.state = "ESCALATE"
return escalate_response(session, reason="No reliable KB match")
# 4) Tool usage (optional)
tool_result = None
if needs_tool(session.intent):
tool_result = call_tool(session.intent, session.slots)
# 5) Generation (draft)
draft = generate_answer(
intent=session.intent,
slots=session.slots,
kb_sources=sources,
tool_result=tool_result,
answer_template=ANSWER_TEMPLATE,
)
# 6) Quality gates
check = validate_answer(
draft=draft,
sources=sources,
tool_result=tool_result,
policies=COMPANY_POLICIES,
schema=ANSWER_SCHEMA
)
if not check.passed:
# Option A: retry with stricter instructions
if check.can_retry:
return generate_with_constraints_and_recheck(session, check, draft)
# Option B: escalate
session.state = "ESCALATE"
return escalate_response(session, reason=check.reason)
# 7) Post-process
final = post_process(draft)
log_quality_metrics(session, check)
return final
Ключевой момент: “судьба ответа” определяется не только генерацией, а validate_answer.
Валидация ответа: что именно проверять
Ниже пример того, как можно реализовать некоторые проверки.
Проверка запретных запросов/данных
BANNED_PATTERNS = [
r"\bпин\b", r"\bпаспорт\b", r"\bномер\s+карты\b"
]
def policy_check_forbidden_data(text):
lowered = text.lower()
for pat in BANNED_PATTERNS:
if re.search(pat, lowered):
return False, f"Forbidden data pattern: {pat}"
return True, "ok"
Проверка наличия обязательных полей (JSON-слой или разметка)
Если вы просите модель возвращать структурированный ответ (например, JSON), вы можете валидировать схему.
from jsonschema import validate, ValidationError
ANSWER_SCHEMA = {
"type": "object",
"properties": {
"short_answer": {"type": "string"},
"next_step": {"type": "string"},
"need_clarification": {"type": "boolean"},
"order_id_echoed": {"type": ["string", "null"]}
},
"required": ["short_answer", "next_step", "need_clarification", "order_id_echoed"]
}
def schema_check(answer_json):
try:
validate(instance=answer_json, schema=ANSWER_SCHEMA)
return True, "ok"
except ValidationError as e:
return False, f"Schema validation error: {e}"
Даже если чат-UI покажет только текст, внутренний формат помогает удерживать качество.
Сверка фактов со “сниппетами”
В простом варианте можно требовать, чтобы конкретные ключевые утверждения встречались в источниках. В более зрелом — делать entailment/semantic проверку.
Пример “на уровне маркеров”:
def grounding_check(claims, sources):
source_text = "\n".join(s["snippet_text"] for s in sources)
for c in claims:
if c.lower() not in source_text.lower():
return False, f"Ungrounded claim: {c}"
return True, "grounded"
Как спроектировать “хороший сценарий” для бизнеса
Сценарий — это не дерево вариантов в стиле “если сказал X, то Y”. Для AI-ботов сценарий должен быть системой решений.
Шаг 1. Выделите 5–15 основных интентов
Типовые для e-commerce/услуг:
- статус заказа
- доставка и сроки
- возврат/обмен
- наличие товара
- оплата/счета
- гарантия
- настройка аккаунта
- юридические/политики
Не распыляйтесь. Пока intents не стабильны, любая генерация будет страдать.
Комментарии
Пока нет комментариев