- BrainTools - https://www.braintools.ru -

Слепое пятно LLM-разработки: контекст за пределами кода

Слепое пятно LLM-разработки: контекст за пределами кода - 1

Качество работы LLM — функция от качества контекста на входе. Это утверждение звучит банально, однако зачастую разработчики оптимизируют модель, выбирая между GPT, Claude, Gemini и прочими, и промпт, но не контекст в целом. Между тем, разница между “агент с правильным контекстом” и “агент без контекста” — не 20% и не 50%. Эта разница находится в дистанции вариантов между “решил задачу за 5 минут” и “потратил час, сломал два сервиса, и результат пришлось откатить из-за массы новых проблем”.

Я solo-разработчик. В моей экосистеме десятки актуальных проектов: платформа из десятков микросервисов, AI-инференс кластер на неспецифическом железе типа mac studio и dgx spark, масса shared-библиотек, инфраструктура на нескольких физических и десятках виртуальных хостов.

Последний год “пишу” код почти исключительно через LLM и Cursor. Начинал с deepseek на уровне “подскажи как написать функцию для …” и дошел до полноценной оркестрации на Claude 4.6: я формулирую задачу, агент анализирует условия и кодовую базу, обсуждаем архитектурный план, агент пишет код и тесты, запускает тесты, исправляет ошибки [1], получает от меня обратную связь по результатам ручной проверки.

Это работает хорошо, когда агент глубоко понимает контекст. И катастрофически плохо, когда контекста недостаточно. Эта статья — про то, как я решаю проблему контекста системно.

Оговорка о применимости

Описанная методология разрабатывалась и обкатывалась на одной из наиболее сильных моделей для работы с кодом — Claude 4.6 Opus с контекстным окном в миллион токенов. Это важно зафиксировать: большое окно контекста означает, что агент физически способен “увидеть” knowledge base, а сильные аналитические способности модели позволяют извлечь из неё пользу, а не утонуть в шуме.

Даже на такой модели разница между “с workbench” и “без workbench” оказалась существенной — и именно это стало основанием для формализации подхода. Если структурированный контекст заметно улучшает и без того сильную модель, можно предположить, что для менее мощных моделей — с меньшим окном или слабее — он может быть ещё важнее: у них меньше способности компенсировать отсутствие контекста “сообразительностью”.

Однако практической проверки на open source моделях или моделях с меньшим окном контекста я не проводил. Вероятно, потребуется адаптация: более компактные документы, приоритизация контекста, возможно — автоматическая выборка релевантных фрагментов вместо подключения всей knowledge base. Это остаётся открытым вопросом, и если у кого-то из читателей есть опыт [2] подобных экспериментов — это интересно обсудить.

Проблема: эфемерная память

У LLM-агентов нет долговременной памяти [3]. Это фундаментальное свойство архитектуры на текущем уровне развития технологий: каждый чат — изолированный контекст, который исчезает после завершения сессии. Cursor Rules и system prompts дают агенту характер (конвенции, стиль, ограничения), но не дают знаний о системе — историю решений, архитектуру, состояние проектов.

Для одного проекта это терпимо: ключевая часть кодовой базы помещается в контекст, README описывает основные моменты, агент ориентируется используя небольшой объем контекстного окна и времени на вызов инструментов. Но когда проектов десятки и они связаны — каждая сессия начинается с фазы ориентации, на которую уходят лишние токены и время. Агент не знает без дополнительных усилий:

  • Что проект использует шину данных для коммуникации, а не REST

  • Что два месяца назад был отвергнут Redis в пользу NATS KV Store и почему

  • Что один из проектов намеренно остановлен из-за ограничений используемой БД

  • Что другой проект — это legacy, который находится в процессе замены

  • Какие shared-библиотеки существуют и какая кодовая база их использует

Отсутствие системного контекста порождает четыре класса ошибок:

1. Повторение [4] отвергнутых решений. Без ист��рии решений агент зачастую предлагает то, что уже было рассмотрено и отвергнуто. Из сессии в сессию. “Давайте используем Redis для кэширования” — мы уже это обсуждали два месяца назад, вот почему не Redis.

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

3. Потеря времени на ориентацию. Без навигационных документов существенная часть сессии уходит на рекогносцировку: “какие файлы отвечают за X”, “где конфигурация Y”, “как деплоится Z”.

4. Generic-код вместо идиоматического. Без знания конвенций проекта агент пишет в стиле “среднего по больнице”: camelCase вместо snake_case, axios вместо project-specific api_request(), console.log вместо structured logger.

Cursor Rules (.cursor/rules/) частично решают проблему — проектные конвенции, anti-patterns, стек. Но rules — это про один проект. Всё иначе, когда нужно, чтобы агент понимал систему целиком: как проекты связаны, какие решения приняты, что в каком состоянии.

Принципы контекста

Контекст должен быть постоянным (persistent), структурированным (structured) и находиться рядом с кодовой базой (co-located).

Если контекст теряется между сессиями — ему нужно обеспечить долговременное хранение. Если контекст хранится хаотично — его нужно структурировать. Если контекст лежит в отдельном инструменте (wiki, Notion, Google Docs) — LLM-агент его не видит. Значит, контекст должен лежать рядом с кодом, в фор��ате, который агент может прочитать.

Из этих принципов вытекает следующий вариант решения: один git-репозиторий с документацией по всей системе, подключаемый вторым (или дополнительным) корнем в IDE.

Я назвал его workbench. Он подключается еще одним корнем в каждый workspace через .code-workspace файл:

{
    "folders": [
        {
            "name": "front-app",
            "path": "/path/to/generic/front-app"
        },
        {
            "name": "workbench",
            "path": "/path/to/meta/workbench"
        }
    ]
}

Агент видит и код текущего проекта, и весь контекст — в одном окне. Не нужно переключаться, не нужно copy-paste, не нужно “прочитай этот документ из Notion”.

Архитектура базы знаний: два измерения организации

Документация различается по двум независимым параметрам, и оба влияют на то, как её организовать.

Scope — масштаб применимости. Одни документы касаются конкретного проекта: его бэклог, план миграции, заметки по расследованию бага. Другие — пересекают границы проектов: карта серверов, справочник shared-библиотек, архитектурные решения, затрагивающие несколько сервисов. Если кросс-проектное знание лежит внутри одного проекта, остальные его не видят.

Lifecycle — скорость устаревания. Бэклог проекта устаревает за недели по мере реализации. Описание протокола взаимодействия — возможно, за месяцы. Архитектурное решение сохраняет актуальность годами. Если свалить документы с разным жизненным циклом в одну папку, быстро станет невозможно понять, чему доверять: актуален ли этот план, или это артефакт прошлого периода.

На практике эти два измерения коррелируют: проектная документация чаще бывает быстроживущей, а кросс-проектная — долгоживущей. Но совпадение не полное: стратегический план по конкретному проекту может жить месяцами, а кросс-проектный бэклог обновлений зависимостей — устаревать за недели. Тем не менее, для верхнеуровневой структуры scope оказывается более надёжным критерием: он определяет, где искать документ, в то время как lifecycle определяет, насколько ему доверять.

Отсюда основное разделение — по scope:

workbench/
├── projects/<name>/ Документация конкретного проекта
│ ├── service-alpha/ Архитектура, бэклоги, планы
│ ├── frontend/ UI планы, improvements
│ └── compute-engine/ Migration plan, протокол

├── domains/ Кросс-проектные знания
│ ├── platform/ Реестр, топология, CI, решения
│ ├── infrastructure/ Железо, модели, инциденты
│ └── engineering/ Практики разработки

├── tools/ Скрипты валидации и автоматизации
├── workspaces/ .code-workspace файлы
└── .cursor/rules/ Глобальные cursor rules

projects/<name>/ — документация, привязанная к конкретному репозиторию: планы, бэклоги, логи рабочих сессий, заметки по расследованию проблем. При работе с конкретным сервисом агент заглядывает в его проектную папку за контекстом.

domains/<topic>/ — знания, пересекающие границы проектов: карта серверов, справочник библиотек, стратегические планы, архитектурные решения. Нужно агенту независимо от того, в каком проекте идёт работа.

Scope определяет навигацию: “как деплоить сервис X” — ищется в projects/X/, а “какие серверы вообще существуют” — в domains/platform/topology.md [5]. А lifecycle определяет доверие: документ с пометкой “обновлён 3 месяца назад” в projects/ — повод для подозрения, в domains/ — скорее всего, нормально.

Единый реестр проектов

В системе из десятков сервисов знание о состоянии разработки каждого из них неизбежно рассыпается: версия — в package.json, сервер деплоя — в CI-конфигурации, статус — в голове разработчика. Агент не может собрать это из разрозненных источников за разумное время. Нужен один документ, агрегирующий ключевые факты по всем проектам.

В моём workbench эту роль выполняет Project Registry — таблица, где по каждому проекту собрано:

Поле

Что даёт агенту

Версия в репо vs версия в production

Видит рассинхронизацию

Сервер и способ деплоя

Знает куда и как деплоить

Наличие CI/CD

Знает, есть ли pipeline

Зависимости от shared-библиотек

Видит граф зависимостей

Статус: active / legacy / stopped

Не тратит время на неактуальный код

На практике это означает: задача “добавь streaming результатов в подсистему бэктестинга” не начинается с рекогносцировки. Агент смотрит в реестр и сразу видит, какие сервисы задействованы, на чём они написаны, где развёрнуты и как взаимодействуют. Без реестра та же ориентация занимала бы минуты вызовов инструментов и намного большее окно контекста.

Архитектурные диаграммы в текстовом формате

Визуализация архитектуры — стандартная практика. Но выбор формата определяет, кто её потребитель. draw.io создаёт бинарный файл — LLM его не прочитает. Mermaid — текстовый, но описывает структуру декларативно, а не визуально: чтобы понять диаграмму, её нужно отрендерить. Для knowledge base, ориентированной на LLM, нужен формат, который читается как текст и при этом передаёт топологию.

ASCII-диаграммы — компромисс, который работает:

frontend UI ──→ backend API ──→ orchestrator

┌───────────────────┼──────────────────┐
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ worker-1 │ │ worker-2 │ │ worker-3 │
│ 24 cores │ │ 512 cores│ │ 512 cores│
└──────────┘ └──────────┘ └──────────┘

У этого формата есть и другие достоинства помимо LLM-совместимости: diff-friendly в git, рендерится в любом контексте (терминал, чат, email), не зависит от внешних инструментов. В моём workbench несколько таких диаграмм — по основным подсистемам. Агент видит, как данные текут через систему, не задавая вопросов.

Память о принятых решениях: ADR

У каждого проекта — и тем более у системы из десятков проектов — есть история архитектурных развилок: что рассматривали, что выбрали, что отвергли и почему. У человека эта память [6] живёт в голове, в истории переписок, в wiki. LLM не имеет доступа ни к чему из этого — и потому будет раз за разом предлагать варианты, которые уже были рассмотрены и отвергнуты. Причём каждый раз — убедительно аргументируя, потому что вариан�� может выглядеть сильным в изоляции, если не учитывать контекст конкретной системы: её ограничения, зависимости между компонентами, историю эксплуатации и причины, по которым альтернатива не подошла.

Architecture Decision Records (ADR) — способ сделать эту память явной и доступной. Суть формата — не в тяжёлой бюрократии, а в четырёх полях:

Контекст — что происходило, какая проблема решалась
Рассмотренные варианты — что оценивали, с плюсами и минусами
Решение — что выбрали и почему
Условия пересмотра — когда решение стоит переоценить

Последнее поле — ключевое. Без него ADR фиксирует прошлое, с ним — задаёт условия для будущего пересмотра. Пример:

# ADR-001: СУБД для аналитического pipeline
## Контекст
Текущая СУБД не поддерживает оконные функции для near-realtime агрегации.
## Варианты
A. Оптимизация текущей СУБД — ограничение фундаментальное, не решается настройкой
B. ClickHouse + потоковый ETL — полноценный SQL, materialized views
C. TimescaleDB — на порядок медленнее на аналитике тиковых данных
## Решение
Выбран вариант B.
## Условия пересмотра
Текущая СУБД добавит оконные функции и continuous aggregation.

Без ADR агент предложит “а давайте попробуем вариант C”. С ADR — увидит, что C уже рассматривался, и при необходимости задаст правильный вопрос: “условия пересмотра не изменились?”. Без ухода сессии в нежелательное пространство вариантов.

Несколько ретроспективных ADR по ключевым развилкам системы — и цикл “обсудить → забыть → обсудить заново” разорван.

Разделение по уровням значимости: от общего к частному

Есть знания, общие для всех проектов: стиль кода, подход к обработке ошибок, формат коммитов. И есть специфика конкретного проекта: какие API-обёртки использовать, какие паттерны запрещены в этой кодовой базе. Если смешать оба уровня в одном месте, при каждом новом проекте придётся заново описывать общие правила. Если разделить — общее наследуется автоматически.

В Cursor этот принцип реализуется через иерархию правил:

workbench/.cursor/rules/     → Общие для всех проектов
  workbench.mdc              → Структура системы, навигация
  robust-implementation.mdc  → Defensive coding, анализ blast radius
  
project/.cursor/rules/       → Специфичные для проекта
  project-overview.mdc       → Стек, модули, команды запуска
  anti-patterns.mdc          → Что запрещено в этом проекте
  git-commits.mdc            → Формат коммитов

Общие правила из workbench действуют в каждом workspace. Проектные могут ужесточить, но не ослабить. Пример общего правила:

> Не использовать console.log для ошибок. На фронтенде — console.error с префиксом модуля, на бэкенде — structured logger сервиса.

Пример проектного ограничения:

> Не вызывать fetch() напрямую из компонентов. Использовать обёртку api_request(), которая добавляет retry, нормализацию ошибок и заголовки авторизации.

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

Параллельные сессии и когнитивная нагрузка

При работе с LLM естественный ритм — несколько проектов параллельно: один в активном фокусе, в остальных агент работает автономно и ждёт ревью. Узкое место в этом процессе — не LLM, а человек. Нужно мгновенно отличать окна друг от друга, чтобы не отправить промп�� не в тот проект и не сбиться с “потока”.

Простое решение — визуальная кодировка workspace. В каждом .code-workspace задаётся уникальный цвет заголовка окна и статус-бара:

"workbench.colorCustomizations": {
  "titleBar.activeBackground": "#2d1f3d",
  "statusBar.background": "#2d1f3d"
}

Цвета назначаются по ассоциации [7] с проектом: фронтенд — фиолетовый (цвет UI), Rust-сервис — оранжевый, сервис автоматизации — зелёный. Приглушённые тёмные тона, различимые периферийным зрением [8]. Взгляд на полоску внизу экрана — и контекст переключился. Эта мелочь экономит когнитивный ресурс, который лучше потратить на содержательное ревью.

Что это меняет на практике

По отдельности каждый из описанных элементов — реестр, топология, ADR, уровни правил — решает свою узкую задачу. Вместе они работают как система: агент начинает продуктивную работу не через несколько минут ориентации, а сразу.

Без knowledge base: “Расскажи про архитектуру подсистемы бэктестинга” → минуты на поиск по файлам, чтение разрозненных README, уточняющие вопросы.

С knowledge base: Агент открывает реестр, видит задействованные сервисы и их связи, читает топологию для понимания потоков данных, заглядывает в план проекта для деталей. Через короткое время он ориентируется и может работать.

Без ADR: “Может, перейдём на другую СУБД?” → повторение дискуссии, которая уже была три месяца назад, с теми же выводами. Или забег по старым “граблям”.

С ADR: Агент видит, что альтернатива рассматривалась и отвергнута. Задаёт единственный нужный вопрос: «условия пересмотра не изменились?» — ответ занимает секунды.

При нескольких рабочих сессиях в день разница накапливается в часы. Это не абстрактная «продуктивность» — это конкретное время, которое уходит на решение задач вместо повторения пройденных шагов.

Для следующих частей есть наработки – как не дать knowledge base устареть: автоматическая валидация, агрегация бэклогов, жизненный цикл документов. Мета-ловушки рекурсивной оптимизации и как с ними бороться.

Автор: VsBirdEye

Источник [9]


Сайт-источник BrainTools: https://www.braintools.ru

Путь до страницы источника: https://www.braintools.ru/article/27157

URLs in this post:

[1] ошибки: http://www.braintools.ru/article/4192

[2] опыт: http://www.braintools.ru/article/6952

[3] долговременной памяти: http://www.braintools.ru/article/9500

[4] Повторение: http://www.braintools.ru/article/4012

[5] topology.md: http://topology.md

[6] память: http://www.braintools.ru/article/4140

[7] ассоциации: http://www.braintools.ru/article/621

[8] зрением: http://www.braintools.ru/article/6238

[9] Источник: https://habr.com/ru/articles/1010478/?utm_source=habrahabr&utm_medium=rss&utm_campaign=1010478

www.BrainTools.ru

Rambler's Top100