Read in:
Русский

Token Economy: тяните нужный раздел, а не весь файл

Агент, который загружает заметки целиком, платит токенами за текст, который никогда не прочитает. trip2g даёт агентам два инструмента, чтобы тянуть только нужное: структурный доступ к разделу через toc_path и GraphQL-запросы, возвращающие ровно те поля, которые запрошены — ни полем больше.

Эта заметка разбирает оба инструмента и объясняет, почему именно такой подход правильный.


Проблема grep -A/-B и полных дампов

grep -A 20 -B 20 "пул воркеров" note.md возвращает фиксированное окно в 40 строк вокруг каждого совпадения. Окно не знает о границах разделов. Оно может оборвать предложение посередине, захватить абзацы из соседнего раздела, вернуть три перекрывающихся окна для термина, который встречается трижды. Агент платит за всё это.

Загружать заметку целиком ещё хуже. Заметка на 3000 токенов содержит один нужный абзац. Агент читает все 3000 токенов, чтобы его найти — и ответ оказывается в середине контекстного окна, где способность модели к воспроизведению хуже всего.

Антипример: агент вызывает search("пул воркеров"), получает совпадение, вызывает note_html(pid=42) без toc_path. Загружает всю заметку. Платит 3000 токенов, модель вспоминает плохо.

Пример: агент вызывает search("пул воркеров"), читает toc_path: ["Горутины", "Пул воркеров"] из результата, вызывает note_html(pid=42, toc_path=["Горутины", "Пул воркеров"]). Получает один раздел, ~300 токенов.


Точное извлечение раздела

Fuzzy Pointer описывает механизм полностью. Коротко:

  1. search(query) возвращает результаты. Каждый содержит toc (полную иерархию заголовков) и matches[].toc_path (путь к разделу, где нашлось совпадение).
  2. note_html(pid=N, toc_path=match.toc_path) возвращает HTML только этого раздела.

Контраст по токенам: один раздел — ~300 токенов. Вся заметка — ~3000 токенов. Нужен соседний раздел — берите его из того же toc, без второго вызова search.

Полный разбор с деталями про pid и случаи без заголовков — в Fuzzy Pointer.


GraphQL-выборка полей

MCP-инструмент graphql_request выполняет GraphQL-запрос к хранилищу в режиме чтения. Результат приходит в поле structuredContent MCP-результата — в конверте {data, ...}. Поле content[0].text — короткая заглушка ("structured result"); реальные данные в structuredContent. Читайте structuredContent, а не заглушку.

GraphQL позволяет запросить ровно те поля, которые нужны. Сравните:

Антипример: агент grep-ит результаты поиска или загружает объекты заметок целиком. Получает body, frontmatter, raw_markdown, created_at, updated_at, tags — все поля, которые существуют, включая те, что не нужны. Каждое лишнее поле — токены впустую.

Пример:

{ search(input: { query: "пул воркеров" }) {
    nodes {
      highlightedTitle
      url
    }
  }
}

Возвращает только highlightedTitle и url на каждый результат, в structuredContent.data.search.nodes. Когда нужен контекст для навигации, добавьте поля:

{ search(input: { query: "пул воркеров" }) {
    nodes {
      highlightedTitle
      url
      document { path title }
    }
  }
}

Добавьте document { path title }, когда нужно перейти к заметке. Не добавляйте, когда не нужно. Данные появляются один раз — в structuredContent — без дублирования в текстовом блоке. Это намеренно: одна копия, без избыточности.

Выборка полей составная. Платите только за то, что запросили.


Почему это правильный подход

Три источника обосновывают этот выбор.

Руководство Anthropic Effective context engineering for AI agents (2025) описывает загрузку контекста «just in time» через лёгкие идентификаторы вместо предзагрузки документов целиком. Цель — «минимальный набор высокосигнальных токенов». Руководство также вводит понятие context rot: способность модели к воспроизведению ухудшается по мере заполнения контекстного окна малорелевантным материалом.

Публикация Anthropic Contextual Retrieval (сентябрь 2024) показывает, что добавление контекста раздела к каждому чанку перед встраиванием — чтобы чанк не был оторван от иерархии документа — резко улучшает качество поиска. Дизайн trip2g с breadcrumb-заголовком на каждый чанк реализует именно это: каждый индексируемый фрагмент несёт полный путь раздела Паттерны конкурентности в Go > Горутины > Пул воркеров, а не просто текст абзаца.

Liu et al., «Lost in the Middle» (TACL 2023) измерили: модели воспроизводят содержимое в начале и конце контекстного окна значительно лучше, чем в середине. Дамп целого файла помещает ответ в середину. Извлечение нужного раздела помещает его в начало.

toc_path и выборка полей GraphQL — это реализация принципа «just in time, минимальный высокосигнальный набор» в trip2g.


Рекомендованный сценарий агента

1. search(query)
   → результаты с toc + matches[].toc_path

2. Для контента: читаем toc_path лучшего совпадения
   → note_html(pid=N, toc_path=match.toc_path)
   → получаем только нужный раздел

3. Для метаданных: graphql_request с нужными полями
   → результат в structuredContent.data

4. Нужен соседний раздел?
   Берём из toc того же результата поиска
   → без второго вызова search

Полный справочник по MCP-инструментам: MCP-сервер. Навигация по разделам: Fuzzy Pointer.