diff --git a/SKILL.md b/SKILL.md index 5b63f91..dc4fe58 100644 --- a/SKILL.md +++ b/SKILL.md @@ -42,7 +42,7 @@ description: "Meta-research под вопрос или решение: веб-п 3. **Plan** [`opus`/medium] — `plan.md` по шаблону §0–16 из `workflow.md`: HEADER → SCOPE → STRUCTURE → EXECUTION → TRACKING. Несущее: acceptance criteria, гипотезы, risk register, subtopic↔blocks mapping (least-to-most для многошаговых вопросов), sourcing strategy §12, opposition queries, stop-criteria. 3.5. **Capability Discovery** [`sonnet`/low] (deep — обязательна) — audit env vars, подтемы → доступные API, fallback на awesome-lists. См. `capability_discovery.md`. 3.7. **Plan-review gate** [`sonnet`/low] (shallow — skip) — единственная human-in-the-loop точка ПЕРЕД дорогой Фазой 4: показать сжатый план (вопрос, решение, жанр, гипотезы, каналы, стоп-критерий, routing). **deep — ЖДАТЬ явного «Ок»; medium — soft.** Плюс **скаут-пасс** (deep — рекомендуется): 3–4 `Explore` на `haiku` ищут непокрытые подвопросы, а не источники; выход — правки `plan.md`, ноль записей в `sources/`. См. `plan_gate.md`. -4. **Поиск** [main `sonnet`/medium; sub-agents: `haiku` web/api, `sonnet` academic/long-source] — (4.0) Source Dispatch по матрице → `plan.md` §12; количественный подвопрос ⇒ primary-канал registry/API. (4.1) medium/deep — `general-purpose` суб-агенты параллельно, каждому свой диапазон id (`s01-s09`, `s10-s19`…) и своя ось поиска, не только подтема; shallow — главный поток. (4.2) Fetch, дедуп с замером `overlap_rate`. (4.3) Агент сам пишет `sources/NN_slug.md`, в главный поток — только index-строки. После раунда 1 — snowball. Loop: goal-check → bounded deviation → circuit breaker (2 раунда без нового ⇒ стоп, остаток в Open Questions). **Окно раунда пересобирается, не накапливается** (medium/deep): `state.md` ПЕРЕЗАПИСЫВАЕТСЯ перед каждым раундом (`## Known` статусами со ссылками · `## Gaps` · `## Next`, ≤6 КБ), планирование по нему, а не по транскрипту. См. `source_dispatch.md`, `subagents_v2.md`. +4. **Поиск** [main `sonnet`/medium; sub-agents: `haiku` web/api, `sonnet` academic/long-source] — (4.0) Source Dispatch по матрице → `plan.md` §12; количественный подвопрос ⇒ primary-канал registry/API. (4.1) medium/deep — `general-purpose` суб-агенты параллельно, каждому свой диапазон id (`s01-s09`, `s10-s19`…) и своя ось поиска, не только подтема; shallow — главный поток. (4.2) Fetch, дедуп с замером `overlap_rate`; WebFetch вернул тонко (<500 знаков, нет цитаты, блок/paywall) ⇒ сразу `fetch_source.py`, не по желанию. (4.3) Агент сам пишет `sources/NN_slug.md`, в главный поток — только index-строки. После раунда 1 — snowball. Loop: goal-check → bounded deviation → circuit breaker (2 раунда без нового ⇒ стоп, остаток в Open Questions). **Окно раунда пересобирается, не накапливается** (medium/deep): `state.md` ПЕРЕЗАПИСЫВАЕТСЯ перед каждым раундом (`## Known` статусами со ссылками · `## Gaps` · `## Next`, ≤6 КБ), планирование по нему, а не по транскрипту. См. `source_dispatch.md`, `subagents_v2.md`. 5. **Claims-ledger + триангуляция** [`haiku`/low] — `claims.csv` (схема колонок — `source_scoring.md`). `triangulated` ⟺ ≥3 источника И ≥2 типа И ≥2 корня (`root:`) И ≥2 пути (`discovery_path:`); иначе `single-type`/`single-root`/`single-path`, потолок medium. Без primary — потолок medium; caveat (`vendor`/`self-reported`/`disputed:sNN`) — потолок medium, `disputed` без арбитра → low. **Защита меньшинства:** непогашенный `dissent` от `Primary`/`credibility ≥ 4` ⇒ `contested` независимо от большинства, обе позиции в отчёт. Gap-волна на не-triangulated, max 2 круга, иначе `data-insufficient`. См. `source_scoring.md`. 5.5. **Evidence-фильтр: relevance × authority** [`sonnet`/low] (medium/deep — обязательно) — фильтр на ВХОДЕ синтеза. **Relevance:** пара (claim, source) → Correct/Ambiguous/Incorrect по дословным цитатам → relevant-only цитаты в `evidence/CN.md`; claim без relevant-источника → `data-insufficient` или до-поиск. **Authority** (несущие пары: claim в memo/F1/F9, ИЛИ с числом, ИЛИ источник единственный корень, ИЛИ `caveat` ≠ `-`): «вправе ли ЭТОТ источник утверждать ЭТО» → `qualified`/`unqualified-for-this-claim`/`unknown` → `.verify/authority.json`. **`unknown` — карантин:** не единственная опора, не `high`. См. `evidence_filter.md`. 5.7. **Сверка с вики** [`sonnet`/low] (medium/deep — обязательно) — `claims.csv` прогона против кросс-прогонной вики, **до синтеза**: расхождение с прошлым ресёрчем обязано попасть в отчёт, заметить его в Фазе 7 значит заметить поздно. `wiki_pair.py build` собирает пары и сам закрывает всё, что не требует суждения; остаток идёт в `.verify/wiki_pairs.json` → вердикт из четырёх (`same-claim-agree`/`same-claim-conflict`/`different-claim`/`unknown`) → `wiki_pair.py record`. **`unknown` — карантин**, не конфликт. Подтверждённый конфликт = обе позиции в отчёт, потолок `medium`, без арбитра `contested`. Срез по `MAX_ADJUDICATED` называть вслух. Пороги прескрина и правила `record` — `wiki.md`. @@ -125,6 +125,7 @@ description: "Meta-research под вопрос или решение: веб-п - Фаза 6.5: не доверять наличию ссылки — проверять entailment по дословной цитате; вердикты писать в `.verify/*.json` и не пересчитывать в rubric/F10; пары брать из `evidence/`, не пересканировать `sources/`. Чинить отчёт, а не ledger. - Для fetch+save и red team — `general-purpose` с явным диапазоном номеров, не `Explore` (read-only, только разведка). Не запускать суб-агентов последовательно — только параллельно в одном сообщении. - Не сжимать `sources/` в один файл, не выводить результат только в чат. Не гонять шаги finish-up поодиночке вместо `finish.py` — пропуск флага (`--out`, `--strict`) и есть пропуск фазы. +- Тонкий источник (<2 дословных цитат или `access:` PARTIAL/closed) не считается плечом триангуляции — перефетчить лестницей или понизить статус тезиса. - Не обходить WebFetch произвольным `bash`/`curl`. Единственный санкционированный fallback — `scripts/fetch_source.py` (Фаза 4.2): он читает robots.txt, санитайзит страницу от prompt injection и проставляет `fetch_tier`. Вывод ручного `curl` в `sources/` не кладётся. - Не принимать `fetch_source.py` за средство против paywall и анти-бот-защиты: `auth-wall` и `antibot` для него — терминальный вердикт. Дальше — fallback-протокол `channels.md` или endpoint из `api_sources/`. - Не рисовать в отчёте число, которого нет в `numbers.csv`, и не подбирать палитру фигур на глаз — она валидируется скриптом. @@ -144,7 +145,7 @@ Update тоже идёт в вики: `wiki_ingest.py` после дельты ( **Условные — грузить, когда прогон дошёл до условия, а не заранее:** `capability_discovery.md` и `awesome_lists_registry.md` — Фаза 3.5 (обязательна только на deep) · `stat_sources/INDEX.md` (33 категории) и `api_sources/INDEX.md` (47+ endpoints) — Фаза 4, когда подвопрос количественный или Source Dispatch ведёт в registry/API · `refresh_protocol.md` — только режим `update`. -**По фазам:** `subagents_v2.md` (4) · `fetch_source.py` + `channels.md` §«Bot-block ≠ paywall» (4.2, только когда WebFetch ответил «unable to fetch from …») · `source_scoring.md` (шкалы, provenance, claims-ledger, dissent, `numbers.csv` — 5–6) · `evidence_filter.md` (5.5) · `synthesis_outline.md` (6) · `adversarial_pass.md` (6) · `runtime_verification.md` (6.5) · `report_export.md` (6.9) · `swarm_postprocess.md` (после 7) · `decision_walkthrough.md` (8). +**По фазам:** `subagents_v2.md` (4) · `fetch_source.py` + `channels.md` §«Bot-block ≠ paywall» (4.2, штатно при «unable to fetch» И при тонком ответе) · `source_scoring.md` (шкалы, provenance, claims-ledger, dissent, `numbers.csv` — 5–6) · `evidence_filter.md` (5.5) · `synthesis_outline.md` (6) · `adversarial_pass.md` (6) · `runtime_verification.md` (6.5) · `report_export.md` (6.9) · `swarm_postprocess.md` (после 7) · `decision_walkthrough.md` (8). **Блоки (по выбранному жанру):** `frame.md` F1-F10 · `explain.md` E1-E14 · `compare.md` C1-C13 · `map.md` M1-M12 · `validate.md` V1-V10 · `analyze.md` A1-A13 · `close.md` Z1-Z12 · `people.md` P1-P7 · `numbers.md` N1-N8 · `context.md` X1-X7. diff --git a/references/api_sources/search/brave_search.md b/references/api_sources/search/brave_search.md index ac83f40..676d899 100644 --- a/references/api_sources/search/brave_search.md +++ b/references/api_sources/search/brave_search.md @@ -47,15 +47,19 @@ JSON с organic results, news, videos, infobox. 1. https://api.search.brave.com → sign up 2. Получи API key -3. В env: `export BRAVE_API_KEY="BSA..."` +3. В env: `export BRAVE_SEARCH_API_KEY="BSA..."` ## Query patterns +Прямой вызов из скилла: `python3 scripts/search_query.py --engine brave --query "..." [--n 10] [--json]` +(читает `BRAVE_SEARCH_API_KEY` из env, exit 2 если ключа нет). Ниже — сырой HTTP-контракт, +который скрипт реализует. + ### Web search ``` GET https://api.search.brave.com/res/v1/web/search?q={query}&count=20 -Headers: X-Subscription-Token: {BRAVE_API_KEY} +Headers: X-Subscription-Token: {BRAVE_SEARCH_API_KEY} ``` ### News search diff --git a/references/api_sources/search/exa.md b/references/api_sources/search/exa.md index 65ca339..04d6d98 100644 --- a/references/api_sources/search/exa.md +++ b/references/api_sources/search/exa.md @@ -50,6 +50,10 @@ JSON с результатами ranked by semantic similarity to query. ## Query patterns +Прямой вызов из скилла: `python3 scripts/search_query.py --engine exa --query "..." [--n 10] [--json]` +(читает `EXA_API_KEY` из env, exit 2 если ключа нет). Ниже — сырой HTTP-контракт, +который скрипт реализует. + ### Neural search ``` diff --git a/references/api_sources/search/tavily.md b/references/api_sources/search/tavily.md index 7cf797e..a5a0c6c 100644 --- a/references/api_sources/search/tavily.md +++ b/references/api_sources/search/tavily.md @@ -48,6 +48,10 @@ JSON с готовыми **answers + sources**. Tavily — это search API, с ## Query patterns +Прямой вызов из скилла: `python3 scripts/search_query.py --engine tavily --query "..." [--n 10] [--json]` +(читает `TAVILY_API_KEY` из env, exit 2 если ключа нет). Ниже — сырой HTTP-контракт, +который скрипт реализует (basic search, без `include_answer`/`include_domains`). + ### Basic search ``` diff --git a/references/capability_discovery.md b/references/capability_discovery.md index 1aa072b..9f4c5cf 100644 --- a/references/capability_discovery.md +++ b/references/capability_discovery.md @@ -15,6 +15,8 @@ Capability Discovery закрывает этот gap. **Прозрачность для пользователя:** что используется, что пропускается, что бы стоило настроить. +**Второй поисковый движок.** Один движок = одна траектория поиска — конформизм, который правило триангуляции запрещает для источников и должно запрещать для самого канала поиска. Если в env есть `BRAVE_SEARCH_API_KEY`, `TAVILY_API_KEY` или `EXA_API_KEY` — на medium/deep Phase 4 обязана выделить одну поисковую ось под него через `scripts/search_query.py --engine {brave,tavily,exa} --query "..."`, параллельно обычному WebSearch харнесса. Пересечение результатов между движками фиксируется как `overlap_rate` в `plan.md` §15 (низкий overlap — сигнал, что второй движок реально расширяет покрытие, а не дублирует первый). + ## Workflow фазы ### Step 1: Audit env vars @@ -34,10 +36,10 @@ if env $GITHUB_TOKEN exists → mark GitHub as 'authenticated' |---|---|---| | `FRED_API_KEY` | FRED economic data | optional (HTML fallback есть) | | `GITHUB_TOKEN` | GitHub Search | optional (60/h без, 5000/h с) | -| `BRAVE_API_KEY` | Brave Search | **не используется** — скилл ходит через WebSearch харнесса | -| `TAVILY_API_KEY` | Tavily | **не используется** — оставлено для внешних раннеров | -| `EXA_API_KEY` | Exa.ai | **не используется** — оставлено для внешних раннеров | -| `SERPAPI_KEY` | SerpAPI | **не используется** — оставлено для внешних раннеров | +| `BRAVE_SEARCH_API_KEY` | Brave Search | второй поисковый движок, обязателен для одной оси Phase 4 если ключ есть | +| `TAVILY_API_KEY` | Tavily | второй поисковый движок, обязателен для одной оси Phase 4 если ключ есть | +| `EXA_API_KEY` | Exa.ai | второй поисковый движок, обязателен для одной оси Phase 4 если ключ есть | +| `SERPAPI_KEY` | SerpAPI | **не используется** — платный, без free tier | | `NEWSAPI_KEY` | NewsAPI | для news | | `ALPHA_VANTAGE_KEY` | Alpha Vantage | для stock prices | | `CRUNCHBASE_API_KEY` | Crunchbase | для company data | diff --git a/references/fetch_ladder.md b/references/fetch_ladder.md index 2fc4318..0c48bbf 100644 --- a/references/fetch_ladder.md +++ b/references/fetch_ladder.md @@ -22,6 +22,8 @@ uv run scripts/fetch_source.py -o /.fetch/NN.md --meta /.fetch/N - **Фид не заменяет статью.** Из него берутся заголовок, дата, аннотация и URL — но не полный текст. Цитату из аннотации помечать как таковую, не выдавать за цитату из статьи. - **PDF не markdown-ится, а сохраняется как есть.** Скрипт определяет тип по `Content-Type` и магии `%PDF-`, кладёт файл с расширением `.pdf` и ставит `content_kind: pdf` в источник. Дальше — штатный `Read` с диапазоном `pages`; он читает PDF нативно. Для академического канала это основной путь: препринты и рабочие бумаги приезжают PDF-ами, а не страницами. Фид так же сохраняется дословно в `.xml` — markdownify уничтожает структуру, ради которой фид и брали. - **Exit 3 — стоп по умолчанию.** Это `Disallow` для всех клиентов, а не AI-специфичное исключение. Флаг `--ignore-robots` существует, но это решение пользователя: суб-агент его не ставит — пишет в `gaps` и идёт дальше. +- **robots.txt недоступен — тоже exit 3, не «разрешено».** По RFC 9309: 404/410 значит «правил нет», это `allowed`. А 5xx, таймаут или сетевая ошибка — «не смогли узнать», и это fail-closed, не fail-open. Frontmatter получает `robots: unreachable`; `--ignore-robots` снимает и этот блок так же, как обычный `Disallow`. - **Код 200 ≠ успех.** Скрипт классифицирует по паре (статус, содержимое): заглушка под 200 это `antibot`, а не источник. Не подменять эту проверку на «файл записался» — scrapling пишет файл и выходит нулём даже на 401. -- **Что уезжает в источник.** Из `--meta` в frontmatter `sources/NN.md` идут `access:`, `fetched:`, `fetch_tier:` и `fetch_note:`, если он есть. Тир — часть провенанса: страница, взятая браузером поверх AI-исключения, не равна отданной добровольно. +- **Что уезжает в источник.** Из `--meta` в frontmatter `sources/NN.md` идут `access:`, `fetched:`, `fetch_tier:`, `content_sha256:`, `snapshot:` и `fetch_note:`, если он есть. Тир — часть провенанса: страница, взятая браузером поверх AI-исключения, не равна отданной добровольно. +- **`content_sha256` и `snapshot` — на случай, если сайт молча переписал страницу.** `content_sha256` — хэш извлечённого текста; `snapshot` — путь к сырой копии тела ответа рядом с `--out` (`<имя>.snapshot.html` для HTML, `.snapshot.bin` для остального). Без `-o` или с `--no-snapshot` копии нет, `snapshot: -`. - Извлечение всегда идёт в режиме `main_content_only` — он же санитайзер prompt injection (режет скрытые и `aria-hidden` элементы, `