docs(автоподбор): файл состояния движка + промт восстановления контекста (01.07)

Единая точка: что сделано (движок шага 1 пересобран — АНАЛИЗ→канал А 2ГИС(xfetch)+Яндекс
(Playwright)→слияние→канал В федералы(EXA)→сборка; 136 unit+74 feature зелёные, HEAD c3f0f272),
что осталось (полный живой прогон+сверка на 72, калибровка, параллельность), ключи/окружение,
карта кода, промт для восстановления после компакта.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Дмитрий
2026-07-01 04:55:51 +03:00
parent c3f0f272e3
commit fb3027848a
@@ -0,0 +1,131 @@
# 🔧 ДВИЖОК «Автоподбор» — СОСТОЯНИЕ + ПРОМТ ПРОДОЛЖЕНИЯ (01.07.2026)
> Единый файл-состояние: что сделано, что осталось, что прочитать для восстановления контекста.
> Воркстри `worktree-avtopodbor`. **НЕ прод. Коммит — спросив «эскейп». Пуш — gitea. Простым языком.**
---
## 0. ОДНОЙ СТРОКОЙ
Движок шага 1 (поиск конкурентов) **пересобран под финал v4 целиком** и офлайн-зелёный (136 unit +
74 feature). Осталось — **полный живой прогон 2ГИС+Яндекс+В со сверкой на эталон 72** и калибровка.
---
## 1. ✅ ЧТО СДЕЛАНО (всё на gitea, ветка `worktree-avtopodbor`, HEAD **dee2ebbc**)
Финальный поток шага 1 (ZAFIKSIROVANO §0-БИС/§0-ТРИ): **ТОЛЬКО имя+сайт+карточки** (телефоны/описание = шаг 2).
- **Шаг АНАЛИЗ** — `ChannelA\AitunnelQueryAnalyzer`: описание клиента → запросы-рубрики (мелкая модель).
- **Канал А, справочник 1 — 2ГИС (xfetch):** `ChannelA\CategoryScraper` + `CategoryListingParser`
скрейп категории с пагинацией, из СПИСКА берём имя+карточку+**сайт** (сайт в редиректе «Перейти на
сайт», firmId в пути; мусор/агрегаторы отсеяны). БЕЗ захода в карточку.
Живой замер: **48 фирм, 17 с сайтом, ~275с** (xfetch render-bound).
- **Канал А, справочник 2 — Яндекс (локальный Playwright):** `ChannelA\YandexDirectory` (граница) +
`PlaywrightYandexDirectory` + `scripts/render-yandex-list.cjs` (скроллит ленту). Имя+карточка, БЕЗ
сайта (в списке Яндекса сайта нет — только на карточке, не открываем). Firecrawl Яндекс НЕ берёт (§12.2).
Живой замер: **113 организаций за ~18с** (быстро).
- **Слияние 2ГИС+Яндекс:** `AutopodborDedup::mergeCompetitors` (union-find по имени/домену) — один
конкурент из обоих справочников → одна карточка с ДВУМЯ `directory_urls` (2ГИС+Яндекс), сайт из 2ГИС.
- **Канал В (федералы) — одна модель × 2 прохода:** `ChannelB\{ChannelBSearch, AitunnelResearcher,
ResearcherParser}` — sonar-reasoning-pro даёт ТОЛЬКО ИМЕНА (промт §11.3), стоп-лист = имена из А +
примеры. Сайт федерала — `ChannelB\ExaSiteFinder` (EXA по имени; у федерала нет карточки на регион).
Нет сайта = нет якоря → выкидываем.
- **Сборка:** `FindCompetitorsAssembler` — отсев агрегаторов (LLM) → дедуп+вычет клиента → федерал-фильтр
→ похожесть-эмбеддинги (математически, text-embedding-3-small) → DTO §7.2.
- **Оркестратор** `LiveFindCompetitors` собирает всё; **провайдер** `AutopodborServiceProvider`
подключает живьём за флагом `autopodbor.real_find`. На проде НЕ флипнут.
**Коммиты сессии:** `5a651651` (каналы А/В §12) → `5d40de66` (лёгкий канал А из списка) →
`dee2ebbc` (Яндекс-источник + слияние). Доки: `d4684717` (методика+финал), контекст-файлы.
---
## 2. 🔴 ЧТО ОСТАЛОСЬ
1. **ПОЛНЫЙ ЖИВОЙ ПРОГОН** движка на вводе Омеги (2ГИС+Яндекс+В вместе) → сверка с эталоном
`AUTOPODBOR-client-report-top.png` (72 конкурента). Ждём после слияния/дедупа ~60-75 уникальных.
Скрипт-образец: `scratchpad/live_engine_run.php` (обновить под новый конструктор — +Yandex; или
гонять через `app(CompetitorAgent::class)->findCompetitors(...)` с `config real_find=true`).
2. **Калибровка** по фидбэку владельца (он знает рынок): проценты, лишний шум, полнота.
3. **Скорость/параллельность** (владелец: 3-4 клиента разом, шаг 1 и шаг 2): движок stateless per-job;
узкое место — xfetch-2ГИС (~275с) + локальный Playwright на параллельные Яндекс-рендеры. Держать
потолки (`maxPages`, `channelBCap`, `maxQueries`); поднять N воркеров очереди. Возможно распараллелить
каналы А (2ГИС) и Яндекс внутри прогона.
4. **(Отложено, шаг 2)** сайт Яндекса из карточки организации («другой сайт» — владелец согласен отложить).
5. **Провайдер-флип на прод** — отдельное «да» владельца (сейчас за флагом, прод не тронут).
---
## 3. КЛЮЧИ / ОКРУЖЕНИЕ (секреты только в `app/.env`, gitignored — в гит НИКОГДА)
- `XFETCH_API_KEY` — **обновлён 01.07** (старый кончился) — 2ГИС-скрейп, `POST https://xf4.ru/fetch`.
- `AITUNNEL_API_KEY` — sonar-reasoning-pro (канал В), эмбеддинги (похожесть), gpt-4o-mini (АНАЛИЗ, отсев).
- `EXA_API_KEY` — сайт федерала по имени (`POST https://api.exa.ai/search`).
- `FIRECRAWL_API_KEY` — есть, но Яндекс им НЕ берётся (не рендерит); резерв.
- `AUTOPODBOR_REAL_FIND=1` — включает живой движок.
- Playwright: `node` v24 + `node_modules/playwright` **в корне репо** (`Документация/node_modules`),
chromium установлен; `require('playwright')` резолвится из-под `app/scripts/`.
- Тесты: `cd app && ./vendor/bin/pest tests/Unit/Autopodbor tests/Feature/Autopodbor`; PHP `c:/tools/php83/php`.
В тестах real_find принудительно ВЫКЛ (phpunit.xml). lefthook не в PATH (коммиты проходят).
---
## 4. ИСТОЧНИКИ ИСТИНЫ (durable — читать, не по памяти)
- **Методика движка воедино + все промпты:** `docs/superpowers/specs/2026-06-30-DVIZHOK-voedino-metodika-i-prompty.md`
- **Что есть / что переделываем + ФИНАЛ (1 модель×2 прохода, EXA):** `docs/superpowers/specs/2026-06-30-ZAFIKSIROVANO-chto-est-chto-peredelyvaem.md`
- **§12 «ФИНАЛЬНЫЙ ДВИЖОК v4» + §8–§11 (промпты/проходы):** `docs/superpowers/specs/2026-06-28-autopodbor-konkurentov-brainstorm-notes.md` **(ОСНОВНОЙ репо!)**
- **Эталон результата:** `AUTOPODBOR-client-report-top.png` (корень репо, 72 конкурента).
---
## 5. КАРТА КОДА ДВИЖКА (`app/app/Services/Autopodbor/Agent/`)
```
ChannelA/ QueryAnalyzer(iface)+AitunnelQueryAnalyzer — шаг АНАЛИЗ (рубрики)
CategoryListingParser — 2ГИС список → имя+карточка+сайт
CategoryScraper — 2ГИС пагинация
YandexDirectory(iface)+PlaywrightYandexDirectory — Яндекс (Playwright)
ChannelB/ ResearcherClient(iface)+AitunnelResearcher — sonar-reasoning-pro (имена)
ResearcherParser, ChannelBSearch — 2 прохода, стоп-лист
ExaSiteFinder — сайт федерала (EXA)
Resolve/ CompetitorResolver, TwoGis/YandexResolver — (для resolveByName; в лёгком find НЕ зовётся)
Similarity/ Embedder(iface)+AitunnelEmbedder+EmbeddingRelevance — похожесть %
Aggregator/ AggregatorClassifier(iface)+Aitunnel...+AggregatorFilter — отсев площадок
FindCompetitorsAssembler — сборка ядра → DTO
LiveFindCompetitors — ОРКЕСТРАТОР (АНАЛИЗ→А(2ГИС+Яндекс)→В→сборка)
```
Провайдер: `app/app/Providers/AutopodborServiceProvider.php` (`buildLiveFind`). Render: `app/scripts/render-yandex-list.cjs`.
---
## 6. ПРОМТ ДЛЯ ВОССТАНОВЛЕНИЯ КОНТЕКСТА (скопировать в новый чат после компакта)
> Продолжаем движок **«Автоподбор конкурентов»** Лидерры, ШАГ 1. Воркстри `worktree-avtopodbor` (код
> под `app/`). **НЕ прод. Коммит — спросив «эскейп». Пуш — только gitea. Простым языком — владелец не
> программист. Руками 2ГИС/Яндекс не парсить — только сервисы.**
>
> 🔴 **ПРОЧИТАЙ ПЕРВЫМ ДЕЛОМ (durable, не по памяти):**
> `docs/superpowers/plans/2026-07-01-DVIZHOK-STATUS-i-prodolzhenie.md` — состояние, что сделано/осталось,
> ключи, карта кода. Он ссылается на методику (`…-DVIZHOK-voedino…`) и финал (`…-ZAFIKSIROVANO…`).
>
> **ГДЕ МЫ:** движок шага 1 пересобран под финал v4 — шаг АНАЛИЗ → канал А из ДВУХ справочников
> (2ГИС через xfetch: имя+карточка+**сайт** из списка, без захода в карточку; Яндекс через локальный
> Playwright: имя+карточка, 113 орг/18с) → слияние 2ГИС+Яндекс (одна карточка, два справочника) →
> канал В (одна модель sonar-reasoning-pro × 2 прохода → имена федералов → сайт через EXA) → сборка
> (отсев агрегаторов + дедуп + похожесть-эмбеддинги + DTO). Всё за флагом `autopodbor.real_find`, на
> проде не тронуто. Офлайн-тесты: **136 unit + 74 feature зелёные**, HEAD gitea `dee2ebbc`.
>
> **СЛЕДУЮЩЕЕ:** полный ЖИВОЙ прогон на вводе Омеги (регион 29 Красноярский край, профиль «займы под
> залог авто/спецтехники», примеры vashinvestor/caranga, федералы вкл) → сверка с эталоном
> `AUTOPODBOR-client-report-top.png` (72 конкурента). Тратит кредиты (~30₽, xfetch медленный ~5 мин).
> Спросить владельца перед запуском. Ключи в `app/.env` (xfetch ОБНОВЛЁН 01.07). Playwright — в корне
> репо (`Документация/node_modules`). Дальше — калибровка по фидбэку владельца (он знает рынок).
>
> **НЕ воскрешать старое:** ИНН выкинут; похожесть = эмбеддинги (не модель); канал В = ТОЛЬКО имена
> (не якоря); федерал-сайт через EXA; Яндекс — локальный Playwright (не Firecrawl). Перед коммитом — «эскейп».
---
## 7. ЖЁСТКИЕ ПРАВИЛА (не пересматривать без «меняем»)
ИНН выкинут · система универсальна (клиентское из конфига) · похожесть = эмбеддинги · агрегаторы =
LLM-классификатор · is_federal по факту · телефоны не синтезируем · якорю модели не верить · шаг 1 =
имя+сайт+карточки (телефоны/описание = шаг 2) · Яндекс = Playwright, не Firecrawl · секреты только в
`.env` · НЕ прод · коммит — «эскейп» · пуш — gitea.