diff --git a/docs/superpowers/plans/2026-07-01-DVIZHOK-STATUS-i-prodolzhenie.md b/docs/superpowers/plans/2026-07-01-DVIZHOK-STATUS-i-prodolzhenie.md new file mode 100644 index 00000000..5a93d995 --- /dev/null +++ b/docs/superpowers/plans/2026-07-01-DVIZHOK-STATUS-i-prodolzhenie.md @@ -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.