Files
AR 15 M4 60f8a7b398 feat(sprint7.6): оптимизация воронки new_booking до 4 шагов (вариант 2)
Воронка сжата с 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>
2026-04-28 21:04:09 +05:00

8.8 KiB
Raw Permalink Blame History

Ручные кейсы для регрессии

Чеклист для прогонов в Песочнице. Не автоматизирован — это 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 реплики бота (раньше было 56).
  • Имя на 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_question 4+ раза.
  • Ожидание: на 4-м переключении автоматически уйти в escalate_human с reason=routing_loop без вызова LLM.
  • См. 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, фраза «администратор свяжется для уточнения даты».

Как прогонять

  1. Открой Песочницу (http://localhost:8000/sandbox.html).
  2. Создай новый тред для каждого кейса (чтобы счётчики handoff_count и soft_insertion_count были чистыми).
  3. Веди диалог как пациент, проставляй галочки в чеклисте по факту.
  4. Если что-то не так — отметь словесно, приложи скрин/реплику. Возвращаемся в код, правим, прогоняем снова.

Что НЕ делает этот документ

  • Не запускается автоматически. Для автозапуска — Спринт 8 (eval/run.py).
  • Не покрывает все возможные граничные случаи маршрутизатора. Для них есть eval/router_cases_*.jsonl (тоже к Спринту 8).
  • Не сравнивает с baseline по метрикам. Это всё прогоны «глазами».