Воронка сжата с 6 шагов до 4: intro → qualify → book → close.
Спецификация: docs/OPTIMIZATION_CONVERSION_v1.md.
Цель: сравнимая с конкурентом (NEXTBOT/Александра) конверсия — ≤3 реплик
бота до запроса телефона, содержательный ответ на жалобу в первом
осмысленном сообщении.
Промпты шагов:
- intro.md — переписан. Приветствие + открытый вопрос «что беспокоит?».
Имя НЕ спрашиваем (слот name со шага снят), оно собирается на book
вместе с телефоном. Если пациент сразу написал жалобу — не зацикливаемся,
переходим в qualify.
- qualify.md — переписан. Обязательный 5-пунктовый шаблон ответа на жалобу:
эмпатия (одна фраза) → 2-3 ЛОР-гипотезы из RAG-выдержек («может быть
связано с») → специалист → услуга/цена («при необходимости назначит») →
бинарный CTA «записать?». Если в выдержках нет гипотез/цен — пункт
пропускается, не сочиняем. Если жалоба не описана (пациент сразу
«хочу записаться к ЛОРу») — пропускаем гипотезу/услугу, оставляем
эмпатию-формальность + специалист + CTA.
Три особые ситуации сохранены: ребёнок (require_legal_rep), конкретный
врач (waitlist_flag), первичная жалоба на слух (needs_surgologist_first).
- book.md — переписан. Одной репликой: подтверждение плана с
использованием {specialist}/{reason} + запрос телефона + имени (если
ещё не было в истории). При is_child=true — обращение к родителю,
legal_rep_phone используется, если уже собран.
- present.md — DEPRECATED. Файл оставлен в репо на случай отката
(вариант 1 спецификации). Внутри — заглушка «попал по ошибке —
выходи на book».
- close.md и offer_time.md не тронуты (offer_time станет актуален с
реальным календарём).
allowed_next в SEED_INTENT_STEPS:
- intro: [intro, qualify] (без изменений)
- qualify: [qualify, book] (раньше: [qualify, present])
- present: [book] (изоляция; раньше: [present, qualify, offer_time])
- offer_time: [offer_time, book] (deprecated, без изменений)
- book: [book, qualify, close] (раньше: [book, qualify, offer_time, close])
- close: [close] (без изменений)
migrate_new_booking_allowed_next_v2(session) — одноразовая миграция в
services/intent_step_service.py. При старте для каждого шага
new_booking сравнивает текущий allowed_next_json с дореформенным
значением (_PRE_SPRINT_7_6_ALLOWED_NEXT). Если совпадает — обновляет
на новое из SEED. Если оператор правил вручную — пропускает,
warning в лог. Идемпотентна (на повторных запусках ничего не делает).
Подключена в main.py lifespan после ensure_seed_guards.
Защитное условие require_legal_rep на qualify сохранено. Теперь блокирует
переход qualify → book (раньше qualify → present). Логика та же:
при is_child=true и пустых legal_rep_name/legal_rep_phone валидатор
отклоняет переход.
eval/MANUAL_CASES.md — markdown-чеклист для ручных прогонов:
- §A: 5 конверсионных кейсов (храп+уши, боль в горле, тугоухость,
насморк >месяца, звон в ушах) с чеклистом 5 пунктов на первый ответ
и проверкой ≤3 реплик до телефона.
- §B: регрессия 8 ручных сценариев из блока H Спринта 6b со ссылками
на docs/examples/*_v2.md.
SPRINTS.md: Спринт 7.6 → ✅ Закрыт по коду. Применение промптов в БД
и ручная регрессия — за оператором (через UI «Настройки → Шаги»
для каждого из 4 шагов new_booking).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
8.8 KiB
Ручные кейсы для регрессии
Чеклист для прогонов в Песочнице. Не автоматизирован — это markdown-чеклист, по которому оператор/разработчик прогоняет сценарии руками. Полная подсистема прогона (eval/run.py) — Спринт 8.
Раздел A — конверсионные кейсы Спринта 7.6 (новые). Раздел B — регрессия 8 ручных сценариев из блока H Спринта 6b (должны проходить как раньше).
A · Конверсионные кейсы (Спринт 7.6)
Все 5 кейсов — про оптимизацию воронки new_booking до 4 шагов: intro → qualify → book → close. Цель проверки — сжатие воронки и содержательность первого ответа.
Что проверяем на каждом кейсе
Структура первого ответа бота на жалобу пациента (5-пунктовый шаблон, см. prompts/intents/new_booking/steps/qualify.md):
- Эмпатия — одна короткая фраза (одна, не больше).
- Гипотеза — 2–3 ЛОР-причины формулировкой «может быть связано с…», без диагноза. Если в RAG-выдержках причин нет — пункт допустимо пропустить.
- Специалист — рекомендация по профилю (зафиксирован в слот
specialist). - Услуга и цена — формулировкой «при необходимости назначит». Если в RAG нет — пункт пропускается.
- CTA — бинарный вопрос «записать?».
Сжатие воронки:
- До запроса телефона — ≤ 3 реплики бота (раньше было 5–6).
- Имя на
introне спрашивается — спрашивается наbookвместе с телефоном. - Граф работает по
intro → qualify → book → close. Наpresentмодель не попадает (в Песочнице бейдж шага не показываетpresent).
RAG:
- В отладочной панели «Найденные фрагменты» видно, что чанки пришли из подписанных документов ветки
new_booking. - Если ветка не подписана ни на один документ — гипотеза/услуга/цена пропускаются (5-пунктовый шаблон деградирует до эмпатия + специалист + CTA).
Кейсы
A.1 · «Очень сильно храплю, иногда закладывает уши»
Контрольный кейс из docs/OPTIMIZATION_CONVERSION_v1.md §1 (сравнение с конкурентом «Александра»).
- Ожидаемый специалист: ЛОР.
- Ожидаемые гипотезы (из вики): искривление перегородки, аденоиды, ринит.
- Ожидаемая услуга: эндоскопия, 1 000 ₽ (если в подписанных документах есть).
- Слоты после
qualify:reason="храп + заложенность ушей",specialist="ЛОР".
A.2 · «Болит горло уже неделю, не проходит»
- Ожидаемый специалист: ЛОР.
- Ожидаемые гипотезы: тонзиллит, фарингит.
- Слоты:
reason="боль в горле, неделя",specialist="ЛОР".
A.3 · «Стал плохо слышать на одно ухо, и звон»
Особая ситуация 3 (needs_surgologist_first).
- Ожидаемое поведение: сначала уточнить «вас уже обследовал сурдолог?», при первичном —
specialist=ЛОР,needs_surgologist_first=true. - Объяснение: «обычно начинают с ЛОР-врача, при необходимости направит к сурдологу».
A.4 · «Насморк больше месяца, не проходит»
- Ожидаемый специалист: ЛОР.
- Ожидаемые гипотезы: хронический ринит, синусит.
A.5 · «Звон в ушах, какой-то непонятный»
Аналог A.3, проверка устойчивости.
- Ожидаемое поведение: уточнение «были у сурдолога?», при первичном — ЛОР с пометкой про сурдолога.
B · Регрессия 8 ручных сценариев (блок H Спринта 6b)
После переписки воронки в Спринте 7.6 — все 8 сценариев должны продолжать работать. Сравниваем с разобранными примерами в docs/examples/*_v2.md.
B.1 · Базовая запись к ЛОР-врачу
- См.
docs/examples/01_basic_booking_v2.md. - Ожидание: путь
intro → qualify → book → close(3 реплики бота до телефона), без особых ситуаций.
B.2 · Soft-insertion цена в середине записи
- См.
docs/examples/02_price_during_booking_v2.mdВариант A. - Ожидание: на короткое «а сколько стоит?» — ответ в-line, шаг не меняется,
soft_insertion_count++.
B.3 · Hard-handoff в reschedule и возврат
- См.
docs/examples/02_price_during_booking_v2.mdВариант B (тамprice_question, для reschedule аналогично). - Ожидание:
suspended_intent=new_booking, после возврата — восстановлениеcurrent_step_codeиslots.
B.4 · Возврат из suspended_intent
- Подразумевается в B.3.
- Ожидание: при возврате
handoff_countсбрасывается в 0.
B.5 · Упоминание хирургии → escalate с reason=surgery
- Пациент в любом месте говорит «у меня уже была операция, надо перенести» — должен сработать
[INTENT_CHANGE: escalate_human]сreason=surgery.
B.6 · Петля роутера → автоэскалация с reason=routing_loop
- Искусственно: чередовать
new_booking ↔ price_question4+ раза. - Ожидание: на 4-м переключении автоматически уйти в
escalate_humanсreason=routing_loopбез вызова LLM.
B.7 · Запись ребёнка с защитным условием require_legal_rep
- См.
docs/examples/03_child_patient_guard_v2.md. - Ожидание: при
is_child=trueи пустыхlegal_rep_*валидатор блокирует переходqualify → book. Внимание: в Спринте 7.6 переход теперьqualify → book(раньше былоqualify → present). Защитное условие должно продолжать работать на новом переходе.
B.8 · Конкретный врач → лист ожидания
- Пациент: «хочу к доктору Иванову».
- Ожидание:
requested_doctor="Иванов",waitlist_flag=true, фраза «администратор свяжется для уточнения даты».
Как прогонять
- Открой Песочницу (
http://localhost:8000/sandbox.html). - Создай новый тред для каждого кейса (чтобы счётчики
handoff_countиsoft_insertion_countбыли чистыми). - Веди диалог как пациент, проставляй галочки в чеклисте по факту.
- Если что-то не так — отметь словесно, приложи скрин/реплику. Возвращаемся в код, правим, прогоняем снова.
Что НЕ делает этот документ
- Не запускается автоматически. Для автозапуска — Спринт 8 (
eval/run.py). - Не покрывает все возможные граничные случаи маршрутизатора. Для них есть
eval/router_cases_*.jsonl(тоже к Спринту 8). - Не сравнивает с baseline по метрикам. Это всё прогоны «глазами».