Недавно в одном инженерном обсуждении я снова услышал аргумент, который в последнее время звучит всё чаще:

Зачем строить RAG или retrieval-слой, если современные модели уже умеют работать с огромным контекстом?

На первый взгляд аргумент разумный. Контекстные окна растут. Модели становятся лучше. Агент может сам ходить по проекту, открывать файлы, читать соседние модули, искать зависимости и постепенно собирать картину.

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

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

Но в этом споре часто смешивают две разные вещи.

Контекст — это ёмкость. Retrieval — это способ выбрать, что именно в эту ёмкость положить.

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

Где всё ломается: гетерогенность системы

Возьмём типичную задачу: нужно понять, как создаётся заказ — от формы на сайте до внешней системы — и почему заказ, оформленный покупателем, не доехал до 1С или получил неправильный статус.

Цепочка выглядит примерно так: фронтенд отправляет форму → API-сервис принимает запрос → бэкенд валидирует данные → часть состояния пишется в БД → событие уходит в очередь → обработчик забирает его и делает SOAP-вызов во внешнюю систему → регламентное задание в 1С подхватывает изменение → статус заказа обновляется на сайте.

Плюс где-то рядом: SQL с бизнес-логикой, фоновые задачи, feature flags, конфиги, документация, тесты.

Часто каждое звено — отдельный репозиторий, отдельный язык, нередко и отдельная команда. Агент, ограниченный одним репозиторием, видит только своё звено. Типичный симптом: статус заказа на сайте берётся из поля, которое бэкенд уже переименовал, тогда как регламентное задание в 1С по-прежнему пишет в старое. Видимость корректной работы сохраняется ровно до тех пор, пока фактический статус не начинает расходиться с тем, что видит пользователь.

В такой ситуации задача уже не сводится к «прочитать побольше кода». Нужно восстановить связи между разными типами объектов: методами, DTO, контрактами, таблицами, очередями, регламентными заданиями, конфигами, документацией.

Вся эта разнородность и есть источник сложности: она начинается не там, где кода много, а там, где появляется много связей между разнотипными сущностями. Большой контекст сам по себе их не нормализует: он может принять больше текста, но кто-то должен решить, какой текст туда положить.

Контекстное окно — это не размер всей задачи

Работая над одной задачей в Codex CLI, я за сессию набрал 110 миллионов input-токенов, при этом контекстное окно модели составляло 258 тысяч. Это не значит, что в проекте было 110 миллионов уникальных токенов кода. Важнее другое: контекст пересобирался снова и снова на каждом шаге.

Ведь контекстное окно — не размер всей задачи, а максимальный объём информации, который модель может получить на одном шаге: в одном запросе, одном агентском проходе или одной локальной подзадаче.

А работа с реальной системой быстро превращается в цепочку таких шагов:

  • найти релевантные файлы;

  • прочитать модуль;

  • уточнить зависимости;

  • открыть соседний сервис;

  • проверить контракт;

  • сравнить версии;

  • построить гипотезу;

  • сгенерировать патч;

  • прогнать тесты;

  • вернуться и пересобрать картину.

Отдельный такой шаг часто помещается в окно — сотни тысяч токенов для одного обращения действительно много. Но большая инженерная работа живёт не в одном окне, а в последовательности таких обращений, и контекст для каждого собирается заново.

Контекст — это не память системы

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

Проблема в том, что механизм сжатия не знает заранее, какая деталь окажется критичной через несколько шагов. Например, агент может забыть, что поле в контракте очереди переименовали, и продолжать генерировать патч под старое имя. Приходится заново подсвечивать ограничения, которые уже были согласованы раньше в той же сессии.

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

Почему architecture.md и memory.md не решают проблему полностью

Частый контраргумент: можно просто положить в каждый репозиторий architecture.md, service.md или memory.md, и пусть агент читает эти файлы перед работой.

Это хорошая практика. Документация по архитектуре, описание сервиса, краткая память проекта — всё это помогает. Такие файлы могут быть отличной точкой входа.

При этом важно не путать точку входа с источником истины. Любой summary, на который команда начинает опираться как на знание о системе, требует обслуживания. И вопрос не в том, устаревает ли architecture.md, а в том, сколько стоит поддерживать его актуальность и насколько этот процесс поддаётся проверке.

Например, summary может говорить «статус заказа формируется бэкендом», но после изменений часть статуса уже приходит из фоновой задачи или 1С. Агент, опирающийся на такой документ, может уверенно давать неверный ответ.

Поэтому доверять такому файлу всегда одинаково нельзя. Если architecture.md старше последних изменений в связанных модулях, доверие к нему стоит снижать; если же его утверждения подтверждаются кодом, тестами, контрактами или графом вызовов — оно может быть выше. То есть у источника есть не бинарный статус «актуален / неактуален», а две оценки: freshness — свежесть источника и confidence — степень доверия к нему.

С memory.md похожая история. Если агент ведёт общий файл памяти проекта, это может быть полезно. Однако очень быстро такой файл превращается в новый контекстный монолит. Если читать его целиком — раздуваем контекст. Если резать на части — снова делаем retrieval. Только теперь этот retrieval спрятан за markdown-файлом.

Если вокруг markdown появляются чанкинг, поиск, freshness/confidence и ссылки на источники — это уже retrieval-слой, выросший из документации.

Стоимость обслуживания знания

Допустим, после каждого PR мы хотим обновлять architecture.md автоматически. Но чтобы обновить такой файл, агенту всё равно нужно понять: что изменилось, какие модули затронуты, какие контракты поменялись, какие связи стали другими.

То есть мы снова приходим к задаче анализа изменений и связей — той же самой задаче, которую должен выполнять retrieval-слой. Только результат пишется в markdown, а не в индекс.

Поэтому в ряде случаев дешевле и надёжнее доиндексировать diff коммита, обновить эмбеддинги, пересчитать затронутые связи и скорректировать confidence у связанных источников, чем каждый раз запускать полноценного агента для переписывания документации.

И «дешевле» здесь — не про одни деньги. Если каждый раз заново восстанавливать цепочку «фронт — API — очередь — обработчик — 1С — статус на сайте», мы платим токенами, latency, лимитами, вниманием инженера, риском ошибки и необходимостью повторной проверки.

Retrieval — это не только векторный поиск

Я сознательно говорю не «RAG», а «retrieval-слой».

В узком смысле RAG часто понимают как векторный поиск по чанкам документации или кода. Для работы с кодовой базой этого недостаточно.

Для вопроса «кто вызывает метод X?» векторный поиск может быть не лучшим инструментом — здесь точнее сработают индекс символов или граф вызовов.

Для вопроса «что сломается, если поменять контракт между сервисами?» нужен уже другой сценарий: сначала retrieval-слой находит затронутые места — вызовы, DTO, схемы, обработчики очередей, тесты. Потом LLM объясняет последствия, риски и план миграции.

Для вопроса «где используется это поле?» может понадобиться смотреть не только код, но и SQL, фоновые задачи, BI-витрины, очереди и внешние интеграции.

Практический retrieval-слой для инженерной работы не сводится к «одному векторному индексу». Внутри гибридной системы поиска и нормализации знания о системе могут работать разные механизмы:

  • поиск по ключевым словам;

  • grep;

  • AST;

  • LSP;

  • индекс символов;

  • граф вызовов;

  • векторный поиск;

  • граф зависимостей;

  • индексация документации;

  • анализ diff после коммита.

Общая задача одна: найти релевантные источники и связи между ними, до того, как модель начнёт рассуждать.

Retrieval-слой — это код, не LLM

Важное уточнение, которое часто упускают: в инженерном retrieval-слое основную подготовку данных делает код, а не языковая модель.

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

Это важно понимать, чтобы не представлять retrieval как «ещё одного агента, который заранее прочитал весь проект»: агент каждый раз восстанавливает картину заново, тогда как индекс существует независимо от сессии.

Freshness и confidence: источники не одинаково надёжны

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

Поэтому хороший retrieval-слой не просто находит текст, похожий на запрос. Он показывает основания доверия: ссылки на первоисточники, дату последнего изменения, связи с другими источниками.

Что делать с конфигами, feature flags и секретами

Здесь проходит важная граница.

Часть поведения системы зависит от runtime-конфигурации: feature flags, переменных окружения, настроек очередей, внешних сервисов. Это не значит, что retrieval-слой должен индексировать секреты или значения чувствительных переменных. Пароли, токены и production-секреты в индекс тащить не нужно.

Зато retrieval-слой может зафиксировать сам факт зависимости от внешней конфигурации. Обнаружив ветвление по feature flag, он возвращает этот фрагмент не просто как код, а с пометкой о внешней зависимости.

Это честнее, чем делать вид, что код полностью объясняет runtime-поведение. И безопаснее, чем пытаться индексировать всё подряд.

Код остаётся главным источником истины о логике. Впрочем, сложная система живёт не только в коде. Поэтому retrieval-слой должен уметь показывать границы уверенности: где мы знаем, как работает система, а где нужен дополнительный источник.

Локальная память агента — это не сервис

Остаётся ещё одно смешение — память самого агента. По ходу работы у него остаётся agent trace, а по запросу пользователя может вести и memory.md. Кажется, что важное тем самым сохранено и никуда не денется.

На практике это всё ещё локальная память конкретной сессии. Она помогает одному проходу, но не становится знанием команды о системе. Retrieval-сервис устроен иначе: индекс живёт отдельно от конкретного чата, обновляется вместе с кодом, переиспользуется в review, onboarding и анализе изменений, даёт ссылки на источники.

По моему опыту, retrieval-слой особенно помогал не там, где было просто много файлов, а там, где нужно было быстро восстановить связи.

Итог

Контекстное окно — это размер рабочего пространства модели на одном шаге. Большой контекст увеличивает это пространство, а retrieval-слой снижает стоимость поиска нужных источников, связей и ограничений до того, как модель начнёт рассуждать. Он нужен не потому, что модель не может прочитать много текста, а потому что в сложной системе каждый раз заново выяснять, откуда берутся данные, кто кого вызывает и чему можно доверять — слишком дорого.

Поэтому вопрос не в том, нужен ли «RAG ради RAG», а в том, когда команде понадобится инфраструктура знания о системе. Если production держится на том, что агент каждый раз «как-нибудь разберётся» — это не инфраструктура, а импровизация. А на импровизации production не строят.