# Оптимизация конверсии ветки `new_booking` — предложение спринта **Дата:** 2026-04-27 **Автор предложения:** по результатам сравнения песочницы с действующим ботом-конкурентом «Александра» (NEXTBOT) на сайте ЛОР-клиники. **Куда встраивать:** между Спринтом 7 (мульти-RAG, часть A) и Спринтом 8 (мини-eval). Желательно до eval — чтобы baseline в `eval/reports/` уже отражал новую воронку, а не старую. --- ## 1. Контекст и причина На реальной воронке клиники (виджет на сайте) пациент пишет жалобу один раз и хочет либо получить полезный ответ, либо записаться. Каждая «промежуточная» реплика бота — потеря части лидов: пациент закрывает виджет, идёт искать на других сайтах, или просто перестаёт отвечать. Контрольный кейс — стандартный вход «Здравствуйте! очень сильно храплю, иногда закладывает уши». На том же кейсе: | Параметр | Конкурент (NEXTBOT/Александра) | Наш прототип | |---|---|---| | Количество реплик бота до запроса телефона | **2** | **3** | | Количество реплик пациента до запроса телефона | 2 (жалоба → «хочу») | 2 (жалоба → имя) | | Медицинская гипотеза в первом ответе | Да: перегородка / аденоиды / ринит | Нет | | Рекомендация специалиста в первом ответе | Да: ЛОР-врач | Нет | | Услуга и цена в первом ответе | Да: эндоскопия, 1000 ₽ | Нет | | Явный CTA на запись в первом ответе | Да: «Хотите, чтобы я помогла записаться?» | Нет | | Имя пациента | Спрашивается вместе с телефоном (один шаг) | Спрашивается отдельным шагом (`intro`) | Главные содержательные различия — два: 1. **Конкурент сразу решает задачу пациента**, а потом продаёт запись. Мы сначала собираем анкету, а суть жалобы откладываем. 2. **Конкурент укладывает обмен в 4 реплики** (бот → пациент → бот → пациент), мы — в 6+. Каждая лишняя итерация — это и доп. токены, и доп. drop-off. Нынешняя архитектура `new_booking` (intro → qualify → present → offer_time → book → close) полностью валидна для оператора, который ведёт пациента по записи. Проблема не в графе, а в **содержимом первого осмысленного ответа** и в **порядке сбора слотов `name` и `phone`**. ## 2. Цель спринта Сделать воронку `new_booking` сопоставимой с конкурентом по конверсии при сохранении нашей архитектуры (state machine, слоты, защитные условия, soft-insertion). Конкретно — переписать содержание шагов `intro` и `qualify`, поменять момент сбора имени, добавить в `qualify` обязательную «содержательную обвязку» (гипотеза + специалист + услуга + цена + CTA). ## 3. Целевые метрики (для ручной проверки и mini-eval) - **Сжатие воронки.** На контрольном кейсе «храп + заложенность ушей» количество реплик бота до момента, когда у нас в слотах `phone` ≠ null, должно быть **≤ 3** (сейчас ~5). - **Содержательность первого ответа.** На любую входную реплику с явной ЛОР-жалобой первый осмысленный ответ бота должен покрывать пять пунктов: - короткое сочувствие в одну фразу, - 2–3 возможные ЛОР-причины формулировкой «может быть связано с», - рекомендация специалиста, - упоминание профильной услуги и цены (эндоскопия / аудиограмма / приём — то, что есть в вики и применимо к жалобе), - бинарный CTA «записать вас на приём?». - **Сохранение защитных условий.** Все 8 ручных сценариев из блока H Спринта 6b продолжают проходить (запись ребёнка, листы ожидания, эскалация, routing_loop). Новая воронка не ломает существующие guard'ы. - **Сохранение тона.** Тёплое обращение, «вы», без диагнозов, без дозировок — все правила базового промпта `new_booking.md` остаются. ## 4. Что меняем — обзор Изменения локальные: четыре файла промптов и один JSON `intent_steps.allowed_next` (таблица переходов). Кода трогаем минимум. | Файл | Что меняем | |---|---| | `prompts/intents/new_booking/steps/intro.md` | Урезаем до одной короткой реплики приветствия + инициирующего вопроса. Снимаем требование собрать `name` именно здесь. | | `prompts/intents/new_booking/steps/qualify.md` | Добавляем обязательный шаблон «содержательного ответа на жалобу»: гипотеза → специалист → услуга/цена → CTA. Имя становится опциональным слотом. | | `prompts/intents/new_booking/steps/present.md` | Сокращаем до одной фразы-подтверждения (если вообще оставляем — обсуждаемо). | | `prompts/intents/new_booking/steps/book.md` | Запрос телефона + имени в одной реплике; имя становится частью контактного блока, а не отдельной анкетой. | | `intent_steps.allowed_next` (сид + миграция данных) | Разрешаем `intro → book` напрямую при коротком пути «есть жалоба + согласие» (см. блок A). | ## 5. Блоки задач ### Блок A. Сжатие воронки и перестановка сбора имени **Промпты:** - [ ] `intro.md` — переписать. Новая задача шага: поздороваться одной фразой и **сразу спросить, чем можем помочь**, не запрашивая имя. Текст приветствия — «Здравствуйте! Я виртуальный ассистент клиники. Расскажите, что вас беспокоит — подскажу, к какому специалисту записаться.» Слот `name` со шага `intro` снимаем (становится опциональным, заполняется на `qualify` или `book`). - [ ] `book.md` — переписать запрос контакта: «Чтобы администратор связался с вами и подтвердил время — напишите ваш номер телефона и как к вам обращаться». В одной реплике собираем `phone` и (опционально, если ещё не собрано) `name`. Если пациент назвал имя раньше — повторно не спрашиваем. - [ ] `qualify.md` — снять требование «не уходи дальше пока нет `name`», т.к. имя теперь не обязательно для перехода с `intro` и собирается естественно по ходу. **Таблица переходов (`intent_steps.allowed_next`):** - [ ] Расширить `allowed_next` шага `intro`: добавить переход `intro → qualify` (как сейчас) и **новый прямой `intro → present`** на случай, когда пациент уже первой репликой назвал и жалобу, и согласие записаться (редкий, но возможный случай). - [ ] Подтвердить, что `qualify → book` через `present` остаётся, а сам `present` мы либо радикально сокращаем (см. блок C), либо удаляем как самостоятельный шаг. **UI-чекпойнт A:** - [ ] В «Песочнице» прогнать кейс «Здравствуйте, болит ухо» — на первой реплике бот **не** спрашивает имя, а сразу даёт содержательный ответ (это уже эффект блока B). - [ ] В «Песочнице» прогнать кейс «Здравствуйте, я Сергей, болит ухо, хочу записаться» — слот `name=Сергей` подхватывается на `qualify`, на `book` имя повторно не спрашивается. - [ ] **Что проверяем глазами:** общее число реплик бота до запроса телефона — 3 или меньше. В timeline переходов нет «зависания» на `intro`. ### Блок B. Содержательный `qualify` — гипотеза, специалист, услуга, цена, CTA **Промпты:** - [ ] `qualify.md` — добавить обязательный шаблон ответа на первую реплику с жалобой. Шаблон в системном промпте шага описывается как пять пунктов в строгом порядке: 1. **Эмпатия** — одна фраза («Понимаю, это действительно может мешать»). 2. **Гипотеза** — 2–3 возможные ЛОР-причины формулировкой «может быть связано с» (без диагноза). Источник причин — RAG из подписанных документов ветки (Спринт 7), при отсутствии подходящего чанка — общая фраза без конкретики. 3. **Специалист** — рекомендация по профилю жалобы (ЛОР, сурдолог и т. д.). 4. **Услуга и цена** — упомянуть профильную услугу, которую врач может назначить на приёме, с ценой из вики, формулировкой «при необходимости назначит». Цена — отдельным предложением, чтобы не выглядело как «обязаны заплатить». 5. **CTA** — бинарный вопрос: «Записать вас на приём?» / «Хотите, я помогу записаться?». - [ ] В `qualify.md` зафиксировать: если пациент сразу ответил «да/хочу/записывайте» — переход `qualify → present` (или `qualify → book` напрямую, если решим в блоке C сокращать `present`). Слот `reason` фиксируем по тексту жалобы, `specialist` — по выводу гипотезы. - [ ] Сохранить все три «особые ситуации» (ребёнок, конкретный врач, первичная жалоба на слух) — они срабатывают как сейчас и не конфликтуют с новым шаблоном (просто добавляются в логику ответа). **RAG (зависимость от Спринта 7):** - [ ] Подписать на ветку `new_booking` документы вики, содержащие связки «жалоба → возможные причины → специалист → услуга → цена». Если на момент Спринта документов нет — завести задачу для Натальи: подготовить wiki-страницы по 5–7 типовым жалобам (храп, заложенность ушей, боль в горле, тугоухость, насморк, головокружение, шум в ушах) в формате «жалоба → 2–3 ЛОР-причины → специалист → процедура и цена». - [ ] Для жалоб, не покрытых вики, шаблон деградирует мягко: эмпатия + рекомендация ЛОР-врача + CTA, без гипотез и услуги. Это лучше, чем выдумывать. **UI-чекпойнт B:** - [ ] В «Песочнице» прогнать 5 контрольных кейсов: храп + уши, боль в горле, тугоухость, насморк > месяца, звон в ушах. На каждом — первый ответ бота должен содержать все 5 пунктов шаблона (или явно деградировать на 3, если документа в подписке нет). - [ ] В отладочной панели «Найденные фрагменты» — видно, какие чанки пошли в гипотезу/услугу. - [ ] **Что проверяем глазами:** на контрольном кейсе из раздела 1 наш ответ субъективно «не хуже» ответа Александры. Можно показать ответы рядом и сравнить. ### Блок C. Сокращение или удаление шага `present` **Решение требует обсуждения** перед началом работ: **Вариант 1 (минимальное вмешательство):** оставить `present` как есть, но переписать на одну короткую фразу-подтверждение («{name?}, оформляю запись к {specialist}, на приёме врач уделит внимание {reason}»). Сразу после — переход на `book` (запрос контакта), без отдельного шага `offer_time` для текущей итерации воронки. **Вариант 2 (агрессивный):** убрать `present` как самостоятельный шаг. Подтверждение плана зашить в первую фразу `book` («Записываю к {specialist}. Чтобы администратор связался — телефон и имя?»). Тогда воронка: `intro → qualify → book → close`, всего 4 шага вместо 6. **Аргументы за вариант 2:** ровно так делает конкурент (сразу после «Хочу» — запрос телефона). Каждый шаг — это +1 реплика бота, и `present` без нового действия от пациента ощущается как «вода». **Аргументы за вариант 1:** меньше риска сломать ручные сценарии 1–8 из Спринта 6b, проще откатить, шаг `present` остаётся точкой, куда возвращаемся при пересогласовании специалиста. Предлагаю **вариант 2** с явным фолбэком: если на ручных кейсах пациенты теряют ощущение, что их услышали, — возвращаем `present` обратно в граф. **Задачи (для варианта 2):** - [ ] `present.md` — пометить как deprecated в рамках спринта, не удалять файл (история). - [ ] `book.md` — добавить в начало шаблон одной фразы-подтверждения с использованием слотов `specialist` и `reason`. - [ ] Миграция `intent_steps`: убрать `present` из `allowed_next` шага `qualify`, добавить прямой переход `qualify → book`. Шаг `present` оставить в таблице как «висящий» на случай отката. - [ ] Обновить `prompts/intents/new_booking/transitions.yaml` (если есть) или соответствующий сид. **UI-чекпойнт C:** - [ ] Прогнать в «Песочнице» все 8 сценариев Спринта 6b. Сценарии 7 (ребёнок) и 8 (конкретный врач) — проверить отдельно, что guard'ы и waitlist-рукав не сломались. - [ ] **Что проверяем глазами:** базовый кейс из раздела 1 закрывается за 4 реплики бота вместо 6. Ручной сценарий 7 (ребёнок) — guard `require_legal_rep` всё ещё блокирует переход. ### Блок D. Тест-кейсы и регрессия **Подготовка eval-набора (заготовка для Спринта 8):** - [ ] В `eval/MANUAL_CASES.md` добавить раздел «Конверсионные кейсы» с 5 контрольными жалобами из блока B. Для каждого — ожидаемые слоты после первой реплики пациента, ожидаемая структура первого ответа бота (проверяется глазами по чек-листу из 5 пунктов), ожидаемое количество реплик до сбора `phone`. - [ ] Добавить негативный кейс: «Здравствуйте» (без жалобы) — бот должен задать открытый вопрос, не уйти в шаблон гипотезы (т. к. нет `reason`). - [ ] Добавить кейс с быстрой записью: «Запишите меня к ЛОРу на завтра» — бот должен пропустить блок гипотезы (жалоба не описана) и сразу подтвердить + спросить контакт. **Проверка отсутствия регрессии:** - [ ] Все 8 сценариев из блока H Спринта 6b проходят без правок ожиданий. - [ ] `eval/router_cases.csv` — accuracy не упала. Особое внимание: на кейсах с жалобами роутер по-прежнему возвращает `new_booking`, а не `medical_question` (наш sticky state machine это страхует, но всё равно проверяем). - [ ] Soft-insertion (Спринт 6b блок D) работает: «а сколько стоит приём?» внутри новой короткой воронки — отвечается на месте, шаг не сбрасывается. ## 6. Принятые компромиссы и риски - **Цены в первом ответе.** Чтобы упоминать цену, нужен корректный документ в RAG. Если документа нет — бот не выдумывает, и тогда первый ответ без цены и без гипотез — просто эмпатия + специалист + CTA. Это всё ещё лучше текущего «как к вам обращаться?», но без цены воронка слабее. Прогресс по этому риску напрямую зависит от качества вики (задача Натальи). - **Имя пациента может потеряться.** Если пациент не назвал имя ни на `intro` (где мы его теперь не спрашиваем), ни на `book`, в слот `name` останется пустым. Это нормально — `name` всё равно опциональное поле для вежливого обращения, а не идентификатор. На `book` спрашиваем явно, поэтому шанс потерять минимальный. - **Subjective trade-off: тон.** Перенос имени с `intro` на `book` ощущается «менее персонально» в первой реплике. Компенсируем содержательностью ответа — пациент видит, что бот понял его проблему, и это сильнее, чем «как к вам обращаться?». - **Конкурент тоже не идеален.** Александра упоминает цену на эндоскопию, но не предлагает её альтернативы и не уточняет жалобу. Это окей для нашего MVP, но в бэклог стоит внести задачу «варьировать услугу по типу жалобы» (для тугоухости — аудиограмма, не эндоскопия). ## 7. Критерий готовности спринта - [ ] На контрольном кейсе раздела 1 наш бот в «Песочнице» отвечает по 5-пунктовому шаблону, и весь обмен до запроса телефона укладывается в 3 реплики бота. - [ ] Все 8 ручных сценариев из блока H Спринта 6b проходят без правок ожиданий. - [ ] 5 контрольных конверсионных кейсов из блока D добавлены в `eval/MANUAL_CASES.md` и прогнаны вручную; результаты — в `eval/MANUAL_REPORT.md`. - [ ] Промпты `intro.md`, `qualify.md`, `book.md` обновлены, изменения видны во вкладке «Шаги» (Спринт 6a, блок A) — оператор может прочитать без выгрузки кода. - [ ] Если выбран вариант 2 блока C — миграция таблицы переходов выполнена, `present` помечен как deprecated. ## 8. Что НЕ делаем в этом спринте - Не трогаем `_router.md` — изменения локальные внутри ветки. - Не делаем confidence threshold для RAG (это в бэклоге, нужно после прогона eval). - Не пишем CRM-интеграцию (мок-инструменты `crm.create_booking` — отдельный пункт бэклога). - Не трогаем шаги `offer_time` и `close` — они внутренние, конкурент их вообще не показывает в первой воронке. Их роль (выбор времени из календаря и финал) станет актуальна, когда подключим реальный календарь в Спринте 9 / при подключении канала. ## 9. Дальнейшие идеи (на потом) - **Вариация услуги по жалобе.** Сейчас предлагаем стандартную эндоскопию. После наполнения вики — научить ветку выбирать профильную процедуру по `reason` (тугоухость → аудиограмма, насморк > 4 недель → риноскопия и т. д.). Это требует отдельного слота `suggested_procedure` и подсказки в промпте `qualify`. - **A/B тестирование двух версий первого ответа.** После Спринта 8 (eval) запустить две версии `qualify` параллельно и сравнить, какая даёт лучшее покрытие 5-пунктового шаблона на ручных кейсах. - **Постпродажа на `close`.** После сбора телефона — короткое «также можем напомнить за день до приёма SMS» / «оставить второй контакт для родственника». Конкурент этого не делает; это не догоняние, а попытка обогнать. Завести в идеи только после стабилизации основной воронки. --- **Зависимости:** - Спринт 6a (вкладка «Шаги», структурированный выход) — должен быть закрыт **до** старта этого спринта, иначе править промпты шагов через UI не получится. - Спринт 7 (мульти-RAG) — желателен закрытым, чтобы цены и услуги попадали в `qualify` через подписки документов. При незакрытом 7 спринт делаем на устаревшем механизме «вся коллекция», результат будет хуже. **Оценка трудозатрат (в условных единицах):** - Блок A (промпты + переходы): 1 день. - Блок B (содержательный qualify + RAG-увязка): 1.5 дня. Зависит от готовности вики. - Блок C (вариант 2): 0.5 дня. - Блок D (eval-кейсы): 1 день. - Итого: ~3–4 дня инженерного времени + ~2 дня Натальи на вики (параллельно).