From d9ded99e81944ce8391547d270285d29418d8b89 Mon Sep 17 00:00:00 2001 From: Hermes Date: Tue, 15 Sep 2026 15:50:19 +0200 Subject: [PATCH] =?UTF-8?q?docs(autocall):=20HTML-=D0=B2=D0=B5=D1=80=D1=81?= =?UTF-8?q?=D0=B8=D1=8F=20=D0=BF=D1=80=D0=B5=D0=B7=D0=B5=D0=BD=D1=82=D0=B0?= =?UTF-8?q?=D1=86=D0=B8=D0=B8=20=D0=B8=D0=B7=20=D0=BE=D0=B1=D1=89=D0=B5?= =?UTF-8?q?=D0=B3=D0=BE=20=D1=84=D0=B0=D0=B9=D0=BB=D0=B0=20=D1=81=D0=BE?= =?UTF-8?q?=D0=B4=D0=B5=D1=80=D0=B6=D0=B0=D0=BD=D0=B8=D1=8F=20(deck=5Fcont?= =?UTF-8?q?ent.py),=20=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B0=20?= =?UTF-8?q?=D1=81=D0=BE=D0=B2=D0=BF=D0=B0=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F=20?= =?UTF-8?q?=D1=82=D0=B5=D0=BA=D1=81=D1=82=D0=B0=20=D1=81=20PDF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/autocall/autocall-how-it-works.html | 88 ++++++ docs/autocall/deck_content.py | 349 +++++++++++++++++++++++ docs/autocall/render-html.py | 188 ++++++++++++ 3 files changed, 625 insertions(+) create mode 100644 docs/autocall/autocall-how-it-works.html create mode 100644 docs/autocall/deck_content.py create mode 100644 docs/autocall/render-html.py diff --git a/docs/autocall/autocall-how-it-works.html b/docs/autocall/autocall-how-it-works.html new file mode 100644 index 0000000..4da8b3b --- /dev/null +++ b/docs/autocall/autocall-how-it-works.html @@ -0,0 +1,88 @@ + + + + + +Автодозвон SuperSam — как это работает + + + +
СУПЕР САМ · ДОСТАВКА И САМОВЫВОЗ

Автодозвон: как это работает

Робот звонит клиенту, согласует день и половину дня — без участия оператора

Что описано: правила звонка, паузы, поведение в нештатных случаях (не дозвонились,

сбросили трубку, не нажали цифру), где что настраивается и что видит менеджер.

 

Основано на текущем коде и настройках боевой системы, без обещаний и продажи.

Автодозвон SuperSam · как это работает1

Что делает робот

Коротко: он не продаёт и не консультирует — только согласует день и половину дня

Три задачи

  • Дозвониться, когда заказ готов и клиент не подтвердил день по ссылке из SMS
  • Предложить конкретные дни голосом и получить выбор цифрой
  • Записать выбор в заказ — дату, половину дня и признак «согласовано роботом»

Чего он не делает

  • Не соединяет с живым менеджером — такой цифры в диалоге нет
  • Не спорит и не уговаривает: нет ответа — повторит по расписанию
  • Не предлагает нерабочие дни и не звонит вне рабочего времени

Как это выглядит для клиента

  • Звонок с одного номера, голос один и тот же: «Ваш заказ готов. Выберите день и время»
  • Клиент нажимает цифру дня и цифру половины дня — робот подтверждает выбор вслух
  • После подтверждения даёт возможность изменить выбор (нажатием цифры 2)
Автодозвон SuperSam · как это работает2

Путь заказа до звонка

Когда именно звучит звонок

1. Заказ готовменеджер отмечаетготовность в системе
›
2. SMS со ссылкойклиент может выбратьдень сам, без звонка
›
3. Пауза 90 минутнастройка админки:столько ждём ответа
›
4. Звонок роботаесли клиент неподтвердил по ссылке
›
5. Результат в заказдата и половина дняв самом заказе

Важно

  • Если клиент подтвердил день по ссылке сам — звонка не будет: заказ уже согласован
  • Звонок идёт только в рабочее время и только в рабочие дни (настройки кампании)
Автодозвон SuperSam · как это работает3

Откуда берутся дни и время

Админка → Расписание → Рабочие дни: один источник правды

Что настраивается

  • Рабочие дни склада (самовывоз) и рабочие дни доставки
  • Часы: открытие и закрытие склада, часы доставки
  • «Первая половина — до» — граница между половинами дня
  • «Сегодня предлагаем до» — до какого времени предлагать сегодняшний день

Как это работает в коде

  • Окно звонка считается рабочими днями: выходные не сокращают окно, а пропускаются
  • Робот называет реальные дни голосом: «сегодня», «в понедельник», «во вторник»
  • Клиентская ссылка использует те же дни — расхождений между звонком и ссылкой нет

Текущие значения в системе

  • Склад: пн–пт, 09:00–17:30, половина дня до 13:00, «сегодня» предлагаем до 15:30
  • Доставка: пн–пт (суббота — выходной) 09:00–21:00, половина дня до 15:00
  • Утро и вечер: до 13:00 робот спросит, какая половина дня удобнее; после 13:00 — сразу зафиксирует вторую
Автодозвон SuperSam · как это работает4

Самовывоз: какие дни предлагает

Окно — «день готовности + два рабочих дня», выходные пропускаются

Заказ готовЧто предложит роботПочему так
в понедельникпн, вт, сртри рабочих дня подряд
в пятницупт, пн, втсуббота и воскресенье — выходные склада
в субботупн, вт, срсам выходной не предлагаем
в пятницу после 15:30пн, вт«сегодня» больше не предлагаем (настройка)
в понедельник после 13:00пн, вт, срполовину дня не спрашиваем — сразу вторая
неделю назадзвонка нетокно прошло — звонить не о чем

Две настройки, которые это двигают

  • «Сегодня предлагаем до» (сейчас 15:30) — позже этого времени сегодняшний день исчезает из звонка и из ссылки
  • «Первая половина — до» (сейчас 13:00) — после неё робот не спрашивает половину дня, сразу пишет вторую
Автодозвон SuperSam · как это работает5

Доставка: не в день готовности

Нужен запас времени, чтобы собрать маршрут — поэтому начинаем со следующего дня

Заказ готовЧто предложит робот
в понедельниквторник и среда
в пятницупонедельник и вторник (в субботу не возим)
в субботупонедельник и вторник
в четверг, звонок в пятницупятница и понедельник
в пятницу, звонок в понедельникпонедельник и вторник

Ключевое отличие от самовывоза

  • В день готовности доставку не предлагаем никогда — только следующие рабочие дни
  • Суббота и воскресенье в графике доставки — выходные, их робот не назовёт
  • Время доставки — половина дня: до 15:00 или после (границу задаёт админка)
Автодозвон SuperSam · как это работает6

Как проходит разговор

Робот задаёт вопрос и ждёт цифру; молчание не считается согласием

ШагЧто говорит роботЧто делает клиентСколько ждёт
Приветствие«Ваш заказ готов. Выберите день и время доставки»слушает—
День«Нажмите один, чтобы доставить. В понедельник. Нажмите два…»нажимает 1 или 29 секунд
Половина дня«Первая половина дня или вторая?»нажимает 1 или 29 секунд
Подтверждение«Первая половина дня» — озвучивает выборслушает—
Изменить?подсказка про кнопку 2 — один разнажимает 2, чтобы переделать6 секунд

Что делает при ошибке ввода

  • Не понял цифру — повторяет тот же вопрос один раз
  • Снова не понял или молчит — завершает: «Вы не выбрали время. С вами свяжется менеджер»
  • Клиент нажимает 2 — диалог начинается заново («день и время»), прежний слот при этом не теряется
Автодозвон SuperSam · как это работает7

Что можно нажать

Всего несколько цифр — и одна из них «больше не звоните»

ЦифраЗначениеЧто происходит
1первый предложенный деньробот переходит к выбору половины дня
2второй предложенный деньробот переходит к выбору половины дня
3третий день (только самовывоз)робот переходит к выбору половины дня
1 / 2половина дняпервая половина / вторая половина
9отказ от звонковробот больше не позвонит; в карточке — «отказ от звонков»
0 и прочиене принимаютсявопрос повторяется; соединения с оператором нет

Отдельно про «0»

  • Раньше в диалоге была попытка «позвать менеджера», но она никогда не срабатывала (нажатие не отличалось от тишины)
  • Эту ветку убрали: живого соединения с оператором у робота нет, менеджер подключается только задачей из системы
Автодозвон SuperSam · как это работает8

Если не дозвонились

Робот различает «не взял трубку» и «вместо человека ответил автоответчик/оператор»

Как понимает, что трубку не взяли

  • Никто не ответил за 45 секунд — попытка считается неудачной
  • Трубку подняли, но гудки шли больше 20 секунд и ни одной цифры не нажали — это объявление оператора, а не человек: пишем «не взял трубку»
  • Живой человек почти всегда берёт трубку раньше, поэтому правило надёжно отделяет автоответчик

Что происходит дальше

  • Повторный звонок — не раньше чем через 120 минут (настройка)
  • Всего не больше 5 попыток на заказ (настройка)
  • Звонит только в рабочие часы кампании: 09:00–20:00, дни — по настройке
  • Исчерпали попытки — заказ уходит менеджеру, автозвонок по нему останавливается

Для менеджера это видно в карточке

  • Каждая попытка — отдельная строка истории: время, результат, длительность разговора
  • Статус заказа меняется на «нужен менеджер» — с комментарием «не дозвонились / не нажал»
  • Отдельно красным предупреждаем, если заявка «Позвонить сейчас» висит больше 15 минут
Автодозвон SuperSam · как это работает9

Если сбросили трубку в середине разговора

Робот запоминает то, что успел услышать, и не портит уже согласованный слот

Когда положили трубкуЧто запишет роботЧто увидит менеджер
до выбора дняответил, ничего не нажал«взял, не нажал (день)» — попытка зачтена
день выбрал, время не успелдень выбран, время нет«взял, не нажал (время)»
выбор подтвердил и сбросилслот согласовандата и половина дня в заказе, статус «согласовано»
начал менять выбор и сбросилостаётся прежний слот«правка не доведена» — прежнее согласование в силе

Почему так

  • Слот становится согласованным только после двух ответов: день и половина дня — это защита от «услышал половину фразы»
  • Обрыв не считается согласием: если клиент не подтвердил, робот перезвонит по расписанию или отдаст заказ менеджеру
  • Если клиент уже согласовал слот, а потом начал менять и бросил трубку — прежний выбор сохраняется, ничего не теряется
Автодозвон SuperSam · как это работает10

Когда подключается менеджер

Робот доводит дело до конца сам, но в трёх случаях передаёт человеку

Не нажал цифру 2 раза

  • Робот писал «взял, но не нажал» дважды
  • Заказ помечается «нужен менеджер», комментарий «не дозвонились / не нажал»
  • Порог «не нажал × N» настраивается в админке автодозвона

Исчерпаны 5 попыток

  • Каждый звонок — отдельная попытка
  • Когда попытки закончились, автозвонок прекращается
  • Заказ остаётся в списке у менеджера

Отказ или неверный номер

  • Цифра 9 — клиент просит больше не звонить: ставим запрет автоматически
  • Неверный номер — задача менеджеру
  • Отказ клиента действует и на другие заказы: он в списке «не звонить»

Что видит менеджер в карточке заказа

  • Единый статус автодозвона и история всех попыток с результатами и слотами
  • Кнопка «Позвонить сейчас» — ручной звонок клиенту (не тестовый номер) и живое состояние: «в очереди», «звоним», «звонок сделан» с результатом
  • Кнопка «Не звонить» — если робот не должен беспокоить этого клиента
Автодозвон SuperSam · как это работает11

Когда робот не звонит вообще

Список условий, при которых звонка не будет — проверяются до набора номера

По клиенту и заказу

  • Клиент сам подтвердил день по ссылке — звонок не нужен
  • Клиент просил больше не звонить (нажал 9) или менеджер поставил «Не звонить»
  • Номер в списке исключений — например, на МТС-диапазон звонки не идут
  • Самовывоз: автообзвон по самовывозу включается отдельной галочкой (сейчас выключен)

По времени и географии

  • Вне рабочих часов кампании (09:00–20:00) и вне рабочих дней
  • Город исключён целиком или для города выключен канал
  • В админке автодозвона выключены звонки по городу — например, Ялта: SMS идут, звонков нет
  • Меньше 90 минут прошло после SMS со ссылкой

Админка автодозвона: где это переключается

  • Панель «Автодозвон»: тестовый режим, часы и дни звонков, паузы, лимит попыток, порог «не нажал × N»
  • Панель «Локации»: для каждого города — звонки включены или выключены (SMS не затрагивается)
  • Панель исключений: номера и префиксы, на которые не звоним, с причиной
Автодозвон SuperSam · как это работает12

Что записывается в заказ

Результат звонка виден там же, где всё остальное

ПолеЗначение
Датаконкретная дата, которую назвал робот (например, понедельник 21.09)
Время«первая половина дня» или «вторая половина дня»
Источник«согласовано роботом» — видно, что выбор сделал клиент в звонке
Статус«согласовано» / «нужен менеджер» / «отказ от звонков»
Комментарий«Согласовано роботом: завтра, первая половина дня»

Зачем это нужно

  • Менеджер видит причину и источник решения, а не просто «дата стоит»
  • История звонков хранится отдельно: видно, сколько было попыток и чем они закончились
  • Дата, названная роботом, используется дальше в работе склада и доставки
Автодозвон SuperSam · как это работает13

Тестовый режим и текущее состояние

Сейчас обзвон клиентов выключен — система в тестовом режиме

ПараметрЗначение сейчасЧто будет при запуске
Обзвон клиентоввыключенвключается отдельной командой
Тестовый номерзвонит только на негомассовый обзвон идёт клиентам
Кнопка «Позвонить сейчас»звонит клиенту сразутак же — звонок клиенту
Звонки по самовывозувыключены галочкойвключаются отдельно от доставки

Как проверяется перед запуском

  • Тестовый звонок на свой номер: диалог, дни, половина дня, запись результата
  • Прогон диалога без звонка: проверка всех веток (согласие, обрыв, молчание, отказ)
  • Проверка окон: робот предлагает именно те дни, которые заданы в админке
Автодозвон SuperSam · как это работает14

Что пока не учитывается

Честный список: это важно знать до запуска

График

  • Праздники: календаря нерабочих дат нет — если праздник попадёт в окно, робот его предложит
  • Часы доставки используются частично: границу половины дня робот проговаривает словами, без конкретного времени

Настройки, которые пока не влияют

  • «Попыток в день» — поле есть, но расписание повторов задаётся интервалом между попытками
  • «Попытки следующего дня (10:00, 14:00)» — пока не используются
  • «Таймаут звонка, 30 секунд» — звонок ждёт ответа 45 секунд (значение в самом звонке)

Что предлагаем уточнить до боевого запуска

  • Список праздников (или решение не учитывать их)
  • Нужен ли жёсткий график повторов по времени, кроме интервала 120 минут
  • Оставить ли «0» без реакции — сейчас это просто неверная цифра
Автодозвон SuperSam · как это работает15

Коротко

Одна страница вместо всей презентации

Что делает система

  • Ждёт 90 минут после SMS и звонит, если клиент не подтвердил день сам
  • Предлагает только рабочие дни по графику: самовывоз — включая сегодня, доставка — со следующего дня
  • Спрашивает день и половину дня, подтверждает вслух, даёт один раз переделать выбор
  • Повторяет попытки не чаще чем раз в 2 часа, максимум 5 раз, только 09:00–20:00 в рабочие дни
  • После двух «взял, но не нажал» или исчерпания попыток передаёт заказ менеджеру
  • Отказ (цифра 9) останавливает обзвон клиента навсегда
  • Каждая попытка и результат видны в карточке заказа; ручной звонок идёт клиенту и показывает своё состояние

Что осталось решить

  • Праздники и точный график повторов — по вашим правилам
  • Момент включения боевого обзвона клиентов и по самовывозу
Автодозвон SuperSam · как это работает16
+ + diff --git a/docs/autocall/deck_content.py b/docs/autocall/deck_content.py new file mode 100644 index 0000000..50f9896 --- /dev/null +++ b/docs/autocall/deck_content.py @@ -0,0 +1,349 @@ +"""Содержание презентации «Автодозвон: как это работает». + +Один источник правды: и PDF (how-it-works.py), и HTML (render-html.py) собираются +отсюда, поэтому версии не разъезжаются при правках. + +Факты сверены с кодом: scripts/autocall_scheduler.py, docs/ats/ss-dialog.agi, +supabase/functions/autocall-result/index.ts и настройками боевой системы. +""" + +ACCENT, ACCENT2, WARN, DANGER = "accent", "accent2", "warn", "danger" + +TITLE_SLIDE = { + "kind": "title", + "kicker": "СУПЕР САМ · ДОСТАВКА И САМОВЫВОЗ", + "title": "Автодозвон: как это работает", + "sub": "Робот звонит клиенту, согласует день и половину дня — без участия оператора", + "lines": [ + "Что описано: правила звонка, паузы, поведение в нештатных случаях (не дозвонились,", + "сбросили трубку, не нажали цифру), где что настраивается и что видит менеджер.", + "", + "Основано на текущем коде и настройках боевой системы, без обещаний и продажи.", + ], +} + +SLIDES = [ + { + "kind": "cards2full", + "title": "Что делает робот", + "sub": "Коротко: он не продаёт и не консультирует — только согласует день и половину дня", + "cards": [ + {"title": "Три задачи", "accent": ACCENT, "items": [ + "Дозвониться, когда заказ готов и клиент не подтвердил день по ссылке из SMS", + "Предложить конкретные дни голосом и получить выбор цифрой", + "Записать выбор в заказ — дату, половину дня и признак «согласовано роботом»", + ]}, + {"title": "Чего он не делает", "accent": WARN, "items": [ + "Не соединяет с живым менеджером — такой цифры в диалоге нет", + "Не спорит и не уговаривает: нет ответа — повторит по расписанию", + "Не предлагает нерабочие дни и не звонит вне рабочего времени", + ]}, + ], + "full": {"title": "Как это выглядит для клиента", "accent": ACCENT2, "items": [ + "Звонок с одного номера, голос один и тот же: «Ваш заказ готов. Выберите день и время»", + "Клиент нажимает цифру дня и цифру половины дня — робот подтверждает выбор вслух", + "После подтверждения даёт возможность изменить выбор (нажатием цифры 2)", + ]}, + }, + { + "kind": "steps", + "title": "Путь заказа до звонка", + "sub": "Когда именно звучит звонок", + "steps": [ + ("1. Заказ готов", "менеджер отмечает\nготовность в системе"), + ("2. SMS со ссылкой", "клиент может выбрать\nдень сам, без звонка"), + ("3. Пауза 90 минут", "настройка админки:\nстолько ждём ответа"), + ("4. Звонок робота", "если клиент не\nподтвердил по ссылке"), + ("5. Результат в заказ", "дата и половина дня\nв самом заказе"), + ], + "full": {"title": "Важно", "accent": WARN, "items": [ + "Если клиент подтвердил день по ссылке сам — звонка не будет: заказ уже согласован", + "Звонок идёт только в рабочее время и только в рабочие дни (настройки кампании)", + ]}, + }, + { + "kind": "cards2full", + "title": "Откуда берутся дни и время", + "sub": "Админка → Расписание → Рабочие дни: один источник правды", + "cards": [ + {"title": "Что настраивается", "accent": ACCENT, "items": [ + "Рабочие дни склада (самовывоз) и рабочие дни доставки", + "Часы: открытие и закрытие склада, часы доставки", + "«Первая половина — до» — граница между половинами дня", + "«Сегодня предлагаем до» — до какого времени предлагать сегодняшний день", + ]}, + {"title": "Как это работает в коде", "accent": ACCENT2, "items": [ + "Окно звонка считается рабочими днями: выходные не сокращают окно, а пропускаются", + "Робот называет реальные дни голосом: «сегодня», «в понедельник», «во вторник»", + "Клиентская ссылка использует те же дни — расхождений между звонком и ссылкой нет", + ]}, + ], + "full": {"title": "Текущие значения в системе", "accent": WARN, "items": [ + "Склад: пн–пт, 09:00–17:30, половина дня до 13:00, «сегодня» предлагаем до 15:30", + "Доставка: пн–пт (суббота — выходной) 09:00–21:00, половина дня до 15:00", + "Утро и вечер: до 13:00 робот спросит, какая половина дня удобнее; после 13:00 — сразу зафиксирует вторую", + ]}, + }, + { + "kind": "tablecard", + "title": "Самовывоз: какие дни предлагает", + "sub": "Окно — «день готовности + два рабочих дня», выходные пропускаются", + "table": { + "rows": [ + ["Заказ готов", "Что предложит робот", "Почему так"], + ["в понедельник", "пн, вт, ср", "три рабочих дня подряд"], + ["в пятницу", "пт, пн, вт", "суббота и воскресенье — выходные склада"], + ["в субботу", "пн, вт, ср", "сам выходной не предлагаем"], + ["в пятницу после 15:30", "пн, вт", "«сегодня» больше не предлагаем (настройка)"], + ["в понедельник после 13:00", "пн, вт, ср", "половину дня не спрашиваем — сразу вторая"], + ["неделю назад", "звонка нет", "окно прошло — звонить не о чем"], + ], + "widths": [200, 240, 428], + }, + "full": {"title": "Две настройки, которые это двигают", "accent": ACCENT, "items": [ + "«Сегодня предлагаем до» (сейчас 15:30) — позже этого времени сегодняшний день исчезает из звонка и из ссылки", + "«Первая половина — до» (сейчас 13:00) — после неё робот не спрашивает половину дня, сразу пишет вторую", + ]}, + }, + { + "kind": "tablecard", + "title": "Доставка: не в день готовности", + "sub": "Нужен запас времени, чтобы собрать маршрут — поэтому начинаем со следующего дня", + "table": { + "rows": [ + ["Заказ готов", "Что предложит робот"], + ["в понедельник", "вторник и среда"], + ["в пятницу", "понедельник и вторник (в субботу не возим)"], + ["в субботу", "понедельник и вторник"], + ["в четверг, звонок в пятницу", "пятница и понедельник"], + ["в пятницу, звонок в понедельник", "понедельник и вторник"], + ], + "widths": [300, 568], + }, + "full": {"title": "Ключевое отличие от самовывоза", "accent": ACCENT2, "items": [ + "В день готовности доставку не предлагаем никогда — только следующие рабочие дни", + "Суббота и воскресенье в графике доставки — выходные, их робот не назовёт", + "Время доставки — половина дня: до 15:00 или после (границу задаёт админка)", + ]}, + }, + { + "kind": "tablecard", + "title": "Как проходит разговор", + "sub": "Робот задаёт вопрос и ждёт цифру; молчание не считается согласием", + "table": { + "rows": [ + ["Шаг", "Что говорит робот", "Что делает клиент", "Сколько ждёт"], + ["Приветствие", "«Ваш заказ готов. Выберите день и время доставки»", "слушает", "—"], + ["День", "«Нажмите один, чтобы доставить. В понедельник. Нажмите два…»", "нажимает 1 или 2", "9 секунд"], + ["Половина дня", "«Первая половина дня или вторая?»", "нажимает 1 или 2", "9 секунд"], + ["Подтверждение", "«Первая половина дня» — озвучивает выбор", "слушает", "—"], + ["Изменить?", "подсказка про кнопку 2 — один раз", "нажимает 2, чтобы переделать", "6 секунд"], + ], + "widths": [130, 360, 200, 178], + }, + "full": {"title": "Что делает при ошибке ввода", "accent": WARN, "items": [ + "Не понял цифру — повторяет тот же вопрос один раз", + "Снова не понял или молчит — завершает: «Вы не выбрали время. С вами свяжется менеджер»", + "Клиент нажимает 2 — диалог начинается заново («день и время»), прежний слот при этом не теряется", + ]}, + }, + { + "kind": "tablecard", + "title": "Что можно нажать", + "sub": "Всего несколько цифр — и одна из них «больше не звоните»", + "table": { + "rows": [ + ["Цифра", "Значение", "Что происходит"], + ["1", "первый предложенный день", "робот переходит к выбору половины дня"], + ["2", "второй предложенный день", "робот переходит к выбору половины дня"], + ["3", "третий день (только самовывоз)", "робот переходит к выбору половины дня"], + ["1 / 2", "половина дня", "первая половина / вторая половина"], + ["9", "отказ от звонков", "робот больше не позвонит; в карточке — «отказ от звонков»"], + ["0 и прочие", "не принимаются", "вопрос повторяется; соединения с оператором нет"], + ], + "widths": [110, 240, 518], + }, + "full": {"title": "Отдельно про «0»", "accent": DANGER, "items": [ + "Раньше в диалоге была попытка «позвать менеджера», но она никогда не срабатывала (нажатие не отличалось от тишины)", + "Эту ветку убрали: живого соединения с оператором у робота нет, менеджер подключается только задачей из системы", + ]}, + }, + { + "kind": "cards2full", + "title": "Если не дозвонились", + "sub": "Робот различает «не взял трубку» и «вместо человека ответил автоответчик/оператор»", + "cards": [ + {"title": "Как понимает, что трубку не взяли", "accent": WARN, "items": [ + "Никто не ответил за 45 секунд — попытка считается неудачной", + "Трубку подняли, но гудки шли больше 20 секунд и ни одной цифры не нажали — это объявление оператора, а не человек: пишем «не взял трубку»", + "Живой человек почти всегда берёт трубку раньше, поэтому правило надёжно отделяет автоответчик", + ]}, + {"title": "Что происходит дальше", "accent": ACCENT, "items": [ + "Повторный звонок — не раньше чем через 120 минут (настройка)", + "Всего не больше 5 попыток на заказ (настройка)", + "Звонит только в рабочие часы кампании: 09:00–20:00, дни — по настройке", + "Исчерпали попытки — заказ уходит менеджеру, автозвонок по нему останавливается", + ]}, + ], + "full": {"title": "Для менеджера это видно в карточке", "accent": ACCENT2, "items": [ + "Каждая попытка — отдельная строка истории: время, результат, длительность разговора", + "Статус заказа меняется на «нужен менеджер» — с комментарием «не дозвонились / не нажал»", + "Отдельно красным предупреждаем, если заявка «Позвонить сейчас» висит больше 15 минут", + ]}, + }, + { + "kind": "tablecard", + "title": "Если сбросили трубку в середине разговора", + "sub": "Робот запоминает то, что успел услышать, и не портит уже согласованный слот", + "table": { + "rows": [ + ["Когда положили трубку", "Что запишет робот", "Что увидит менеджер"], + ["до выбора дня", "ответил, ничего не нажал", "«взял, не нажал (день)» — попытка зачтена"], + ["день выбрал, время не успел", "день выбран, время нет", "«взял, не нажал (время)»"], + ["выбор подтвердил и сбросил", "слот согласован", "дата и половина дня в заказе, статус «согласовано»"], + ["начал менять выбор и сбросил", "остаётся прежний слот", "«правка не доведена» — прежнее согласование в силе"], + ], + "widths": [250, 250, 368], + }, + "full": {"title": "Почему так", "accent": ACCENT2, "items": [ + "Слот становится согласованным только после двух ответов: день и половина дня — это защита от «услышал половину фразы»", + "Обрыв не считается согласием: если клиент не подтвердил, робот перезвонит по расписанию или отдаст заказ менеджеру", + "Если клиент уже согласовал слот, а потом начал менять и бросил трубку — прежний выбор сохраняется, ничего не теряется", + ]}, + }, + { + "kind": "cards3full", + "title": "Когда подключается менеджер", + "sub": "Робот доводит дело до конца сам, но в трёх случаях передаёт человеку", + "cards": [ + {"title": "Не нажал цифру 2 раза", "accent": WARN, "items": [ + "Робот писал «взял, но не нажал» дважды", + "Заказ помечается «нужен менеджер», комментарий «не дозвонились / не нажал»", + "Порог «не нажал × N» настраивается в админке автодозвона", + ]}, + {"title": "Исчерпаны 5 попыток", "accent": WARN, "items": [ + "Каждый звонок — отдельная попытка", + "Когда попытки закончились, автозвонок прекращается", + "Заказ остаётся в списке у менеджера", + ]}, + {"title": "Отказ или неверный номер", "accent": DANGER, "items": [ + "Цифра 9 — клиент просит больше не звонить: ставим запрет автоматически", + "Неверный номер — задача менеджеру", + "Отказ клиента действует и на другие заказы: он в списке «не звонить»", + ]}, + ], + "full": {"title": "Что видит менеджер в карточке заказа", "accent": ACCENT, "items": [ + "Единый статус автодозвона и история всех попыток с результатами и слотами", + "Кнопка «Позвонить сейчас» — ручной звонок клиенту (не тестовый номер) и живое состояние: «в очереди», «звоним», «звонок сделан» с результатом", + "Кнопка «Не звонить» — если робот не должен беспокоить этого клиента", + ]}, + }, + { + "kind": "cards2full", + "title": "Когда робот не звонит вообще", + "sub": "Список условий, при которых звонка не будет — проверяются до набора номера", + "cards": [ + {"title": "По клиенту и заказу", "accent": ACCENT, "items": [ + "Клиент сам подтвердил день по ссылке — звонок не нужен", + "Клиент просил больше не звонить (нажал 9) или менеджер поставил «Не звонить»", + "Номер в списке исключений — например, на МТС-диапазон звонки не идут", + "Самовывоз: автообзвон по самовывозу включается отдельной галочкой (сейчас выключен)", + ]}, + {"title": "По времени и географии", "accent": WARN, "items": [ + "Вне рабочих часов кампании (09:00–20:00) и вне рабочих дней", + "Город исключён целиком или для города выключен канал", + "В админке автодозвона выключены звонки по городу — например, Ялта: SMS идут, звонков нет", + "Меньше 90 минут прошло после SMS со ссылкой", + ]}, + ], + "full": {"title": "Админка автодозвона: где это переключается", "accent": ACCENT2, "items": [ + "Панель «Автодозвон»: тестовый режим, часы и дни звонков, паузы, лимит попыток, порог «не нажал × N»", + "Панель «Локации»: для каждого города — звонки включены или выключены (SMS не затрагивается)", + "Панель исключений: номера и префиксы, на которые не звоним, с причиной", + ]}, + }, + { + "kind": "tablecard", + "title": "Что записывается в заказ", + "sub": "Результат звонка виден там же, где всё остальное", + "table": { + "rows": [ + ["Поле", "Значение"], + ["Дата", "конкретная дата, которую назвал робот (например, понедельник 21.09)"], + ["Время", "«первая половина дня» или «вторая половина дня»"], + ["Источник", "«согласовано роботом» — видно, что выбор сделал клиент в звонке"], + ["Статус", "«согласовано» / «нужен менеджер» / «отказ от звонков»"], + ["Комментарий", "«Согласовано роботом: завтра, первая половина дня»"], + ], + "widths": [200, 668], + }, + "full": {"title": "Зачем это нужно", "accent": ACCENT, "items": [ + "Менеджер видит причину и источник решения, а не просто «дата стоит»", + "История звонков хранится отдельно: видно, сколько было попыток и чем они закончились", + "Дата, названная роботом, используется дальше в работе склада и доставки", + ]}, + }, + { + "kind": "tablecard", + "title": "Тестовый режим и текущее состояние", + "sub": "Сейчас обзвон клиентов выключен — система в тестовом режиме", + "table": { + "rows": [ + ["Параметр", "Значение сейчас", "Что будет при запуске"], + ["Обзвон клиентов", "выключен", "включается отдельной командой"], + ["Тестовый номер", "звонит только на него", "массовый обзвон идёт клиентам"], + ["Кнопка «Позвонить сейчас»", "звонит клиенту сразу", "так же — звонок клиенту"], + ["Звонки по самовывозу", "выключены галочкой", "включаются отдельно от доставки"], + ], + "widths": [230, 280, 358], + }, + "full": {"title": "Как проверяется перед запуском", "accent": ACCENT2, "items": [ + "Тестовый звонок на свой номер: диалог, дни, половина дня, запись результата", + "Прогон диалога без звонка: проверка всех веток (согласие, обрыв, молчание, отказ)", + "Проверка окон: робот предлагает именно те дни, которые заданы в админке", + ]}, + }, + { + "kind": "cards2full", + "title": "Что пока не учитывается", + "sub": "Честный список: это важно знать до запуска", + "cards": [ + {"title": "График", "accent": WARN, "items": [ + "Праздники: календаря нерабочих дат нет — если праздник попадёт в окно, робот его предложит", + "Часы доставки используются частично: границу половины дня робот проговаривает словами, без конкретного времени", + ]}, + {"title": "Настройки, которые пока не влияют", "accent": DANGER, "items": [ + "«Попыток в день» — поле есть, но расписание повторов задаётся интервалом между попытками", + "«Попытки следующего дня (10:00, 14:00)» — пока не используются", + "«Таймаут звонка, 30 секунд» — звонок ждёт ответа 45 секунд (значение в самом звонке)", + ]}, + ], + "full": {"title": "Что предлагаем уточнить до боевого запуска", "accent": ACCENT2, "items": [ + "Список праздников (или решение не учитывать их)", + "Нужен ли жёсткий график повторов по времени, кроме интервала 120 минут", + "Оставить ли «0» без реакции — сейчас это просто неверная цифра", + ]}, + }, + { + "kind": "cards2full", + "title": "Коротко", + "sub": "Одна страница вместо всей презентации", + "cards": [ + {"title": "Что делает система", "accent": ACCENT, "full": True, "dense": True, "items": [ + "Ждёт 90 минут после SMS и звонит, если клиент не подтвердил день сам", + "Предлагает только рабочие дни по графику: самовывоз — включая сегодня, доставка — со следующего дня", + "Спрашивает день и половину дня, подтверждает вслух, даёт один раз переделать выбор", + "Повторяет попытки не чаще чем раз в 2 часа, максимум 5 раз, только 09:00–20:00 в рабочие дни", + "После двух «взял, но не нажал» или исчерпания попыток передаёт заказ менеджеру", + "Отказ (цифра 9) останавливает обзвон клиента навсегда", + "Каждая попытка и результат видны в карточке заказа; ручной звонок идёт клиенту и показывает своё состояние", + ]}, + ], + "full": {"title": "Что осталось решить", "accent": WARN, "items": [ + "Праздники и точный график повторов — по вашим правилам", + "Момент включения боевого обзвона клиентов и по самовывозу", + ]}, + }, +] + +ALL_SLIDES = [TITLE_SLIDE] + SLIDES diff --git a/docs/autocall/render-html.py b/docs/autocall/render-html.py new file mode 100644 index 0000000..0bb494c --- /dev/null +++ b/docs/autocall/render-html.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python3 +"""HTML-версия презентации «Автодозвон: как это работает» (тот же текст, что в PDF). + +Содержание берётся из deck_content.py, поэтому PDF и HTML не разъезжаются. +Файл самодостаточный: без внешних шрифтов, картинок и скриптов — открывается +двойным щелчком и с телефона, и печатается в PDF по одному слайду на страницу. + +Запуск: python3 docs/autocall/render-html.py +""" +from html import escape +from pathlib import Path + +import deck_content as content + +OUT = Path(__file__).with_name("autocall-how-it-works.html") + +TONE = { + "accent": "#4ADE80", + "accent2": "#60A5FA", + "warn": "#FBBF24", + "danger": "#F87171", +} + + +def esc(text): + return escape(str(text)) + + +def card_html(card, extra_class=""): + tone = TONE.get(card.get("accent", "accent"), TONE["accent"]) + items = "".join(f"
  • {esc(i)}
  • " for i in card["items"]) + classes = " ".join(f"card {extra_class} {'dense' if card.get('dense') else ''}".split()) + return ( + f'
    ' + f'

    {esc(card["title"])}

    ' + ) + + +def table_html(table): + head, *body = table["rows"] + widths = table.get("widths") or [] + colgroup = "".join(f'' for w in widths) if widths else "" + head_html = "".join(f"{esc(c)}" for c in head) + body_html = "".join( + "" + "".join(f"{esc(c)}" for c in row) + "" for row in body + ) + return ( + f'{"" + colgroup + "" if colgroup else ""}' + f"{head_html}{body_html}
    " + ) + + +def steps_html(steps): + out = [] + for i, (title, desc) in enumerate(steps): + lines = "".join(f"{esc(line)}" for line in desc.split("\n")) + arrow = '
    ›
    ' if i < len(steps) - 1 else "" + out.append(f'
    {esc(title)}{lines}{arrow}
    ') + return f'
    {"".join(out)}
    ' + + +def slide_html(slide, number): + kind = slide["kind"] + parts = [''] + + if kind == "title": + lines = "".join(f"

    {esc(line)}

    " if line else "

     

    " for line in slide["lines"]) + body = ( + f'
    {esc(slide["kicker"])}
    ' + f'

    {esc(slide["title"])}

    ' + f'

    {esc(slide["sub"])}

    ' + f'
    {lines}
    ' + ) + else: + head = f'

    {esc(slide["title"])}

    {esc(slide["sub"])}

    ' + body = head + if kind == "steps": + body += steps_html(slide["steps"]) + elif kind in ("cards2full", "cards3full"): + cards = "".join(card_html(c) for c in slide["cards"]) + body += f'
    {cards}
    ' + elif kind == "tablecard": + body += table_html(slide["table"]) + full = slide.get("full") + if full: + body += card_html(full, extra_class="full") + + return f'
    {body}{parts[0]}
    ' + + +def build(): + slides = "".join(slide_html(s, i + 1) for i, s in enumerate(content.ALL_SLIDES)) + html = f""" + + + + +Автодозвон SuperSam — как это работает + + + +{slides} + + +""" + OUT.write_text(html, encoding="utf-8") + print(f"готово: {OUT} — слайдов {len(content.ALL_SLIDES)}") + + +if __name__ == "__main__": + build()