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

Контракты между треками

Контракты — это форматы данных, на которых встречаются три трека. Пока они не меняются, треки работают параллельно и не ждут друг друга.

Статус: определены в задаче T00, версия CONTRACT_VERSION = "1.0". Каждое решение (Decision) хранит версию контракта, по которой оно построено.

Где лежат

  • apps/api/contracts.py — Pydantic-модели. Это единственный источник правды; описание ниже только перечисляет, что там есть.
  • tests/fixtures/ — пример JSON для каждой модели. Трек 2 строит движок на этих примерах, трек 3 — экраны, ещё до того, как готов синтетический мир.
  • tests/test_contracts.py — проверяет, что каждый пример проходит валидацию, совпадает со своей сериализацией (нет лишних полей и молча добавленных значений по умолчанию) и согласован с остальными примерами.

Соглашения

  • Деньги — целые рубли (int).
  • Ставки, доли, PD и LGD — доли единицы: 0.209 означает 20,9%.
  • Даты и время — ISO 8601, месяцы — "YYYY-MM".
  • null значит «нет данных», а не ноль (SPEC §5.1). В Features все поля обязательны: если признак посчитать нельзя, производитель явно пишет null, и признак попадает в интервал «нет данных».
  • Все модели запрещают неизвестные поля: опечатка в имени поля — ошибка валидации, а не тихо потерянное значение.
  • Пороги (границы уровней качества данных, минимальный DSCR, таблица LGD, потолок ставки) — это кредитная политика, они лежат в config/: config/data_quality.json (T09), config/credit_policy.json (T16, лимит и ставка). Контракты переносят только посчитанные по ним значения и версию политики (policy_version).

Что входит

Контракт Кто производит → кто потребляет Модель Примеры
Заявка клиентский интерфейс (трек 3) → конвейер (трек 2) ApplicationRequest: цель, сумма, срок, когда нужны деньги, заявленная выручка, оборудование для инвестиционного кредита, согласия (БКИ 218-ФЗ по компании и собственнику, ПДн 152-ФЗ) application_request.json
Ответы источников мок-сервис apps/sources/ (трек 1, T06) → адаптеры и оркестратор apps/sources/enrichment.py (трек 1, T07) → досье (трек 3) SourceResponse — конверт вызова (статус, время запроса и ответа, задержка, число попыток, ошибка) и payload своего типа для каждого источника: AccountsPayload, ClientCardPayload, EgrulPayload (ЕГРЮЛ и ЕГРИП), FnsPayload (+ГИР БО для ООО, декларация для ИП), BkiPayload (отчёты по компании и собственнику), FsspPayload, ArbitrationPayload (арбитраж и Федресурс), AmlPayload (115-ФЗ). Тип ответа выбирается по полю source sources/*.json, в том числе fssp_timeout.json — недоступный источник после трёх попыток
Fact адаптеры, apps/sources/facts.py (трек 1, T07) → движок (трек 2) Название, заявленное значение, подтверждённое значение, источник, уровень надёжности, время получения; относительное расхождение заполняется само, если оба значения числовые. Один факт на пару «название — источник». Факты из загруженных клиентом выписок (T23) идут с источником statement_upload и уровнем client_document facts.json
DataQuality признаки, apps/api/features/data_quality.py (трек 1, T09; пороги — config/data_quality.json) → движок (трек 2), досье (трек 3) Четыре показателя со своими уровнями, итоговый уровень FULL / PARTIAL / THIN (модель проверяет, что он худший из четырёх), база для покрытия выручки (ФНС или заявленная), фраза для клиента data_quality_full.json, data_quality_thin.json
Features признаки (трек 1) → скоринг (трек 2) Признаки скоринговой карты из SPEC §8 с типами; запрошенной суммы среди них нет намеренно features.json
Файл скоринговой карты обучение (трек 1) → скоринг (трек 2) Scorecard — формат scorecard_vN.json: версия, происхождение (hand_set / trained), масштабирование (600 баллов = шансы 50:1, +20 баллов = шансы ×2), базовые баллы, интервалы и баллы по каждому признаку с обязательным интервалом «нет данных», границы грейдов A–E. Обученная карта — apps/ml/scorecard_v1.json (T10), как она читает признаки — apps/ml/score.py, применяет карту скоринговый модуль apps/api/scoring.py (T15) scorecard_example.json — пример формата, не рабочие веса
Decision движок (трек 2; лимит и ставка предложений — apps/api/limits.py, apps/api/pricing.py, T16; набор предложений и контрфактические подсказки — apps/api/offers.py, T17; маршрут и его причины — apps/api/routing.py, T18; собирает и хранит решение apps/api/workflow.py, T19; решение сотрудника — apps/api/staff_actions.py, T20; запрос выписок и пересчёт с ними — apps/api/statements.py, T23) → интерфейсы (трек 3) Статус (offered, declined, in_review, needs_data), matches_request, маршрут и его причины, причины решения, стоп-факторы, флаги, PD, баллы, грейд и вклады факторов, результат модели-претендента, качество данных, до четырёх предложений с расчётом лимита (все ограничения, определяющее отмечено) и ставки по составляющим, контрфактические подсказки, запрошенные документы, дата повторного обращения, версии модели и политики decision_offered.json, decision_in_review.json, decision_declined.json

Тестовая персона в примерах

Источники, заявка, факты, признаки, data_quality_full.json и decision_offered.json описывают одну синтетическую компанию — ООО «Зёрна», оптовую пекарню (ИНН 5406000001). Числа в примерах получены по формулам SPEC §5.1, §8 и §9, поэтому они согласованы между собой:

  • клиент просит 1 млн ₽ на 12 месяцев, CFADS около 109 тыс. ₽, грейд B, текущий платёж по кредиту 15 270 ₽;
  • лимит ограничен денежным потоком, поэтому предложений два: 1 млн ₽ на 17 месяцев или 730 тыс. ₽ на 12 месяцев;
  • контрфактическая подсказка: без текущего кредита на 12 месяцев было бы доступно 890 тыс. ₽ (движок T17 даёт 900 тыс. ₽, потому что пересчитывает ставку под новую сумму, apps/api/OFFERS.md).

decision_in_review.json — та же компания, но заявленная выручка расходится с данными ФНС больше чем на 30%. Это флаг, поэтому заявка уходит андеррайтеру, и расходы на обработку поднимают ставку. decision_declined.json, egrip_ip.json, fns_ip.json и data_quality_thin.json относятся к ИП Петровой с недавно открытым счётом (уровень THIN) и стоп-фактором по просрочке 90+ дней.

Примеры — не ожидаемые результаты движка. Это образец формата с правдоподобными числами. Эталонные исходы — у демо-персон из синтетического мира: apps/world/PERSONAS.md (T12); тесты по ним — T33.

Упрощения в контрактах

  • # MOCK: зона риска ЦБ «Знай своего клиента» в ответе 115-ФЗ и гарантия Корпорации МСП как вид обеспечения — моки. В реальности это отдельные интеграции со своими форматами.
  • # SIMPLIFICATION: ответы источников урезаны до полей, которые нужны конвейеру (SPEC §5). Реальные ответы ЕГРЮЛ, ФНС и БКИ намного шире.

Как менять контракт

  1. Отдельный PR, который меняет contracts.py и соответствующие примеры в tests/fixtures/.
  2. Тесты проходят.
  3. PR одобряют все три программиста.
  4. Если изменение ломает совместимость, поднимается CONTRACT_VERSION, а в журнал решений в TASKS.md добавляется строка.