Инфоконтексты (Smart Snippets)
Справочник: что это, чем ограничено, как устроено в скилле и как чинить, когда инфоконтекстов нет.
Официальная документация: Получение инфоконтекстов, Текстовый поиск.
Что это
Обычная выдача даёт ссылку, заголовок и сниппет в одно-два предложения — этого хватает человеку, который пойдёт на страницу сам, и почти всегда не хватает агенту, которому надо ответить здесь и сейчас.
Инфоконтекст — фрагмент найденной страницы примерно на 500 токенов, релевантный запросу и содержащий цитаты из документа. Промежуточное звено между сниппетом и полным текстом: информации уже достаточно для ответа, но обрабатывать страницу самому не нужно.
Ограничения
| Что | Значение |
|---|---|
| Метод | только синхронный WebSearchService/Search (POST /v2/web/search) |
| Тип поиска | только SEARCH_TYPE_RU — русская поисковая база |
| Размер | ~500 токенов на документ |
| Формат ответа | JSON, а не XML или HTML |
Асинхронный POST /v2/web/searchAsync инфоконтекстов не возвращает.
web_search_async.sh предупреждает об этом на старте и уходит за обычной
выдачей — батч на сотни запросов остаётся асинхронным.
SEARCH_TYPE_RU — обязательное условие, а не просто дефолт. Если задать
--search-type SEARCH_TYPE_TR (или любой другой), скилл выполнит именно тот
поиск, который попросили, но без инфоконтекстов, и скажет об этом в stderr.
Молча подменять поисковую базу он не станет.
Потолок — 20 документов, и просьба о большем даёт меньшее. В документации
этого нет, замерено на живом API: при groupsOnPage 21 и выше сервер не
обрезает выдачу до 20, а роняет её до дефолтных 10 — по цене инфоконтекстного
запроса. Поэтому вместе с инфоконтекстами --results клампится до 20 и об
этом пишет в stderr. Без них потолка нет, работает документированный диапазон
1–100. Реальное количество всегда видно в строке Всего: N, с выдержками: M.
Как это включено в скилле
Включено по умолчанию. Выключается двумя способами:
# разово
bash scripts/web_search_sync.sh --query "..." --no-snippets// config/config.json — насовсем
"search": {
"smart_snippets": {
"enabled": false,
"docs": 20
}
}docs — сколько результатов запрашивать, когда инфоконтексты включены и
--results не задан явно. Без инфоконтекстов работает прежний
search.results_per_page.
Протокол
Имена из протокола собраны в начале scripts/common.sh:
SMART_SNIPPETS_FLAG_KEY="x-genesis-info-context"
SMART_SNIPPETS_FLAG_VALUE="on"
SMART_SNIPPETS_SEARCH_TYPE="SEARCH_TYPE_RU"Флаг уезжает в metadata.fields — map key:value из WebSearchRequest,
штатный способ включать флаги поиска, не меняя схему запроса:
{
"query": {
"searchType": "SEARCH_TYPE_RU",
"queryText": "Yandex Cloud"
},
"groupSpec": { "groupMode": "GROUP_MODE_FLAT", "groupsOnPage": 20, "docsInGroup": 1 },
"region": "225",
"folderId": "b1g...",
"metadata": { "fields": { "x-genesis-info-context": "on" } }
}Ошибка в документации Яндекса, подтверждённая командой Search API: страница «Текстовый поиск» пишет «чтобы получить инфоконтекст, передайте заголовок
x-genesis-full-text». Правильный ключ —x-genesis-info-context, как на странице «Получение инфоконтекстов» и во всех её примерах. Страницу обещали поправить.
x-genesis-full-textпроверен живым запросом и не делает ничего: ответ приходит XML, инфоконтекстов ноль. Пустое полеfull_text, которое видно в успешном ответе рядом сinfo_context, — рудимент, а не признак второй фичи. Перебирать ключи не нужно, правильный один.
Ответ
Ответ приходит в rawData base64. Дальше формат зависит от запроса, и это
главная неочевидность: с инфоконтекстами это JSON, без них — XML.
Документация показывает документ плоским:
{"docs": [{"Num": 1, "DocumentTitle": "…", "FullUrl": "…", "Description": "…", "info_context": "…"}]}Живой API кладёт метаданные во вложенный rich_data:
{
"docs": [
{
"FullUrl": "https://yandex.cloud/",
"rich_data": {
"Num": 1,
"DocumentTitle": "Yandex Cloud",
"Description": "Облачная платформа Yandex Cloud",
"FullUrl": "https://yandex.cloud/",
"ForcedShortUrl": "yandex.cloud",
"UrlMenu": []
},
"info_context": "Yandex Cloud — это облачная платформа, которая предоставляет...",
"full_text": "",
"doc_mtime_datetime_seconds": 1767225600,
"last_acess_datetime_seconds": 1767312000
}
]
}Встречаются обе формы, поэтому парсер ищет каждое поле сперва в rich_data,
потом на верхнем уровне. info_context лежит на верхнем уровне всегда.
parse_search_response определяет формат по первому непробельному символу и
приводит оба ответа к одному виду, так что остальному коду разница не видна:
| Поле результата | Из JSON | Из XML |
|---|---|---|
position |
Num (rich_data → верхний уровень) |
порядок <doc> в выдаче |
title |
DocumentTitle (там же) |
<title> |
url |
FullUrl (там же) |
<url> |
snippet |
Description (там же) |
первый <passage> |
domain |
хост из FullUrl |
<domain> |
extract |
info_context |
всегда пусто |
Что получается на выходе
Один поиск оставляет в cache/results/ три файла:
| Файл | Что внутри | Кому |
|---|---|---|
<hash>.md |
пак: заголовок, URL и инфоконтекст по каждому документу | агенту — читать |
<hash>.json |
то же структурно, единый формат для обоих ответов API | коду — обрабатывать |
<hash>.raw |
исходное тело ответа: JSON или XML | разбору полётов |
<hash> — первые 12 символов md5 от текста запроса, так что повторный поиск
той же фразы перезапишет те же файлы.
Гигиена контекста
20 инфоконтекстов по ~500 токенов — это порядка 10 тысяч токенов. Столько нельзя печатать в stdout: у песочницы жёсткий лимит вывода, и при его превышении скрипт падает молча — «Error running command» без текста и без stderr.
Поэтому тексты всегда уходят в файл, а в stdout идёт только индекс: строка на
документ с позицией, доменом, размером инфоконтекста и заголовком. В батче
(--file) на весь запрос печатается одна строка — иначе десять запросов
упрутся в тот же лимит.
Дальше выбор за агентом:
- нужен один источник —
Readпака со смещением илиgrepпо нему; - нужны все —
Readпака целиком; - нужна структура —
<hash>.json.
Повторный поиск той же фразы ради текста, который уже лежит в паке, — потраченные деньги и время.
Отладка
# показать тело запроса и выйти, ничего не оплачивая
YSA_DRY_RUN=1 bash scripts/web_search_sync.sh --query "тест"Полезно, чтобы убедиться: флаг в metadata.fields действительно уехал, а
searchType равен SEARCH_TYPE_RU.
# офлайн-тесты: ни сети, ни сервисного аккаунта
sh scripts/tests/run.shТесты гоняют разбор обоих форматов ответа, сборку тела запроса, отрисовку пака
и разбор флагов CLI на фикстурах из scripts/tests/fixtures/.
Когда инфоконтекстов нет
| Симптом | Причина | Что делать |
|---|---|---|
с выдержками: 0, а <hash>.raw начинается с < |
пришёл XML — флаг не сработал | проверить, что фича подключена к вашему folder_id |
с выдержками: 0, а <hash>.raw начинается с { |
фича работает, но страницы не отдали фрагменты | нормально для части выдачи; проблема, только если пусто у всех |
с выдержками: 0, в stderr про SEARCH_TYPE_RU |
задан другой тип поиска | убрать --search-type или вернуть SEARCH_TYPE_RU |
| часть документов без инфоконтекста | страница закрыта или плохо размечена | нормально, пользуйтесь тем, что пришло |
| инфоконтекстов нет в батче | запускался web_search_async.sh |
повторить через web_search_sync.sh --file |
| ошибка 403 | у сервисного аккаунта нет роли | search-api.webSearch.user, см. config/README.md |
| пустой stdout, «Error running command» | вывод превысил буфер песочницы | уменьшить --results, читать пак файлом |
Что именно вернул API, всегда видно в <hash>.raw.
Цены
Инфоконтексты — отдельная, заметно более дорогая позиция в тарифах (цены за 1000 запросов, с НДС, на момент написания):
| Тип запроса | ₽ |
|---|---|
| Дневные синхронные | 488 |
| Ночные синхронные (00:00–07:59 UTC+3) | 366 |
| Дневные отложенные (async) | 30,5 |
| Запросы инфоконтекста поиска в регионе RU | 1 500 |
То есть инфоконтекст дороже обычного синхронного запроса примерно втрое, а
асимметрия с асинхронным режимом — почти пятидесятикратная. Практический вывод:
если задача — снять позиции по сотне запросов, инфоконтексты не нужны, берите
--no-snippets или асинхронный режим.
Актуальный прайс: Правила тарификации Yandex Search API.