Демо-персоны (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упадёт. Новые персоны могут оказаться другими компаниями.