ПТИЦА: основной источник скорости продаж (objective) не выбирался ни разу — порог 50%, максимум покрытия 23.7% #2967

Open
opened 2026-08-20 09:53:01 +00:00 by bot-backend · 0 comments
Collaborator

Факт

За последние 120 суток в analysis_runs поле velocity_source принимает только два значения:

none                1313
rosreestr_fallback   748
objective              0     ← ни разу

objective объявлен основным источником (velocity.py:130, docstring compute_velocity: «1. Objective… 3. Если Objective coverage < 50% конкурентов → rosreestr_fallback»), но не выбирался никогда.

Почему

Гейт: _OBJECTIVE_COVERAGE_MIN_RATIO = 0.50 (velocity.py:90), сравнивается с долей конкурентов, у которых есть маппинг и данные.

Замер этого же показателя по тем же разборам (objective_coverage_pct в payload):

разборов 1547
среднее покрытие 17.2 %
максимум за 120 суток 23.7 %
разборов с покрытием ≥ 50 % 0

Максимум за четыре месяца — меньше половины порога. То есть ветка недостижима не «редко», а структурно.

Причина покрытия видна в самом запросе: CTE mapped (velocity.py:284-290) берёт только явный objective_complex_mapping, да ещё с confidence-гейтом. Явный маппинг покрывает 308 объектов из 1556 — 19.8 %, что совпадает с наблюдаемыми 17.2 %.

Чем это оплачивается

Все показатели скорости в отчёте приходят из rosreestr_fallback, а он фабрикует объём:

proxy_used=True,  # объём = deal_count × avg_area_per_deal (фабрикуется)

и не даёт разбивку по комнатности (by_room_bucket={}). Плюс, по замечанию аудита #2445 A4, нормирующая городская медиана может быть частично посчитана по таким же count×45-строкам.

То есть отчёт показывает скорость, посчитанную по прокси, при том что реальные данные Объектива в базе есть (85 653 строки в objective_corpus_room_month, 65 532 в окне 24 месяцев).

Что НЕ является решением

Просто понизить порог — нет. Порог отделяет «данных достаточно, чтобы говорить о районе» от «данных на пару конкурентов». При 17 % покрытия ветка objective давала бы величину по одной шестой выборки и выдавала бы её за измеренную.

Настоящее узкое место — покрытие маппинга. Смежное: #2962 (спатиально-именной gap-fill сломан — тянет чужие ЖК). Но даже если его починить и включить сюда, покрытие станет (308+185)/1556 = 31.7 % — всё ещё ниже порога.

Что предлагаю решить владельцу

  1. Расширить явный маппинг. Он корректен (мост по project_name), но покрывает пятую часть. 300 из 308 строк уже отревьюены — процесс есть, нужен объём.
  2. Либо признать fallback основным путём и убрать из кода/доков описание objective как «основного» — сейчас это утверждение расходится с фактом на 100 % прогонов.

Критерий приёмки

Хотя бы один разбор с velocity_source='objective'. Сегодня их ноль за 120 суток.

Оговорка

Мерил analysis_runs за 120 дней (1547 разборов с полем покрытия). Более ранние прогоны не проверял — возможно, порог достигался при другом составе маппинга. Утверждение «никогда» относится к измеренному окну.

## Факт За последние 120 суток в `analysis_runs` поле `velocity_source` принимает **только два** значения: ``` none 1313 rosreestr_fallback 748 objective 0 ← ни разу ``` `objective` объявлен основным источником (`velocity.py:130`, docstring `compute_velocity`: «1. Objective… 3. Если Objective coverage < 50% конкурентов → rosreestr_fallback»), но не выбирался никогда. ## Почему Гейт: `_OBJECTIVE_COVERAGE_MIN_RATIO = 0.50` (`velocity.py:90`), сравнивается с долей конкурентов, у которых есть маппинг **и** данные. Замер этого же показателя по тем же разборам (`objective_coverage_pct` в payload): | | | |---|---| | разборов | 1547 | | среднее покрытие | **17.2 %** | | **максимум за 120 суток** | **23.7 %** | | разборов с покрытием ≥ 50 % | **0** | Максимум за четыре месяца — меньше половины порога. То есть ветка недостижима не «редко», а структурно. Причина покрытия видна в самом запросе: CTE `mapped` (`velocity.py:284-290`) берёт **только явный** `objective_complex_mapping`, да ещё с confidence-гейтом. Явный маппинг покрывает 308 объектов из 1556 — 19.8 %, что совпадает с наблюдаемыми 17.2 %. ## Чем это оплачивается Все показатели скорости в отчёте приходят из `rosreestr_fallback`, а он **фабрикует объём**: ```python proxy_used=True, # объём = deal_count × avg_area_per_deal (фабрикуется) ``` и не даёт разбивку по комнатности (`by_room_bucket={}`). Плюс, по замечанию аудита #2445 A4, нормирующая городская медиана может быть частично посчитана по таким же count×45-строкам. То есть отчёт показывает скорость, посчитанную по прокси, при том что реальные данные Объектива в базе есть (85 653 строки в `objective_corpus_room_month`, 65 532 в окне 24 месяцев). ## Что НЕ является решением Просто понизить порог — нет. Порог отделяет «данных достаточно, чтобы говорить о районе» от «данных на пару конкурентов». При 17 % покрытия ветка objective давала бы величину по одной шестой выборки и выдавала бы её за измеренную. Настоящее узкое место — покрытие маппинга. Смежное: #2962 (спатиально-именной gap-fill сломан — тянет чужие ЖК). Но даже если его починить и включить сюда, покрытие станет (308+185)/1556 = 31.7 % — всё ещё ниже порога. ## Что предлагаю решить владельцу 1. **Расширить явный маппинг.** Он корректен (мост по `project_name`), но покрывает пятую часть. 300 из 308 строк уже отревьюены — процесс есть, нужен объём. 2. **Либо признать fallback основным путём** и убрать из кода/доков описание objective как «основного» — сейчас это утверждение расходится с фактом на 100 % прогонов. ## Критерий приёмки Хотя бы один разбор с `velocity_source='objective'`. Сегодня их ноль за 120 суток. ## Оговорка Мерил `analysis_runs` за 120 дней (1547 разборов с полем покрытия). Более ранние прогоны не проверял — возможно, порог достигался при другом составе маппинга. Утверждение «никогда» относится к измеренному окну.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2967
No description provided.