Решение владельца: общий Redis-кэш карточек 2ГИС/Яндекс на 5 дней, единый для шага 1 (поиск) и шага 2 (изучение) и повторных прогонов. Кэшируем ТОЛЬКО карточки (/firm/, /maps/org/); поиск (/search/) и ленты Яндекса — всегда свежие (добывание новых фирм не ограничиваем). Кэш ограничивает свежесть только у уже найденных карточек — редко меняются. Замер на САПС (счётчик скачиваний): 12 повторных открытий / ~54с внутри одного сбора (карточка Метрополиса тянулась 3× — ingest+поиск+разворот); повторный сбор в течение 5 дней даёт ~200с+ и резкое падение нагрузки на 2ГИС/Яндекс/прокси. План реализации — через TDD за флагом AUTOPODBOR_RENDER_CACHE (ВЫКЛ по умолчанию), TTL AUTOPODBOR_RENDER_CACHE_TTL_DAYS=5. Плюс промт-хендофф для следующей сессии. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
12 KiB
Автоподбор: кэш отрисованных карточек (2ГИС/Яндекс) — дизайн + хендофф
Дата: 2026-07-06 · Ветка: worktree-avtopodbor (НЕ прод) · Статус: решение принято, НЕ реализовано.
0. Где мы (контекст сессии 06.07)
Ветка worktree-avtopodbor (воркстри: .claude/worktrees/avtopodbor). Сегодня закоммичено
(0131ecfc, НЕ запушено):
- Качество шага 2 (якорь по данным клиента). Привязанные клиентом карточки 2ГИС/Яндекс
используются как «земля» и якорь холдинга; слепой поиск по имени берётся только при наличии якоря;
AnchorGateпустой якорь → drop. Чужие фирмы по широкому имени («Притяжение» → стоматология, салоны) больше НЕ попадают в карточку. Доказано на САПС (comp 743): свежий сбор — 0 салонов. Спека:docs/superpowers/specs/2026-07-06-avtopodbor-site-anchor-search-fix.md. - Скорость: параллельный разворот за флагом
AUTOPODBOR_PHONE_REVERSE_PARALLEL(сейчас ВКЛ в dev.env). 2ГИС- и Яндекс-разворот идут разом (max, не сумма). Сверка: данные один в один, потерь нет. Таймаут подпроцесса 600с (драйверprocessиначе берёт дефолт 60с и убивает разворот).ParallelPhoneReverse+CompositePhoneReverse(последовательный, для отката).
Флаги в dev .env: deep_study, self_render, phone_reverse, phone_reverse_yandex, phone_reverse_parallel — все ВКЛ.
Тесты Autopodbor: 464/464. Виртуалка рендера: 51.250.1.97 / https://51-250-1-97.sslip.io/render
(секреты — в gitignored .env, В ГИТ НЕ ВЫНОСИТЬ).
🔴 Правило прокси (доказано): несколько ПАРАЛЛЕЛЬНЫХ лент Яндекса через один прокси теряют
данные (44→30). 2ГИС-initialState пачкой — безопасно. Виртуалка держит глобальный потолок
MAX_CONCURRENCY (8). Яндекс-разворот НЕ параллелить внутри без 2-го прокси-IP.
1. Решение владельца: кэш отрисованных карточек
Задача: снизить нагрузку на 2ГИС/Яндекс/прокси и ускорить, кэшируя отрисованные карточки.
- TTL — до 5 дней (настраиваемый). Обоснование владельца: поле и карточки конкурентов меняются редко («раз в квартал кто-то что-то поменяет»), поэтому 5 дней — с запасом, свежесть не страдает.
- Общий кэш для шага 1 (поиск) и шага 2 (изучение) и для повторных прогонов. Карточка, отрисованная на шаге 1, на шаге 2 берётся из кэша. Ключ — по URL, хранилище Redis (общий между процессами/прогонами).
- Кэш ограничивает свежесть ТОЛЬКО у «уже найденного» (контент карточек), НЕ у поиска новых. Это ключевое уточнение владельца.
Что кэшируем, что нет
| Запрос | Кэш? | Почему |
|---|---|---|
Карточка 2ГИС …/firm/{id} |
✅ 5 дней | «уже найденное», телефоны/сайт стабильны |
Карточка Яндекс …/maps/org/…/{id} |
✅ 5 дней | то же |
Поиск 2ГИС …/search/… |
❌ всегда свежий | ДОБЫВАНИЕ новых фирм — кэш убил бы находки |
Лента Яндекса (yandex-list) |
❌ всегда свежий | добывание; идёт отдельным путём (RenderServiceYandexListSource) |
| Сайт конкурента (CurlPlaywrightFetcher) | ⏸ follow-up | отдельный интерфейс Fetcher, не BatchPageFetcher; можно добавить потом |
Итог для повторного сбора (в течение 5 дней): поиск идёт ЗАНОВО → новые фирмы находятся; известные карточки — из кэша, мгновенно, без обращений к 2ГИС/Яндекс. Ограничение свежести — только у контента уже найденных карточек. Явная «Собрать ещё раз» тоже из кэша (вариант B) — свежесть добывания обеспечивает свежий поиск, а не сброс кэша.
2. Доказательство экономии (замер 06.07, САПС, счётчик скачиваний)
Прогон САПС (3 элемента, последовательно, 670с), обёртка-счётчик над фетчером:
Всего скачиваний карточек/страниц: 57
Уникальных URL: 45
ПОВТОРНЫХ открытий: 12 (~54с впустую)
Топ повторов (подтверждает код):
- Карточка Метрополиса
/firm/70000001045067758— 3× (ingest + поиск + разворот). - Яндекс-карточки Метрополиса/Новой панорамы/Слободы/Притяжения — по 2-3×.
- (Поиск «Слобода Весны» гонялся 4× — это цикл-ретрай пустого поиска в
collect2gis, НЕ кэш-цель.)
Экономия:
- Внутри одного сбора: ~45-54с (повторы карточек
/firm/,/maps/org/). - Повторный сбор / шаг 2 после шага 1 в течение 5 дней: почти все ~45 карточек из кэша → ~200с+ и резкое падение обращений к внешним сервисам. Здесь главный выигрыш.
Причина повторов (по коду): в DeepStudyCollector::collect() фазы ingestClientCards / collect2gis
/ collectYandex / разворот тянут независимо; SelfRenderClient дедупит только внутри одного
htmlBatch-вызова, между фазами — нет. Плюс шаг 1 и шаг 2 не делят кэш вообще.
3. План реализации (через TDD, за флагом)
CachingPageFetcher(новый,app/app/Services/Autopodbor/Agent/Fetch/) — декораторimplements BatchPageFetcher, оборачивает реальный фетчер (SelfRenderClientИЛИXfetchClient):cacheable($url)=str_contains($url,'/firm/') || str_contains($url,'/maps/org/'); НЕ/search/.html($url): если cacheable →Cache::get(key); хит вернуть; промах →inner->html, если непустоCache::put(key, $v, ttl). Не-cacheable → passthrough. Пустое НЕ кэшируем.htmlBatch($urls): cacheable-URL проверить в кэше по одному; промахи добратьinner->htmlBatch; непустое положить; не-cacheable — passthrough. Вернуть map url→content.- Ключ:
'autopodbor:render:'.sha1($url). Хранилище — дефолтный Cache (Redis на dev/prod). - TTL:
config('autopodbor.render_cache_ttl_days', 5)дней.
- Флаг:
config/autopodbor.php→'render_cache' => env('AUTOPODBOR_RENDER_CACHE', false)+'render_cache_ttl_days' => env('AUTOPODBOR_RENDER_CACHE_TTL_DAYS', 5). По умолчанию ВЫКЛ. - Вайринг в
AutopodborServiceProvider: когдаrender_cacheВКЛ — обернуть фетчерCachingPageFetcher-ом ОДИН раз и использовать в ОБОИХ местах:- шаг 1
buildLiveFind:$catPages/$enrichPages(сейчас$srилиLivePageFetcher/RoutingBatchFetcher); - шаг 2:
$pages(deep collector), разворотbuildPhoneReverse($pages+ яндекс card reader). Так как ключ по URL в Redis — даже разные экземпляры декоратора делят один кэш. Проще всего обернуть$sr/$xfetchв самом верхуregister()и дальше передавать обёрнутый. ⚠️ RoutingBatchFetcher/яндекс-Playwright и RenderServiceYandexListSource (ленты) — НЕ кэшировать (это/search/-путь и ленты). Оборачивать только загрузчик КАРТОЧЕК.
- шаг 1
- Тесты (TDD):
CachingPageFetcher:- cacheable карточка: 2-й вызов — из кэша (inner дёрнут 1 раз);
/search/: всегда passthrough (inner каждый раз);- пустой ответ: НЕ кэшируется (inner дёрнут снова);
htmlBatch: часть из кэша, часть добирается. ИспользоватьCache::store('array')илиCache::spy()+ фейковый inner-фетчер (спай вызовов).
- Регресс: весь Autopodbor-набор зелёный. Pint. Не коммитить диагностику/
.env/чужое.
4. Ограничения / грабли
- Пустое не кэшировать — иначе разовый сбой отравит кэш на 5 дней.
- Кэшировать только карточки (
/firm/,/maps/org/), НЕ поиск — иначе новые фирмы не найдутся. - Ключ строго по нормализованному URL (как приходит в фетчер). 2ГИС карточка → initialState-JSON, Яндекс карточка → HTML; тип неявно определяется URL, отдельный ключ по типу не нужен.
- Redis на dev включён (проект использует Redis 7). Проверить
config('cache.default'). - Флаг ВЫКЛ по умолчанию; откат — флаг в false.
5. Окружение (для следующей сессии)
- Воркстри:
c:/моя/проекты/портал crm/Документация/.claude/worktrees/avtopodbor(ветка worktree-avtopodbor). Правки — ТОЛЬКО тут. - PHP:
C:/tools/php83/php.exe. Тесты:php artisan test tests/Unit/Autopodbor tests/Feature/Autopodbor(последовательно). - Дев-БД
liderra(postgres@127.0.0.1:5432). Тесты —liderra_testing. НЕ прод. - Study-воркер запущен:
queue:work --queue=autopodbor-study(перезапускать после правки кода — держит бут в памяти). - Коммит только по «эскейп»/«коммит», по явным путям. Диагностика (
scripts/*,_poll35.php),.env, чужие файлы (XfetchClient*,2026-07-05-...session-status.md) — НЕ коммитить. - Прод liderra.ru — не трогать без разрешения владельца.