🎙️ Call Analytics Playbook · Битрикс24 + AI

Методичка по сборке системы автоматической оценки качества телефонных звонков КЦ-операторов через AI на базе Битрикс24: транскрибация → QA по Калгари-Кембриджу → детектор «упущенных» лидов → извлечение обещанных перезвонов → персональные планы обучения.

Стек: Bitrix24 REST + любая ВАТС-интеграция + STT (Whisper) + LLM (OpenRouter / Anthropic). Старт с нуля — 1 рабочий день, при наличии доступов.


🎯 TL;DR что строим

                 Bitrix24 webhook (scope: crm + telephony + voximplant + disk)
                                 │
              ┌──────────────────┴──────────────────┐
              ▼                                     ▼
     voximplant.statistic.get               crm.lead.get / crm.deal.list
       (метаданные звонков)                (outcome: won/lost/in_progress)
              │                                     │
              ▼                                     │
     ┌── crm.activity.get → FILES → disk.file.get   │
     │   (для ВАТС, у которых mp3 лежит в Activity) │
     ▼                                              │
    mp3 ──► Whisper-large-v3 (STT)                  │
              │                                     │
              ▼                                     ▼
       transcripts/*.txt ───► join по lead_id ───► bundle/per-client
                                       │
                                       ▼
                    ┌─────────────── 3 LLM-промпта ───────────────┐
                    │                                              │
              QA по Калгари-Кембридж        Lost-deals          Callbacks
              (5 этапов + 2 нити)           (горячие лиды,      (обещанные
                                            упущенные)           перезвоны)
                    │                       │                    │
                    └──────────────► dashboard.html ◄─────────────┘
                                     + персональный
                                     coaching.md

📡 1. Принципы работы с источниками данных (Битрикс24)

Что нужно из Битрикс24

Создаёшь входящий вебхук со scope:

URL вида https://<portal>.bitrix24.ru/rest/<userId>/<token>/.

Где лежат записи звонков

У Битрикса нет «единой полки записей» — каждая ВАТС-интеграция кладёт mp3 по-своему:

  1. Прямой URL — у части интеграций voximplant.statistic.get возвращает заполненный CALL_RECORD_URL. Качаешь GET без авторизации, готово.

  2. На Activity-объекте лида/контакта — большинство интеграций (виртуальные АТС, которые «подвешивают» записи к карточке лида) пишут mp3 как прикреплённый файл в Activity:

    voximplant.statistic.get → CRM_ACTIVITY_ID
    → crm.activity.get(id=...) → берёшь первый FILES[0].id
    → disk.file.get(id=file_id) → достаёшь DOWNLOAD_URL с временным токеном
    → качаешь curl-ом

    Это самый частый случай для входящих лидов от ВАТС-партнёров.

  3. Через API провайдера ВАТС напрямую — у каждого крупного оператора есть свой REST API с методами /statistics и /recording/play. Используй если запись не появилась в Битриксе или CRM не прокидывает её через Activity.

Принципиальное правило

Whitelist провайдеров. В voximplant.statistic.get поле REST_APP_NAME показывает, через какую ВАТС-интеграцию прошёл звонок. Чётко решай, какие из них анализируешь (обычно — те, через которые работают КЦ-операторы первой линии, обрабатывающие новые лиды), и фильтруй. Звонки логистов / менеджеров со старыми клиентами — другой процесс, не смешивай в одну выборку.

🎯 ВАЖНО: атрибуция оператора через ASSIGNED_BY_ID, а не PORTAL_USER_ID

Поле voximplant.PORTAL_USER_ID в Битриксе ≠ всегда реальный оператор.

Пример «грабли», на которые легко наступить:

Как отличить сервисный аккаунт:

Правило атрибуции:

if portal_user_id in ROUTER_ACCOUNTS:
    actual_operator_id = lead.ASSIGNED_BY_ID
else:
    actual_operator_id = portal_user_id

Имя оператора подтягивай не через user.get (часто scope не дан), а через regex по транскрипту:


🛠️ 2. Pipeline (6 этапов, все идемпотентны)

# Скрипт Что делает
0 .env Bitrix webhook, STT key, OpenRouter key, мин. длительность
1 fetch_calls.py voximplant.statistic.get → фильтр whitelist → crm.activity.get + disk.file.get → mp3 + calls.csv
2 transcribe.py Whisper-large-v3 для каждого mp3 → transcripts/*.txt
3 pull_lead_outcomes.py Для каждого LEAD_ID из calls.csv — crm.lead.get + crm.deal.list?filter[LEAD_ID]=Xlead_outcomes.json
4 bundle.py (опц.) группирует все звонки одного клиента за день в markdown
5 analyze.py Применяет 3 LLM-промпта (QA, lost-deal, callback) через OpenRouter
6 build_dashboard.py HTML + per-operator markdown-фидбэк

Каждый шаг идемпотентный: если результат уже есть — пропускает. Можешь рестартовать пайплайн в любой момент после rate-limit, обрыва сети, нового аккаунта STT.


🧠 3. Три ключевых LLM-промпта

🆕 D. Passive aggression detector — отдельный сигнал

Стандартный QA по Калгари-Кембриджу ловит структуру звонка и формальные теги, но не видит скрытой агрессии при формальной вежливости. Эти моменты невидимо роняют LTV: клиент не жалуется (всё «по протоколу»), но больше не возвращается.

15+ типов маркеров: снисходительный_тон, менторские_нотки, передразнивание_клиента, тон_сэндвич («понимаю, но...»), псевдо_сочувствие, затянутая_пауза_после_вопроса, условное_с_оттенком («если уж совсем хотите»), и др.

JSON: intensity, markers[] (с цитатой + severity 1-5), overall_tone, client_reaction, recommendation.

Корнер-кейс — норма ≠ ПА: уверенный отказ от услуги или настойчивое объяснение тарифа — это норма. ПА начинается там, где появляется пренебрежение к контексту клиента или менторский режим («ну вы же должны понимать»).

Главный паттерн (виден на реальном проде): «невидимая» ПА — это не грубость, а устойчивая привычка переходить в менторский режим при сложных клиентах. Лидер этой проблемы — обычно самый формально-правильный оператор, потому что «правильный скрипт + усталость от глупых вопросов = менторские нотки».

Что даёт детектор: словарь-замена ~9 ключевых клише («я вам объясняю» → «давайте я по-другому скажу», «вы должны понимать» → «обычно бывает так-то») — это самая action-able точка приложения коучинга.

A. QA по Калгари-Кембриджу (медицинская модель консультации, адаптированная под телефон)

Структура: 5 последовательных этапов + 2 сквозные нити (отношения + структура диалога).

Этап Что проверяет
1. Установление контакта приветствие компании, представление оператора, имя клиента, причина
2. Сбор информации открытый вопрос → конкретика (что/куда/когда/как)
3. Уточнение и расчёт детали, специфика, форма оплаты, канал связи
4. Объяснение и планирование что произойдёт дальше (передача коллеге, ETA, кто перезвонит)
5. Закрытие резюме фактов, подтверждение клиентом, прощание
Нить: Отношения эмпатия, тон, не перебивает
Нить: Структура сигналит переходы, не теряет инициативу, обобщает по ходу

JSON-ответ: для каждого этапа — present + score 1-5 + tags + evidence (цитата ≤120 символов) + improvement (одна фраза в повелительном).

Корнер-кейсы (без них модель ошибается):

B. Lost-deals detector

Находит упущенные лиды — клиент был тёплый/горячий, но не конвертировался по вине оператора (а не потому что не наш профиль или техническая проблема).

JSON:

{
  "is_lost_deal": true,
  "confidence": 0.85,
  "client_warmth": "горячий|тёплый|холодный|не_клиент",
  "lost_reason": "<категория>",
  "operator_actions_wrong": ["конкретные ошибки"],
  "what_to_say_next_time": "альтернативная реплика в прямой речи",
  "evidence_quotes": ["до 2 цитат"]
}

Главные универсальные паттерны упуща (адаптируй категории под свой вертикал):

  1. «Оператор сам назвал цену» — клиент сразу сворачивается на «дорого». Правильно: «цену просчитает специалист, пришлёт в мессенджер».
  2. «Не закрыл звонок» — клиент сказал «спасибо, я перезвоню», оператор отпустил без следующего шага. Правильно: «давайте зафиксирую заявку, пришлём просчёт без обязательств».
  3. «Не предложил альтернативу» — услуга не подходит, оператор не дал вилку.
  4. «Не отработал возражение» — оператор САМ озвучил возражение, которого клиент не делал.
  5. «Грубый/снисходительный тон» — портит впечатление о бренде, даже если формально отказ корректный.

C. Callback catcher

Извлекает из транскрипта обещанные перезвоны, парсит deadline в ISO.

JSON:

{
  "callbacks": [{
    "promised_by": "оператор|клиент|<коллега>",
    "deadline_text": "как звучало",
    "deadline_parsed_iso": "...+03:00",
    "deadline_type": "конкретное_время|относительное|неопределённое",
    "topic": "о чём",
    "client_explicit_consent": true,
    "evidence_quote": "цитата"
  }],
  "has_pending_callback": true,
  "urgency": "критично|обычно|не_срочно|нет",
  "operator_action_required": true
}

Правила парсинга времени (anchor = CALL_START_DATE):

КРИТИЧНОЕ anti-false-positive правило: Дежурная фраза «передам коллеге, он свяжется с другого номера» БЕЗ конкретного времени и БЕЗ имени — НЕ фиксировать как callback. Иначе пайплайн зальёт оператора шумом (это ~80% звонков).


📊 4. Бенчмарк моделей (как выбирать)

Что мерить

Метрика Зачем
ok_rate % звонков с валидным JSON-ответом
latency_med / p95 UX оператора и стоимость окна
cost_per_1k бюджет на масштабе
verdict_match_rate (cross-model) согласованность нескольких моделей
score_MAE_vs_baseline точность относительно ручной экспертной разметки
tags_jaccard_mean пересечение бинарных тегов

Принципы выбора production-модели


🎓 5. Lessons learned (универсальные)

Архитектура и метрики

  1. Дубли ВАТС-интеграций реальны. Один и тот же звонок может прийти через две интеграции в Битрикс (тот же phone, CALL_START_DATE ± несколько секунд, разные REST_APP_NAME). Дедуп обязателен — иначе один оператор в дашборде получит двойной счёт.
  2. Знаменатель в процентах = «релевантная подвыборка», а не «все звонки оператора». Иначе получишь «скрипт сказан в 118% случаев». Например, тег «звонок с другого номера» считай только по первичкам, а не по всем звонкам.
  3. Группируй по ASSIGNED_BY_ID лида, а не по voximplant.PORTAL_USER_ID. Иначе сервисный аккаунт интеграции (Wazzup24, IVR-роутер, форма с сайта) появится как «фантомный оператор» с большим объёмом, который никогда не разговаривал. Имя оператора подтягивай regex'ом из транскрипта (см. раздел 1).
  4. Outcome лида — основа объективности. Без has_deal / deal_won ты оцениваешь стиль, а не результат. QA-score без outcome — это субъективщина.
  5. Команда — это N операторов, а не «один топ-исполнитель». Если ты видишь, что «один оператор работает на весь КЦ» — почти наверняка ты неправильно сгруппировал по PORTAL_USER_ID. Перепроверь атрибуцию через CRM (crm.lead.getASSIGNED_BY_ID).

Промпт-инжиниринг

  1. Калгари-Кембридж даёт структуру, а не галочки. Оператор может собрать 6 параметров, но если в случайном порядке — структура падает. Это лечится отдельной осью «structure» в промпте.
  2. Per-stage improvement лучше общего. «Подкрутить closing» полезнее чем «улучшить звонок».
  3. Корнер-кейсы в промпте — не опция. Без них модель штрафует за «не поздоровался» когда STT обрезал первую секунду.
  4. confidence 0-1 в JSON — must have. Низкий confidence — фильтр для дашборда (не показываем уверенно то, в чём модель не уверена).
  5. Цитаты ограничивай длиной (≤120 символов). Иначе модель вставит абзац и сломает UI.

Стоимость и стабильность LLM/STT

  1. Whisper-large-v3 на бесплатном tier большинства провайдеров имеет лимит секунд аудио/час. На ~200 звонков нужен либо часовой rest между батчами, либо платный tier.
  2. Reasoning-модели нужны reasoning.effort: low + max_tokens: 8000+. Иначе пустой ответ или обрыв на середине JSON.
  3. Бюджетные модели нестабильны на длинных промптах. Если промпт > 15K символов — % валидных JSON падает. Решение: либо ужать промпт, либо fallback на более крепкую модель при первом неудачном парсе.
  4. High recall / low precision — типовая болезнь дешёвых моделей на detection-задачах (callback, lost-deal). Если модель помечает 80% звонков как «требуют действия» — это шум. Используй ансамбль или строже формулируй anti-false-positive в промпте.
  5. «Vердикт средний» — статистический attractor многих моделей. Чтобы получить bimodal распределение (хороший vs плохой) — нужен либо ансамбль 2 моделей, либо ужесточение порогов в промпте, либо few-shot примеры краёв.

Безопасность и privacy

  1. mp3 — это PII. Не публиковать. Дашборд за SSO или хотя бы basic auth.
  2. Транскрипты для обучения / шеринга редактировать вручную или регекспами: имена/номера/адреса клиентов выкидывать или маскировать.

🚫 6. Red flags (что НЕ делать)

❌ Не делать ✅ Делать
Просить модель оценить «цену» или «работу других ролей» Только сам оператор и сам звонок
Считать в знаменателе все звонки Считать только «применимые» к данному тегу
Group by operator_name из транскрипта Group by PORTAL_USER_ID, name — display only
Штрафовать «не поздоровался» при STT-обрыве Поднять score этапа до 3 (floor)
Использовать temperature > 0 Всегда temperature: 0 для QA
Запускать на звонках < 15 сек Skip + помечать как мусор
Доверять одной модели на критичных решениях Бенч + ансамбль
Накапливать все теги в общий score Per-stage scores → суммировать в total
Сразу писать в Битрикс (создавать Activity по callback) Dry-run неделю → валидация супервайзером → потом продакшн

📁 7. Структура файлов

call_analytics/
├── .env
├── README.md
├── PLAYBOOK.md
├── scripts/
│   ├── common.py                  ← Bitrix REST helper + paths
│   ├── fetch_calls.py             ← тянет звонки → mp3
│   ├── transcribe.py              ← STT
│   ├── pull_lead_outcomes.py      ← lead status + deals
│   ├── bundle.py                  ← per-client markdown
│   ├── analyze.py                 ← LLM (day + bench mode, prompt-agnostic)
│   ├── bench_compare.py           ← сводка по моделям
│   ├── bench_vs_baseline.py       ← точность vs экспертная разметка
│   ├── insights.py                ← недельная аналитика
│   └── build_dashboard.py         ← HTML + per-operator feedback
├── prompts/
│   ├── qa_v3.md                   ← Калгари-Кембридж 5 этапов + 2 нити
│   ├── lost_deal_v1.md            ← детектор упущов
│   └── callback_catcher_v1.md     ← извлечение перезвонов
└── data/
    ├── lead_outcomes.json
    ├── bench_results.json
    └── YYYY-MM-DD/
        ├── calls.csv
        ├── raw/                   ← mp3
        ├── transcripts/           ← .txt + .json
        ├── bundles/               ← per-client md
        ├── qa_v3_results.json
        ├── lost_deal_results.json
        └── callback_results.json

🤖 8. Инструкция для AI-агента «построить такой же пайплайн на Битрикс24»

Если ты агент-разработчик и тебя попросили построить аналогичную систему:

  1. Стартуй с конфига:

  2. Реверс-инжиниринг записей:

  3. Реализуй pipeline в строгом порядке: fetch_callstranscribepull_lead_outcomesbundleanalyzebuild_dashboard. Каждый шаг идемпотентен (skip уже сделанного).

  4. Адаптируй промпты:

  5. Откалибруй вручную:

  6. Выбор production-модели:

  7. Дашборд:

  8. Что НЕ делать:


📈 9. Метрики успеха (что показывать в дашборде)

Метрика Норма Алерт
Pickup rate входящих ≥ 90% < 80%
Conversion LEAD → сделка ≥ 50% < 35%
Won rate (от лидов) ≥ 5% < 2%
Средний QA score (если max=35) ≥ 25 < 20
Доля красных флагов < 15% > 25%
Покрытие ключевых скриптовых тегов в первичках ≥ 70% < 40%
Lost-deals в день < 10% от первичек > 25%
Просроченных callbacks (после прохождения dry-run) 0 ≥ 1
Разрыв конверсии лидера vs аутсайдера < 1.3x > 1.5x (есть что раскатить)

🔌 9½. Какие ещё источники можно подключить

Базовый pipeline тянет данные из Битрикс24, но при желании можно обогатить:

Источник Что даёт Зачем
ВАТС API напрямую (Mango/Novofon/MTT/Beeline/UIS/Asterisk/Sipuni) mp3 + точное время начала разговора (после дозвона), статистика операторов Если CRM не прокидывает запись или нужно time_to_answer
WhatsApp/Telegram-роутеры (Wazzup24, Chat2Desk, Edna, Avtoeyes) текст переписки клиент↔︎оператор Multi-channel QA: оценивать не только звонки, но и чаты
Веб-аналитика (Я.Метрика / GA4) UTM-метки лида, путь клиента до звонка, страница входа Сегментировать качество звонков по источнику трафика
1С / финучёт реальная выручка/маржа по сделке через N дней QA по фактической прибыли, не только по факту сделки
DaData / Geocoder нормализация адресов, телефонов, ФИО Дедупликация лидов, валидация ввода оператора
Отраслевые базы (например, ATI для грузоперевозок, ЕГРЮЛ для B2B) бенчмарк цен, профиль клиента Сравнить цену оператора с рынком, скорить юр.лиц
Speech-to-Text с диаризацией (AssemblyAI, Deepgram, GigaAM) разделение реплик клиент↔︎оператор + интонации talk_time_share, перебивания, паузы — гораздо точнее QA
Корпоративный мессенджер супервайзера (TG/Slack) автоматический пуш разборов и упущенных лидов Коучинг не открывают сами — нужно «принести» оператору

Принцип: начинай с минимума (Bitrix24 + STT + один LLM) — это даёт 80% ценности. Остальное подключай только когда базовый цикл работает.


🏆 10. Best-practice mining: учись у лидера команды

Самая большая ценность QA-системы — не в наказании отстающих, а в извлечении приёмов лидера. Алгоритм:

  1. Определи лидера команды по 2-3 метрикам: конверсия LEAD→сделка, QA-score, lost-rate. Лучше брать оператора с самым высоким score при достаточном объёме (минимум 50-80 звонков).
  2. Отдели его 🏆 топ-звонки: первичная_квалификация + has_deal=true + QA-score выше медианы.
  3. Через Sonnet/Opus сравни 10 топ-звонков лидера с 5-6 типичными звонками остальных. Промпт для агента: «найди конкретные речевые приёмы, которые отличают». Не общие рекомендации, а прямые цитаты + call_id.
  4. Выпиши 5-7 шаблонов фраз (готовый раздаточный материал): название → когда применять → пример из реального звонка лидера → почему работает.
  5. Не идеализируй лидера — найди 2-3 паттерна, где у него тоже провалы. Это покажет границы метода и предотвратит «культ героя».
  6. Конкретные тренинг-задания для каждого отстающего на следующую неделю — связать с его персональным feedback.md.

Принцип: один и тот же скрипт у разных операторов даёт разницу 1.5-2x в конверсии. Дело не в скрипте, а в микро-приёмах — что отвечать на «дорого», как закрывать «я подумаю», как делать резюме в конце. Эти приёмы видны только при прямом сравнении транскриптов через LLM-агента.

⚠️ Не делать культ одного лидера

Даже у топ-оператора по конверсии есть слабые места. Если ты эталонизируешь только его — потеряешь сильные приёмы остальных. Алгоритм правильнее:

  1. Mining лучшего по каждой метрике отдельно. Лидер по конверсии может быть слаб в discovery; лидер по NPS — слаб в скорости; лидер по re-activation — слаб в первичках. Это разные люди.
  2. Mining лучшего по типу звонка отдельно. Звонок «квалификация холодного лида», «реактивация повторного», «отказ по тарифу», «B2B с ассистентом и ЛПР» — это разные сценарии. Один оператор не лучший во всех.
  3. 3 эталонных звонка по каждому сценарию (не один) — иначе модель команды копирует индивидуальный стиль, а не приём.
  4. Долгие отношения важнее одной сделки. Эталон — звонок, после которого клиент возвращается через год, даже если сейчас не купил. Это сжимает «жёсткие» приёмы закрытия (которые портят NPS).
  5. Сравнительные приёмы + методологии продаж как baseline: не ограничиваться внутренним лидером. Добавить проверенные ходы из NEPQ (neutral tonality, problem-finding), SPIN (situation→problem→implication→need-payoff), Sandler (up-front contract, снятие страха), Cialdini (reciprocity, social proof, commitment), HubSpot CONNECT, FORD framework. Это убирает «потолок» от уровня твоего лучшего оператора.

🎯 Универсальные best-practice паттерны (из реального mining'а лидера)

Это «общие» приёмы, повторяющиеся у топ-операторов в B2B и B2C независимо от вертикала. Адаптируй формулировки под свою терминологию (логист → менеджер/специалист).

  1. «Голое» приветствие за 1 такт + пауза. «Здравствуйте, компания [Имя], оператор [Имя]» → тишина. Без «слушаю вас», без длинных подводок. Клиент сам формулирует запрос в первые 3 секунды — ты получаешь «сырой» вход без подсказок.

  2. Якорь нижней цены ДО того, как клиент успел спросить. После сбора маршрута/задачи: «По вашему [параметру] расценки начинаются от [нижняя планка]». Перехватывает возражение «дорого» до его появления. Не работает, если назвать середину или верх вилки.

  3. Бинарный выбор по срочности вместо открытого «когда нужно». «Срочно сегодня-завтра или в течение двух недель?» Это дает клиенту субъектность («я выбрал»), а оператору — две заранее заготовленные ветки скрипта. Срочно = премия к цене, отложено = базовый тариф.

  4. Признать «обзвон конкурентов» — НЕ спорить. Когда клиент говорит «я просто узнаю цены / сравниваю»: «Я озвучила [якорь]. Точную цифру даёт [специалист] — он подберёт под вашу задачу. Готовы сегодня переговорить?» Не пытаться дожать на конкретику.

  5. Микро-close перед передачей: «Готовы сегодня с [специалистом] обсудить точную стоимость?» — устное «да» от клиента до фразы «передам коллеге». Cialdini commitment & consistency — резко повышает вероятность того, что клиент возьмёт трубку у специалиста.

  6. Поименное прощание + конкретный next step: «[Имя клиента], спасибо за обращение. [Специалист] передам, свяжется с вами с другого номера, подскажет [конкретику]. Хорошего дня». Имя берётся в первой минуте, не в конце. Двойная фиксация: «Зовут вас [Имя], правильно?»

  7. НИКОГДА не называть точную цену до передачи. На прямой натиск: «Минимум [нижняя планка]. Точно просчитывает [специалист] на конкретные параметры». Назвать середину вилки = потерять сделку: клиент уходит сравнивать.

Что лидер тоже делает неидеально (важно для калибровки модели):

Эти 3 паттерна стоит вынести как отдельные теги в QA-промпте для следующей итерации.


💡 11. Что промпт-инжиниринг показывает на проде

Полезные обобщения, наблюдаемые на реальных прогонах:


🚀 12. Какие подходы реально сработали (executive summary)

Короткий чек-лист подходов, которые показали лучший ROI в этой системе:

Подход Эффект
3 параллельных промпта (QA + lost-detector + callback) на одинаковых транскриптах вместо одного «универсального» промпта получаешь 3 разных сигнала: качество, риски, follow-up
Калгари-Кембридж как универсальный QA-каркас работает для B2B / B2C / медицины / страхования / ритейла без переработки
Атрибуция через ASSIGNED_BY лида, не PORTAL_USER_ID избавляет от фантомных «операторов-роутеров»
Per-stage / per-thread оценки вместо одной общей цифры — видно где именно проблема (закрытие vs контакт)
Best-practice mining через Sonnet/Opus, сравнение лидера с командой находит микро-приёмы, невидимые в формальном скрипте
Ансамбль моделей (дешёвая broad scan + дорогой verifier) для критичных решений избавляет от high-FP у Gemini в callback-detection
Идемпотентный pipeline (skip уже сделанного) можно прерывать на любом шаге, рестартовать после rate-limit
Per-operator weekly markdown для коучинга супервайзер копирует в TG/планёрку, не открывает дашборд
Dry-run неделю перед записью в CRM без него зальёшь Activity-шумом и сорвёшь доверие

Чего точно не делать:


📝 Лицензия и заметки

Делитесь свободно, ссылаться не обязательно. Промпты — адаптируйте под свой вертикал (грузоперевозки / медицина / страхование / ритейл / недвижимость — структура Калгари-Кембридж работает везде).

Если строишь похожее — пиши, обмениваемся опытом.