Перейти к содержанию

Демо-персоны (T12)

Демо-персоны — компании синтетического мира, по одной на каждый исход, который показывает демо (SPEC §14): автоматическое одобрение для ООО и ИП, набор предложений вместо запрошенного, залог оборудования, андеррайтер, комитет, отказ по стоп-фактору и по экономике, два мошеннических архетипа, клиенты PARTIAL и THIN с выпиской из другого банка, недоступный источник и неизвестный ИНН.

Ожидаемые исходы в таблице ниже никто не писал вручную. Каждую персону провёл настоящий конвейер: мок-источники → качество данных → стоп-факторы → скоринг → предложения → маршрут, а для персон с выпиской ещё и пересчёт после загрузки (T23). Таблицу ещё должен подтвердить экономический блок (things_for_the_econ_team_todo.md, раздел T12).

Файлы

Файл Что в нём
apps/world/personas.py Цели персон и отбор: кто может играть персону, какую заявку она подаёт и какое решение должен принять конвейер
apps/world/demo/personas.json Персоны: ИНН, заявка (ApplicationRequest без application_id и submitted_at), сценарий сбоев источников, ожидаемое решение до и после выписки, предодобрение овердрафта
apps/world/demo/statements/*.json Выписки из другого банка для персон PARTIAL и THIN, в формате загрузки T23 (apps/api/STATEMENTS.md)
apps/world/statements.py Как мир строит такую выписку
tests/test_world_personas.py Файл персон совпадает с тем, что конвейер решает сейчас, и покрывает все исходы
tests/test_golden_personas.py Золотые тесты (T33): каждая персона через API и воркер получает решение из таблицы, в том числе после выписки и запроса андеррайтера; сквозной тест от заявки до выдачи

Пересоздать персоны и выписки:

python -m apps.world.personas

Отбор занимает около 20 секунд. Он работает на мире по умолчанию: зерно 42, 5000 компаний, срез 2026-10-01. На этом же мире работает демо: WORLD_* в .env пустые.

Как выбирается персона

  • Кандидаты перебираются в порядке мира (C00000, C00001, …), и персону получает первая компания, решение по которой проходит проверку цели. Каждая компания играет не больше одной персоны. Поэтому при одних и тех же мире, карте и config/ персоны всегда одни и те же.
  • Заявка строится из данных компании: оборотный кредит на 12 месяцев на 5% её выручки, заявленная выручка — declared_annual_revenue из мира, все согласия, которые нужны её правовой форме. Персоны с оборудованием покупают его на 10% (или 20%) выручки на 36 месяцев с первоначальным взносом 30%: залог покрывает до 70% цены (T16).
  • Решение считается на дату среза мира, а предложения датируются днём заявки — 2026-10-24 (DECIDED_AT). Поэтому в файле нет дат действия предложений: в живом демо они отсчитываются от реального дня.
  • Персоны одобрения выбираются только среди тех, чьё лучшее предложение не меньше 300 тыс. ₽. Иначе на многих компаниях демо показало бы минимальную сумму продукта (см. «Ограничения»).
  • # SIMPLIFICATION: отбор читает скрытый архетип компании (T04), чтобы найти «техническую» компанию, «завышателя» и клиентов с неполными данными. Больше ничего из скрытой правды (q, PD, исход дефолта) он не читает. Архетип записан в personas.json только для сценария демо: движок и интерфейс его не видят, флаги находят сами. В настоящем банке персонами были бы обезличенные реальные кейсы, отобранные риск-менеджером.

Ожидаемые исходы

Мир: зерно 42, 5000 компаний, срез 2026-10-01, генератор world-t12-1. Карта — scorecard_v1, политики — credit_policy_v0, data_quality_v0, features_v0. Ставки округлены до сотых процента.

Персона Компания Заявка Решение Маршрут и причины Предложения
auto_ooo — ООО, автоматическое одобрение C00191, ООО, ИНН 6163001918 Оборотный кредит 3 450 000 ₽ на 12 мес. offered, грейд A, FULL Автоматически (route_auto) Как просили: 3 450 000 ₽, 12 мес., 19,32%. Предодобренный овердрафт до 2 820 000 ₽ под 19,38% (ограничение по обороту)
auto_ip — ИП, автоматическое одобрение C00714, ИП, ИНН 165500071419 Оборотный кредит 950 000 ₽ на 12 мес. offered, грейд A, FULL Автоматически Как просили: 950 000 ₽, 12 мес., 20,09%
counter_offer — набор предложений вместо запрошенного C00055, ИП, ИНН 631700005555 Оборотный кредит 3 000 000 ₽ на 12 мес. offered, грейд B, FULL, не как просили Автоматически; лимит по денежному потоку Максимальная сумма: 670 000 ₽, 12 мес., 21,74%
equipment — оборудование в залог C00071, ООО, ИНН 7715000717 Инвестиционный кредит 1 610 000 ₽ на 36 мес., оборудование 2 300 000 ₽ offered, грейд A, FULL Автоматически Как просили: 1 610 000 ₽, 36 мес., 19,41%, залог оборудования и поручительство собственника
underwriter — андеррайтер, грейд D–E C00009, ИП, ИНН 770300000904 Инвестиционный кредит 560 000 ₽ на 36 мес. in_review, грейд E, FULL Андеррайтер: грейд вне A–C (route_grade), флаг концентрации на крупнейшем покупателе Как просили: 560 000 ₽, 36 мес., 25,85%, залог оборудования
committee — кредитный комитет C00005, ООО, ИНН 3664000050 Инвестиционный кредит 5 600 000 ₽ на 36 мес. in_review, грейд D, FULL Комитет: грейд D и сумма больше 5 млн ₽ (route_grade_over_5m), сумма больше 5 млн ₽ Нет: лимит по денежному потоку ниже минимума продукта
stop_factor — отказ по стоп-фактору C00142, ИП, ИНН 772700014292 Оборотный кредит 650 000 ₽ declined, без скоринга Стоп-фактор: просрочка 90+ дней за 24 месяца (bki_overdue_90_24m) Нет
economic_decline — бизнес не тянет платёж C00002, ООО, ИНН 5074000029 Оборотный кредит 1 750 000 ₽ declined, грейд C, FULL Автоматически; лимит по денежному потоку ниже минимума продукта, есть дата повторной заявки Нет
shell — «техническая» компания C00090, ООО, ИНН 5074000903 Оборотный кредит 550 000 ₽ in_review, грейд E, FULL Андеррайтер: грейд, флаги транзитных операций по 115-ФЗ, доли снятия наличных, смены директора или собственника, концентрации на покупателе Нет
inflater — завышенная выручка C03248, ИП, ИНН 616400324807 Оборотный кредит 1 100 000 ₽, заявлено 41 млн ₽ выручки in_review, грейд B, FULL Андеррайтер: заявленная выручка расходится с ФНС больше чем на 30% Максимальная сумма: 570 000 ₽, 12 мес., 27,79% (лимит по выручке ФНС, а не по заявленной)
partial_statement — основной счёт в другом банке C03041, ООО, ИНН 2312030413 Оборотный кредит 3 000 000 ₽ in_review, грейд B, PARTIAL Андеррайтер: PARTIAL и сумма больше 1 млн ₽ Нет. После выписки: FULL, андеррайтер (route_statement_upload), максимальная сумма 720 000 ₽, 12 мес., 26,78%
thin_statement — недавно открытый счёт C01963, ООО, ИНН 6670019632 Оборотный кредит 1 150 000 ₽ needs_data, грейд A, THIN, запрошена выписка Андеррайтер после ответа клиента (route_data_thin) Нет. После выписки: FULL, андеррайтер (route_statement_upload), максимальная сумма 450 000 ₽, 12 мес., 29,16%
source_down — источник недоступен C00298, ООО, ИНН 2309002984 Оборотный кредит 600 000 ₽; БКИ отвечает ошибкой in_review, без скоринга Андеррайтер: обязательный источник недоступен (route_source_unavailable). Без сбоя та же заявка одобряется автоматически Нет
not_found — компания не найдена —, ИНН 7703999999 Оборотный кредит 500 000 ₽ declined company_not_found сразу после ЕГРЮЛ Нет

Полные коды причин, флагов и предложений — в apps/world/demo/personas.json.

Золотые тесты (T33)

Таблицу проверяют два теста. tests/test_world_personas.py вызывает decide напрямую, без базы и воркера. tests/test_golden_personas.py проводит каждую персону так же, как демо:

  • клиент подаёт заявку через POST /api/v2/applications, воркер её решает, решение сотрудника сравнивается с expected (коды, грейд, суммы, сроки, ставки; даты не сравниваются);
  • персона THIN загружает выписку в ответ на needs_data, персоне PARTIAL выписку запрашивает андеррайтер (request_data); после пересчёта решение сравнивается с expected_after_statement;
  • сквозной тест: auto_ooo проходит путь «заявка → предложение → подписание кодом → условия → выдача», а андеррайтер одобряет заявку персоны underwriter.

Мир по умолчанию строится один раз на модуль; сбой БКИ персоны source_down задаётся тем же сценарием мок-источников, что и в демо. Персона not_found подаётся не из демо-профиля: демо-каталог (apps/api/demo_companies.py) предлагает только компании мира, поэтому в демо этот исход сейчас не показать.

python -m pytest tests/test_golden_personas.py -q

Сбои источников и выписки в демо

  • Сбой БКИ у персоны source_down задаётся переменной MOCK_SOURCES_SCENARIOS (apps/sources/README.md): {"2309002984": {"bki": "error"}}. Мир при этом не меняется. Ошибка, а не тайм-аут: тайм-аут держит каждую попытку MOCK_SOURCES_HANG_SECONDS секунд, и демо стоит.
  • Выписку персоны PARTIAL загружают после запроса андеррайтера (request_data, T20), выписку персоны THIN — сразу в ответ на needs_data (T23). Файл — apps/world/demo/statements/<персона>.json.

Выписки из другого банка

Только мир знает, что идёт через другие банки, поэтому выписку строит он (apps/world/statements.py), по скрытому профилю счёта компании (account_profile, T04) и в своём потоке случайных чисел statement:

  • основной счёт в другом банке (main_bank_elsewhere): оставшиеся 1 − our_share безналичного бизнеса за те же 24 месяца. Кредиты компания платит с нашего счёта (T02), поэтому платежей по кредитам в выписке нет;
  • недавно открытый счёт (new_account): прежний счёт до открытия нашего, а дальше — месяцы без оборотов до последнего полного месяца. Иначе выписка была бы старше трёх месяцев и проверка T23 её бы не учла.

Остальным архетипам выписка не нужна: у обычного клиента всё у нас, а свои переводы own_transfers приходят с личных счетов собственника, а не с расчётного счёта бизнеса.

Упрощения:

  • # SIMPLIFICATION: у всех выписок один выдуманный банк — «Северная звезда»;
  • # SIMPLIFICATION: клиент с недавно открытым счётом перевёл к нам весь бизнес сразу, а прежний счёт остался открытым без оборотов. В жизни часть платежей ещё какое-то время шла бы через старый банк;
  • безналичная доля выручки в выписке разыгрывается заново, поэтому вместе с нашим счётом выписка даёт выручку, близкую к настоящей, но не равную ей.

Ограничения

  • Грейды D и E без залога не получают ни одного предложения. При зерне 42 на заявке 100 тыс. ₽ предложения есть у 51% компаний грейда A, 30% — B и 17% — C, у грейдов D и E — ни у одной из 1392: тонкая маржа мира (apps/api/LIMITS.md, «Что видно на синтетическом мире») и повышенный DSCR этих грейдов дают нулевой лимит по денежному потоку. Поэтому персона андеррайтера покупает оборудование: предложение ей даёт залог.
  • Комитет с предложением на этом мире недостижим. Грейдам A–C комитет нужен только при сумме больше 15 млн ₽, а это выше максимума любого продукта; у грейдов D–E на сумму больше 5 млн ₽ лимит нулевой. Поэтому у персоны committee предложений нет, и две подписи (T20) на ней показать нельзя: отказ и запрос данных подписывает один сотрудник. Вопрос передан экономическому блоку.
  • Персоны зависят от карты scorecard_v1, мира и config/. После переобучения карты или любой правки мира или политик их надо пересоздать (python -m apps.world.personas), иначе test_persona_file_is_up_to_date упадёт. Новые персоны могут оказаться другими компаниями.