
За последний год инструментов для агентной разработки стало столько, что в них легко потеряться: одни обещают сохранять контекст между сессиями, другие — «понимать» всю кодовую базу целиком, третьи — память, планирование и автономность в одном флаконе. На GitHub каждую неделю появляются репозитории с внушительным (и не всегда честно заработанным) числом звёзд, которые обещают всё и сразу, а на поверку оказываются README-проектами; другие честно работают на демо-репозитории и падают с OOM при первой же встрече с реальным энтерпрайз-проектом — и так далее, список можно продолжать долго.
Осенью прошлого года я пользовался связкой Claude + RooCode + семантический поиск на Ollama, и это был мой основной рабочий инструмент — ровно до того момента, как связка перестала работать (об этом чуть ниже). Я решил полностью пересесть на Claude Code и начал искать замену семантическому индексу, но так её и не нашёл: альтернативы для меня просто не работали — на монолите в 3,5M+ строк они либо индексировались часами, либо требовали отдать код в облако (и заплатить немалую сумму за эмбеддинги!!!), либо поддерживали Ruby, мягко говоря, номинально. В итоге я принял непростое для себя решение сделать собственный инструмент — начав с форка простого движка семантического поиска на Ollama + Qdrant — и, что характерно, сделал: полностью локальный, чтобы ни код, ни его производные (индекс, эмбеддинги, граф вызовов) никуда не уезжали. На сегодняшний день на него ушло больше полугода (и, честно признаюсь, не одна сотня чашек чая).
И вот какие проблемы я при этом решал:
Агент не находит логику, которую я нахожу за две минуты. Grep, сотня совпадений, прочитанные подряд файлы и выжженный контекст — при том, что по всей кодовой базе то, что продукт называет invoice, в коде называется bill, а payment и вовсе живёт под третьим именем; такой разрыв словарей никаким количеством итераций grep не берётся.
Индексация большого монолита занимает часы. У исходного проекта, который я форкнул, — от четырёх до десяти часов на полную индексацию и сорок минут на инкремент по сотне коммитов; у Roo — пятьдесят-шестьдесят минут. Мне нужны были минуты.
Ruby поддерживается номинально. Чанки режутся не по AST, качество оставляет желать лучшего — а для Ruby стилистика проекта — это половина точности генерации. Про разрешение вызовов в динамическом языке, где половина кода — DSL (Rails-макросы,
delegate,scope, плюс DSL каждого второго гема), в инструментах для агентов в тот момент никто даже не заикался.Агент копирует первый похожий код, а не правильный. Он не отличает стабильный код от хотспота, не знает, чей он и где чаще всего всплывают баги — то самое знание, которое обычно живёт в голове у сеньора.
Агент не видит структуру. Кто вызывает этот метод? Что заденет изменение? Где циклические зависимости? Для него это просто соседние файлы.
Агент переизобретает существующее. N-й retry-хелпер, N-я валидация, N-й форматтер дат — единственный класс ущерба, у которого нет дублирующей защиты: галлюцинации ловит компилятор, стиль — ревью, регрессии — тесты, а N-й корректный способ сделать то же самое не ловит никто.
Даже с подключённым индексом агент им не пользуется — или пользуется невпопад. По привычке идёт в grep, для точного имени символа зовёт семантический поиск вместо мгновенного lookup’а, игнорирует режимы ранжирования и путает параметры. Подключить MCP-сервер — ещё не рабочий процесс: знание «когда и какой инструмент» должно приезжать вместе с инструментом.
Почему эти проблемы придуманы не мной
Впервые я попробовал Claude в июне 2025 — и он меня разочаровал. Не потому, что модель была слабой: ему было попросту сложно ориентироваться в моём рабочем репозитории — большом монолите US B2B SaaS, где, чтобы найти нужную логику, надо знать, как она исторически называется. Лимит токенов сгорал за минуты в обмен на сомнительные результаты, и через пару недель подписку я отменил. Всё изменилось с появлением семантического поиска в Roo: я по достоинству оценил преимущества семантического индекса — несмотря на недостатки, которые тогда казались мелочами. А потом, месяц за месяцем работая с индексом, я начал эти недостатки замечать по-настоящему — те самые пункты 4–6 из списка выше.
Но настоящий поворот случился уже после появления TeaRAGs (да, так я назвал свой собственный инструмент, о происхождении названия ниже) и его демонстрации внутри компании. Мой CTO (ex-FAANG) задал один вопрос: «А как твой движок отличит хороший код от плохого?» Ответа у меня в тот момент не было. Зато с этого вопроса начался ресёрч, который не закончился до сих пор — и по ходу которого выяснилось, что ни одна из проблем выше не придумана мной: под каждой лежит либо исследование (местами полувековой давности), либо ход кого-то из косаток китов, либо и то и другое. Ниже — короткая выжимка по каждой.
Важная оговорка перед списком. Часть этих проблем — прежде всего первую — решает уже семантический поиск сам по себе: как инструмент discovery по смыслу, а не как naive RAG, который критикуют за автоматическую инъекцию «похожего» контекста на всякий случай. Остальные семантика не закрывает — и ниже видно, почему.
1. Агент не находит логику — навигация и контекст
Агент без индекса ищет текстом и читает файлы подряд — и чем больше он прочитал, тем хуже думает. Это не особенность Claude и не вопрос размера контекстного окна: деградация на длинном входе измерена на всех проверенных топовых моделях, а выигрыш от контекста, подтянутого по смыслу, — на основных бенчмарках репозиторного кода.
Context Rot — исследователи из Chroma (2025) взяли 18 топовых моделей на то время, включая GPT-4.1, Claude 4 и Gemini 2.5, и раз за разом удлиняли им вход на одних и тех же простых задачах уровня «найди строчку». Качество падало у всех — неравномерно и плохо предсказуемо, — а нерелевантные фрагменты в контексте роняли его ещё сильнее. Именно такие фрагменты агент и нагребает grep-циклом.
Lost in the Middle — Лю, Лян и коллеги из Стэнфорда, UC Berkeley и Samaya AI (TACL 2024) двигали нужный документ по длинному контексту и получили U-образную кривую: то, что лежит в начале или в конце, модель находит, то, что в середине, — теряет, причём даже модели, специально обученные на длинный контекст.

NoLiMa — исследователи из LMU Munich и Adobe Research (ICML 2025) убрала из классического теста «иголка в стоге сена» буквальные совпадения слов, чтобы модель искала по смыслу, а не по подстроке. Взяли 13 моделей с заявленными 128K+ токенов — и уже на 32K одиннадцать из них упали ниже половины собственного качества на коротком контексте. А 32K — это меньше, чем агент нагребает за пару минут grep-чтения.

CrossCodeEval — исследователи из AWS AI Labs и Колумбийского университета (NeurIPS 2023) собрали бенчмарк дополнения кода, в котором правильно дописать строку невозможно без знания соседних файлов, и сравнили модели с подтянутым контекстом и без. Без кросс-файлового контекста проваливаются даже сильные модели; стоит добавить в промпт фрагменты из других файлов, найденные ретривером, — точность заметно растёт. Показательная деталь: самая простая точка отсчёта в их сравнении ретриверов — BM25, то есть полноценный поиск по ключевым словам.
Better Call Grep — работа посвежее (ISSTA 2026), вышедшая через три года после CrossCodeEval. Результат у неё реальный: на том же CrossCodeEval grep-поиск обогнал RAG на BM25 — 38,6% точных совпадений против 25,0% на Python и 41,7% против 29,6% на Java. Но вывода «grep делает RAG и графы ненужными» из неё не следует, и дело в методологии. Главное — работа меряет repository-level code completion, а не понимание репозитория и не агентную разработку; это принципиально разные задачи. К тому же бенчмарк заметно подыгрывает grep: в CrossCodeEval отбирают строки, которые используют имя из другого файла, импорт этого имени остаётся в коде над курсором, а запросы к ripgrep модель пишет именно по этому коду. Grep находит то, что уже названо в двух шагах от курсора. Где имени нет, авторы сами фиксируют провалы: частые слова вроде
init,configиrunтонут в нерелевантных совпадениях, а неявные связи вроде наследования не находятся. Поиска места по описанию задачи, навигации и решения issue в работе нет вовсе — а первая проблема из моего списка ровно про это: в тикете нет ни одного имени из кода.Собственный замер. На одном из этапов тестирования инструмента мне попалась рабочая задача по поиску и устранению бага, и я решил «найти баг» двумя путями — с TeaRAGs и без. На вход в обоих случаях подавался один и тот же тикет: текстовое описание симптома, без единой детали о коде — ни имени класса, ни файла, ни стектрейса. С голым grep’ом агент искал причину 10+ минут, с TeaRAGs поиск вместе с генерацией отчёта занял всего 40 секунд.
У Claude Code есть на это ответ, и даже не один. Сама Anthropic от RAG не отказывается: в Claude Projects он включается выборочно — когда знания проекта подходят к пределу контекстного окна, Claude достаёт нужное поиском по базе проекта, а не грузит её целиком (RAG for projects). А для больших кодовых баз документация Claude Code прямо советует: если в организации уже есть поиск по коду или RAG-индекс, подключите его как MCP-сервер, чтобы Claude обращался к нему, а не читал файлы подряд (Set up Claude Code in a monorepo or large codebase). Если такого индекса нет, остаётся встроенный ответ — Explore: отдельный сабагент, который гоняет grep у себя и возвращает главному агенту короткую выжимку. И вот почему это не помогает: у сабагента такое же контекстное окно и та же деградация на длинном входе — просто теперь её не видно. Выжимка, которую он отдаёт, — его догадка о том, что важно; если она не попала, главный агент запускает его ещё раз. И ещё раз. Десять минут из моего замера выше — это последовательные запуски, а не время самого grep: он-то отрабатывает за секунды. Сторонний замер говорит то же — grepai прогнал одни и те же пять вопросов по Excalidraw (155K строк TypeScript, январь 2026): агент с grep запустил пять сабагентов и сделал 139 вызовов инструментов, агент с семантическим индексом — ноль сабагентов и 62 вызова; свежих input-токенов 51 147 против 1 326, токенов на создание кэша 563 883 против 162 289, счёт $6,78 против $4,92. Сабагент решает только, куда складывать грязь. Я хотел получить инструмент, чтобы грязи не было в принципе — или было в разы меньше.
Именно эту проблему семантический поиск и закрывает.
2. Индексация занимает часы — индустрия считает индекс дорогим
Индекс кода — дорогая штука, и индустрия это знает: одни киты от него отказались, другие унесли его в собственное облако. Дорогой он, правда, для вендора «на всех» — а не для команды, которая индексирует собственный монолит.
The RAG Obituary — Николя Бустаманте (2025) написал некролог RAG, который попал на главную Hacker News: тезис в том, что агенты вроде Claude Code не извлекают фрагменты, а расследуют — параллельным grep’ом и чтением файлов, — и RAG остаётся костылём эпохи маленьких контекстов (да-да, особенно для легаси-монолитов на три с половиной миллиона строк). Anthropic индекс действительно не строят, и их резоны понятны: эмбеддинги на миллионы чужих кодовых баз, их свежесть и хостинг чужого кода на масштабе флота.
How Cody understands your codebase — Sourcegraph (2024) рассказали, как устроен контекст в Cody, и между делом сообщили, что от эмбеддингов отказываются — «leaving embeddings behind (for now)». Причины назвали прямо: чтобы построить эмбеддинги, весь код приходилось отправлять третьей стороне — в OpenAI, чьей моделью они пользовались, — а клиентам, мягко говоря, не нравилось, что их код туда уезжает; создание и обновление эмбеддингов ложилось лишней сложностью на администраторов; и, наконец, векторная база растёт вместе с кодом, а поиск по ней у организаций со ста тысячами репозиториев оказался слишком дорогим — настолько, что мешал сделать мультирепозиторный контекст. Заменили собственным keyword-поиском, который код никуда не отправляет.
Cursor: Security — Cursor индексирует кодовую базу по умолчанию и целиком у себя: файлы уезжают на их серверы, там режутся на чанки и превращаются в эмбеддинги, которые хранятся в облачной векторной базе (Turbopuffer); исходники при этом не хранятся — только эмбеддинги и обфусцированные метаданные. Стоимость индекса при этом никуда не делась — она просто зашита в подписку.
JetBrains Context — JetBrains (июль 2026) выпустили семантический индекс репозиториев для кодовых агентов и ответили на стоимость тем же способом: индекс живёт у них в облаке на GCP.
Собственный опыт — эмбеддинги съедают 92% wall time полного индекса, и причин тому две. Первая: на собственной шкуре я прочувствовал, что выстроить оптимизированный конвейер индексации — это месяцы работы и неплохие навыки параллельного программирования; в общем, задача не простая. Вторая, и главная: пропускная способность встроенной или домашней видеокарты — она и задаёт потолок, сколько бы параллельных запросов на эмбеддинг ни отправлять.

Время до готового семантического индекса — только эмбеддинги; git-обогащение и граф считаются отдельно, в фоне. Один и тот же репозиторий, одно и то же железо; замеры автора — январь 2026 (форк, RooCode) и июль 2026 (TeaRAGs). Разброс 4–10 часов у форка — это разные комбинации параметров движка, возможно, разные модели эмбеддингов и неидемпотентные прогоны: каждый перезапуск давал своё время, не совпадающее с предыдущим. 22 минуты TeaRAGs на диаграмме — голый MacBook и только эмбеддинги; те же 22 минуты в тексте ниже — полный прогон вместе с графом, но на внешней GPU: совпадение чисел, не величин.
3. Ruby поддерживается номинально — и не просто так
Ruby в этой области — белое пятно, и причина не в лени разработчиков инструментов, а в самом языке: половина вызовов существует только в рантайме, а ещё половина написана на DSL.
Графовые бенчмарки локализации кода — RepoGraph, LocAgent (ссылки в п. 5) — построены на Python-репозиториях. Ruby в агентных бенчмарках встречается только в виде задач «почини issue целиком» (SWE-bench Multilingual), а публичного замера, какую долю вызовов статический граф разрешает на Rails, я не нашёл.
Семантика языка:
method_missing,send(verb)с переменной вместо литерала,define_method("#{x}")не оставляют в AST ни одного ребра — никакой парсер их оттуда не достанет. Это свойство Ruby, а не чья-то лень.Rails-DSL и DSL каждого второго гема —
has_many,scope,delegate,validates,page(2).per(20)— вызовы, которых в AST нет как вызовов. Язык, на котором честный граф вызовов строить труднее всего.

Что резолвер TeaRAGs видит, а что честно помечает как «неоднозначно». По research-документу проекта, июнь 2026.
Инструменты для Ruby, конечно, есть — но не те. Sorbet — по сути надстройка для статического анализа поверх ручных аннотаций, которые в живом Rails-проекте почти никто не пишет, а не граф вызовов. Ruby LSP далёк от идеала: на моём рабочем проекте его индексация просто ломается, а поиска одного символа можно ждать минутами. Граф вызовов для неаннотированного Rails, который понимает DSL —
delegate,scope, ассоциации — и честно показывает, какую долю вызовов разрешил, в инструментах для агента на январь 2026-го мне не встречался: CodeGraphContext сопоставлял Ruby-вызовы по имени, а Serena отдаёт ссылки через Solargraph, а не граф.
4. Агент копирует похожее, а не правильное — история важнее похожести
Как отличить хороший код от плохого, наука знает уже двадцать лет: не по тому, как он выглядит, а по тому, как он живёт — сколько его переписывали, кто и зачем. Под это есть отдельная конференция, а до агентов это знание доходит с трудом — пофайловыми отчётами, а не посчитанным по отдельным методам сигналом в поиске.
Use of Relative Code Churn Measures to Predict System Defect Density — Нагаппан и Болл из Microsoft Research (ICSE 2005) взяли Windows Server 2003 и проверили, что на самом деле предсказывает дефекты. Абсолютные метрики churn — объём добавленного, удалённого и изменённого кода — оказались плохими предикторами; относительные, нормированные на размер компонента и длительность изменений, отличали проблемные бинарники от чистых с точностью 89%.
relativeChurnв TeaRAGs — метрика из их семейства.Don’t Touch My Code! — Бёрд, Нагаппан и коллеги (Microsoft Research, FSE 2011) на Windows Vista и Windows 7 связали владение кодом с качеством: чем больше в компоненте миноритарных контрибьюторов — тех, на кого приходится меньше 5% правок компонента, — тем больше в нём дефектов, а концентрированное владение — наоборот.
Bug Prediction at Google и Does Bug Prediction Support Human Developers? — Google (2011) сделали предсказание багов по взвешенной во времени истории фиксов внутренним инструментом, а затем (ICSE 2013) честно опубликовали, что поведение разработчиков не изменилось: файл помечен «опасным», а как снять пометку — непонятно, и её просто игнорировали. Привет,
bugFixRate— и привет, седьмая проблема.Revisiting Process versus Product Metrics — Маджумдер, Моди и Мензис (NC State, EMSE 2022) перепроверили выводы малых исследований на 722 471 коммите из 700 GitHub-проектов и построили предсказатели дефектов двух видов: по process-метрикам (как код живёт — сколько его меняли, сколько людей, как давно) и по product-метрикам (как он выглядит — размер, цикломатика, связность). Первые находят 98% реально дефектного кода, вторые — 44%, то есть пропускают больше половины. Второй замер — вероятность того, что модель поставит случайно взятый дефектный участок кода выше случайно взятого чистого (в статистике это называется AUC, area under the ROC curve: 100% — упорядочивает безошибочно, 50% — как монета): у process-метрик 95%, у product-метрик 54%. Метрики «как код выглядит» дефекты практически не предсказывают.

Данные Majumder, Mody & Menzies (EMSE 2022), медианы по 700 проектам; перерисовано.
Your Code as a Crime Scene и CodeScene — Адам Торнхилл превратил эту школу в методику и продукт: хотспот = сложность × частота изменений; в одном из его кейсов (400 KLOC) на четыре процента кода пришлось 72% дефектов.
Mining Software Repositories — под всё это с 2004 года существует отдельная площадка: сначала воркшоп при ICSE, с 2008-го конференция.
Ответ на вопрос CTO в литературе был задолго до самого вопроса — я его просто не читал.
5. Агент не видит структуру — код это граф
Граф кода для агента — не мода, а измеренный выигрыш: три независимые группы показали, что структура репозитория поднимает агентам долю реально закрытых задач, а один вектор на чанк вопросы вида «кто кого вызывает» не выражает — предел задаёт размерность вектора.
RepoGraph — Оуян и коллеги (ICLR 2025) проверили, что даёт агенту граф кода, на самом жёстком бенчмарке из существующих — SWE-bench: агенту выдают реальный issue из GitHub-репозитория, и задача считается решённой, только если его патч проходит родные тесты этого репозитория. Граф они сделали отдельным модулем и подключили к четырём готовым агентным системам — в промпт или отдельным действием агента. Доля решённых задач выросла у всех четырёх: у простого RAG-подхода — вдвое, у полноценных агентов — на 9–12% относительно их же результата без графа; лучшая связка стала новым рекордом среди open-source-решений.

Данные RepoGraph (Table 2), перерисовано: у RAG-подхода граф удваивает результат, у агентных систем добавляет 9–12% относительно.
LocAgent — Чэнь, Тан и коллеги (Yale и коллеги, ACL 2025) представили репозиторий как граф файлов, классов и функций и дали агенту ходить по его рёбрам: 92,7% точности локализации файлов (Acc@5), а открытая Qwen-32B показала результаты, сопоставимые с проприетарными моделями, при затратах на 86% ниже и дала +12% к решённым issue.
CoCoMIC — команда AWS AI Labs (LREC-COLING 2024) научила модель дополнения кода подтягивать контекст из соседних файлов и получила +33,9% точных совпадений относительно модели, которая видит только текущий файл — а заодно показала, что без такого контекста модель галлюцинирует несуществующие методы и аргументы.
On the Theoretical Limitations of Embedding-Based Retrieval — Веллер, Боратко и коллеги (Google DeepMind и Johns Hopkins, ICLR 2026) доказали, что число подмножеств документов, которые один вектор способен вернуть как top-k, ограничено его размерностью, и собрали датасет LIMIT, на котором проваливаются даже лучшие эмбеддинг-модели: чем больше разных сочетаний документов должны возвращать запросы («A и B»), тем больше нужна размерность, и при фиксированной размерности часть сочетаний одним вектором не представима. Отсюда и «код — это граф, а не документ».
6. Агент переизобретает существующее — дивергенция решений
Здесь литературы почти нет — не потому, что проблемы нет, а потому что она новая: агенты пишут код быстрее, чем люди успевают заметить, что он уже был написан.
Ближайшая область — клон-детекция с двадцатилетней историей, и в ней семантические клоны (одно и то же, написанное по-разному) до сих пор самый трудный случай. Работ, которые формулировали бы дивергенцию как отдельный класс ущерба от агентов, я не нашёл — здесь честно.
Собственный пример. За иллюстрацией далеко ходить не пришлось — её нашёл в моём же репозитории мой же инструмент. В TeaRAGs живут два
RetryOptions, которые друг о друге не подозревают: один в адаптере эмбеддингов, с полямиmaxAttemptsиbaseDelayMs, второй — в удалении из синка, сmaxRetriesиbackoffMs. Оба написал я, с разницей в 58 дней, про одно и то же — и получил два несовместимых контракта для одного понятия. Компилятор при этом был доволен, тесты зелёные, ревью пройдено: ни один из привычных барьеров такого не ловит, потому что каждая половинка по отдельности безупречна.

Оба интерфейса приведены дословно (комментарии сокращены).
Чужой пример — ещё нагляднее. В компании, где я работаю (детали под NDA), тестовое задание устроено так: добавить фичу в живой open-source-проект — большой, взрослый, с историей. Кандидат на достаточно высокую позицию делал его в паре с топовой «передовой» моделью и всё сделал по учебнику: ещё на этапе плана спросил агента прямым текстом — «подтверди, что мы не пишем велосипедов». Агент назвал пару других мест, где стоит быть осторожнее, и пошёл писать код. Написал метод, который заново решает, откуда брать учётные данные запроса: параметр, заголовок, Bearer-токен, Basic. В том же файле уже было трое практически идентичных методов–клонов…
Дальше — лучшее. Новый метод агент вставил в пятидесяти строках от оригинала, а точнее — между оригиналом и его же комментарием «Returns the API key present in the request»: комментарий оторвался от метода, который описывал, и повис над дублем. Агент стоял буквально внутри существующей реализации и не узнал её. Потом кандидат попросил явное ревью ветки с оценкой критичности каждой проблемы; через четыре с половиной минуты агент нашёл одну мелочь и отчитался: «Других подтверждённых проблем не нашёл». В оценке тестового это стало критическим замечанием — центральный кусок интеграции ушёл бы на переделку.
Магии в этом провале нет, есть grep. У нового метода и трёх старых мест нет ни одного общего слова — ни в именах, ни в вызовах: повторяется поведение, а не текст. А вопрос «нет ли велосипедов» в команду поиска не превращается — чтобы грепнуть, нужно уже знать, что ищешь. Велосипед же по определению живёт под другим именем.
7. Агент не пользуется инструментами — инструмент ≠ рабочий процесс
Даже идеальный индекс бесполезен, если агент не знает, когда его звать: критики RAG правы в том, что поиск «на всякий случай» вредит, а протокол подключения инструментов ничего не говорит о том, как ими пользоваться.
Тот же The RAG Obituary и прочие некрологи RAG — критика поиска, который вызывается всегда и подсовывает контекст на всякий случай. Ответ практиков: планирование решает, когда звать поиск. Но тогда знание «когда и какой инструмент» должно приезжать вместе с инструментом — и первые шаги уже есть: в MCP под это есть поле
instructions, JetBrains учит агента выбирать между семантическим поиском и grep. Но подсказки, какое из нескольких представлений кода брать под какой вопрос, мне не встречалось.Model Context Protocol — Anthropic сделали протокол именно для того, чтобы инструменты жили снаружи агента; но протокол описывает, как инструмент вызвать, а не когда.
Собственный замер — обёртки над рабочими процессами агента с подмешанными сигналами индекса дают в eval’ах в среднем +71 процентный пункт на 136 кейсах.
Так что ни одна метрика здесь не придумана мной — у каждой научная родословная, местами полувековая. Новизна в том, что всё это посчитано по каждому методу, а не по файлу, и работает как сигнал ранжирования прямо в поиске агента — в реальном времени, внутри его рабочего цикла. И на ноутбуке, а не в чужом облаке.
Codebase Intelligence
Теперь перейдём к самому «вкусному». Что же такое Codebase Intelligence? Чем он отличается от простого семантического или кодграф-индекса? Как вы, наверное, уже поняли, он должен решать, как минимум, все проблемы, которые я описал.
Простой поиск по запросу «ai codebase intelligence» в любом известном поисковике может выдать вам что-то такое (сокращено):
AI Code Intelligence — это технология, которая использует нейросети и машинное обучение для глубокого понимания, анализа, генерации, рефакторинга и проверки программного кода. В отличие от простых генераторов текста, AI code intelligence «понимает» контекст всей кодовой базы, связи между модулями, архитектуру проекта и стандарты безопасности.
Далее в определении могут быть перечислены конкретные функции — контекстная генерация и автодополнение, AI code review, рефакторинг, генерация тестов и документации — и направления применения: ассистенты разработки, автоматизированное тестирование безопасности, агенты, которые сами создают pull request’ы.
Обратите внимание, где в этом определении спрятан фокус. Всё перечисленное — генерация, ревью, рефакторинг, тесты — это то, что агент делает. А ключевая фраза — «понимает контекст всей кодовой базы, связи между модулями, архитектуру проекта» — это то, что у агента должно откуда-то быть. Из коробки этого нет: голый агент видит проект как гору текстовых файлов, и мы уже разобрали, на скольких уровнях он при этом слеп. Значит, между агентом и кодовой базой нужен слой, который это «понимание» ему поставляет. Сам термин, кстати, старше нейросетевой волны: «code intelligence» у Sourcegraph — это go to definition и find references через границы репозиториев, то есть IDE-навигация, вынесенная в общий слой; приставка «AI» меняет не суть, а потребителя — теперь это не человек с десятью годами в проекте, а агент без памяти, без опыта и с конечным контекстным окном.
Отсюда моё рабочее определение. Codebase Intelligence Layer — это запрашиваемый слой между агентом и кодовой базой, который отдаёт агенту три представления кода — семантическое (что этот код делает), структурное (кто с кем связан) и историческое (как он жил) — с досье по каждому найденному фрагменту и знанием о том, когда какое представление нужно. Всё остальное из определения — генерацию, ревью, рефакторинг — агент делает сам, но уже с открытыми глазами.

Три представления кода, которые слой отдаёт агенту, — и по одному вопросу, на который отвечает каждое. Как это устроено внутри TeaRAGs — ниже, в разделе про сам инструмент.
Так чем это отличается от простого семантического индекса или от кодграфа? Примерно тем же, чем карта города отличается от списка улиц. Семантический индекс отвечает на один вопрос — «что похоже на мой запрос» — и на этом честно останавливается: он не знает, какой из двадцати похожих process вызывается на самом деле и который из них чинили каждый спринт. Граф вызовов отвечает на другой вопрос — «кто с кем связан», — но ничего не знает ни о смысле (спросить его «где мы считаем прорейт при апгрейде подписки» невозможно), ни о времени: ребро есть, а ведёт ли оно в код, переписанный вчера, — неизвестно. Аналитика по истории сама по себе — дашборд для людей: CodeScene покажет хотспоты, но агент к нему не ходит. Codebase Intelligence — это не сумма трёх индексов, а слой, где они склеены на уровне одного результата: любой найденный фрагмент приходит сразу с рёбрами графа и со своей историей, ранжирование может опираться на все три оси одновременно, а вместе с инструментами едет знание, когда какой из них звать. Уберите любую из осей — и получите один из уже существующих продуктов.
Если наложить это на проблемы из начала статьи, картина складывается. Агент, который не находит логику за разрывом словарей invoice/bill, и индексация, которая занимает часы, — это семантическое представление и то, насколько быстро оно строится. Ruby с его DSL и агент, который не видит, кто кого вызывает и что заденет правка, — это структурное представление, граф вызовов. Агент, который копирует первый похожий хотспот вместо стабильного кода, — это историческое представление, git-траектория. Переизобретение существующего — N-й retry-хелпер — ловится только всеми тремя сразу: похожее по смыслу, связанное по графу, живущее в истории. А агент, который даже с подключённым индексом ходит в grep по привычке, — это последнее слагаемое: знание «когда и какой инструмент», которое должно приезжать в комплекте с самими инструментами.
Технические требования к такому слою
С определением разобрались — теперь неприятная часть. Когда начинаешь строить такой слой не для демо-репозитория, а для монолита на три с половиной миллиона строк, у него обнаруживается список требований: по отдельности каждое звучит как банальность, а вместе они отсеивают почти всё, что я нашёл на рынке. Каждое из них я прочувствовал на собственной шкуре — примерно в том порядке, в котором они идут ниже.
Поиск и анализ быстрее grep-цикла — на порядок, а не на проценты. Весь смысл слоя в том, чтобы заменить десять ходов «grep → прочитать → переформулировать → повторить» одним. Значит, запрос должен возвращать ранжированный топ с досье за доли секунды — и возвращать в таком виде, чтобы агенту не пришлось после этого дочитывать файлы. Тот самый замер «10+ минут против 40 секунд» — это не скорость поиска как такового, это число ходов модели.
Гибридный поиск: эмбеддинги плюс BM25. Половина запросов агента — не «где считается прорейт», а «покажи
RetryOptions»: точное имя, которое он уже знает. Векторный поиск на таких запросах слаб — эмбеддинг размывает редкий идентификатор до «чего-то про повторные попытки», и точное совпадение запросто не попадает в топ. Здесь нужен лексический BM25 — тот самый, что стоит нижней планкой во всех бенчмарках. Он лучше grep по трём причинам: ранжирует, а не вываливает все вхождения в порядке файловой системы; учитывает редкость слова — совпадение по уникальному имени класса весит больше, чем по словуsave; и нормирует по длине документа, чтобы гигантский файл не выигрывал одним размером. Слой обязан уметь оба режима и склеивать их в один ранжированный список — а заодно лексическая половина продолжает работать, когда модель эмбеддингов недоступна.Индексация — минуты, инкремент — секунды. Полная индексация большого монолита должна укладываться в перерыв
на кофена чай, а не в рабочий день, иначе её просто не запускают. А после каждогоgit pullпереиндексироваться должны только изменённые файлы: часовой инкремент аннигилирует пользу инструмента, потому что индекс, который всегда протух, хуже отсутствия индекса — агент ему верит. И реиндекс не должен останавливать поиск.Локально по умолчанию. Ни код, ни его производные — эмбеддинги, граф, история — не покидают машину без явного решения владельца; модель эмбеддингов подключаемая: локальная Ollama или ONNX, а облачная — только если код отдавать не страшно.
AST, а не N строк. Чанк — целый метод или скелет класса, никогда не середина
if. Для языков с DSL (привет, Ruby) граф обязан понимать DSL как грамматику, а не как исключение.Честный граф. Резолвер не гадает: неоднозначный вызов — это «неоднозначно», а не ребро на одноимённый метод чужого класса. Доля разрешённых вызовов измеряется и публикуется по языкам — включая провалы.
Метрики конкретного проекта, посчитанные честно. «32 коммита в файл» — это много или мало? В свежем сервисе — экстрим, в десятилетнем монолите — обыденность. Агенту нужны не голые числа, а их смысл в контексте именно этого проекта: что здесь считается «часто меняется», что — «старый код», что — «один владелец». Значит, точка отсчёта должна вычисляться по распределению самой кодовой базы, а не браться из общемировых констант, причём для тестов отдельно от исходников — у тестов своя жизнь и своя статистика. И слой обязан молчать, когда данных мало: файл с двумя коммитами, один из которых фикс, — это не «половина коммитов багфиксы, тревога», а «история слишком короткая, чтобы судить».
Готов для агента, а не для человека с IDE. Один вызов — одно досье; инструменты живут за MCP, чтобы работать с любым агентом; и вместе с инструментами едет знание, когда какой звать, — иначе агент вернётся к grep.
Скромный след. Для монолита в 3,5M+ строк — порядка гигабайта на диске (квантизованный индекс плюс граф) и порядка гигабайта памяти у сервера. Ноутбук, без кластера и без облака.
Именно по этому списку я дальше и буду мерить — и чужие инструменты, и свой.
Кто уже строит такой слой — и почему мне не подошло
За эти полгода с лишним я исследовал довольно много инструментов из этой ниши — от семантических индексов внутри агентских харнессов до графовых MCP и аналитики по истории кода. Ниже — не обзор всего рынка, а короткая сверка с моим списком требований: что умеет каждый класс инструментов и на каком пункте он для меня отваливается.
Семантический поиск — здесь инструментов больше всего, и здесь же они ближе всего к моему списку требований. Roo и Kilo делают индексацию базой и делают это удобно, но индекс живёт внутри харнесса, а харнесс — чужое решение: в январе 2026-го Anthropic запретила использовать подписку Claude в сторонних харнессах, а в мае 2026-го Roo Code закрыли вовсе, репозиторий в архиве. Cursor держит семантический индекс включённым по умолчанию, но в своём облаке: код уезжает на их серверы, эмбеддинги хранятся у них. И это пока именно семантический индекс, не больше: ни истории, ни графа. Не потому, что это сложно — кит такого размера добавит граф и аналитику за пару кварталов, если решит, — а потому, что пока не решил. claude-context, самый популярный MCP в нише, только семантика (к тому же, на январь 2026 только облачная). JetBrains Context (EAP анонсирован 21 июля 2026 — к тому моменту TeaRAGs лежал на GitHub с 29 января, то есть почти шесть месяцев, а на npm — с 13 марта, больше четырёх) — мультирепозиторный индекс на GCP с best-case цифрами без разбивки. А Claude Code без индекса — это те самые десять минут grep-цикла. Из локальных MCP больше всего мне тогда приглянулся qdrant-mcp-server от mhalder: он использовал тот же стек эмбеддингов, что и Roo, — Ollama плюс Qdrant, — так что мой уже настроенный сетап подходил к нему без переделки. Он и стал основой форка, хотя на монолите, как мы помним, индексировался часами. Общее у всех: ни истории, ни графа; это поиск похожего, а не понимание кодовой базы.
Кодграф — тут наоборот: инструменты есть, но не для агента и не для Ruby. Aider строит repo map через PageRank по AST-графу — внутри собственного харнесса и без единого git-сигнала. Serena даёт symbol-level инструменты поверх LSP — а LSP для Ruby без аннотаций резолвит, как мы уже видели, посредственно. GitHub держит в проде stack-graphs — precise navigation на github.com — но агенту этот граф пока не отдаёт. И главное: ни Aider, ни Serena, ни stack-graphs не публикуют, какую долю вызовов разрешает их граф. Из графовых MCP, которые мне попадались, всерьёз качество графа меряет repowise — точность рёбер против компилятора на Go и TypeScript и ручную разметку на девяти языках, — но Ruby в этих замерах нет. А там, где я замерял сам, на малоизвестных графовых индексах и на тех, что с внушительным, но накрученным числом звёзд на GitHub, эта доля разочаровывает.
Топология — здесь для агента есть немногое. CodeScene с осени 2025-го отдаёт агенту через MCP хотспоты и владельцев — но пофайлово, по подписке и отдельным отчётом, а не сигналом в поиске; весной 2026-го появились открытые MCP с churn и bus factor, тоже пофайловые. TeaRAGs считает те же метрики по отдельным методам: пофайловая метрика размазывает один горячий метод по всему файлу. Sonar, madge, ESLint показывают, что есть, а не что важно, — инвентаризация, а не приоритизация. А знание «когда и какой инструмент звать» вместе с индексом поставляют пока скупо: JetBrains кладёт агенту инструкцию, когда звать семантический поиск, а когда хватит grep, — но учит выбирать только между ними двумя.
Теперь про хронологию. В январе 2026-го, когда я искал замену, standalone-инструменты для агента — те, что можно подключить к любому харнессу, — закрывали по одному представлению: семантику (qdrant-mcp-server, claude-context и им подобные — сыро и пусто: research-grade репозитории, часы на индексацию, Ruby номинально), граф или символы (CodeGraphContext, Serena), историю — пофайловыми хотспотами по подписке (CodeScene). Sourcegraph MCP давал поиск, навигацию и историю коммитов, но с сервера Sourcegraph. Локального слоя, который отдаёт чужому агенту все три представления с посчитанными метриками истории, мне не встречалось. Свою гипотезу я записал во внутренней статье тогда же, в январе, дословно: «TeaRAGs можно превратить в полноценный codebase intelligence layer, который будет помогать coding agents и разработчикам принимать решения, основанные на числах, а не гипотезах». Идею привязать git blame к семантическому индексу я носил ещё с Roo — всякие ChatGPT тогда уверяли меня, что так никто не делает и это неэффективно. TeaRAGs попал на GitHub 29 января и на npm 13 марта. В июле JetBrains назвали соседнюю, более узкую категорию — repository intelligence. Context, вышедший 21 июля, хранит эмбеддинги чанков и их координаты (пути, офсеты, ревизии) на GCP; ни графа вызовов, ни git-оси в описании продукта не заявлено, а их инструкция учит агента выбирать только между семантическим поиском и grep. Что не изменилось: ни у кого из крупных игроков — Sourcegraph, GitHub, JetBrains, Cursor, Augment — нет локально и за одним интерфейсом семантики, графа и посчитанных метрик истории. В опенсорсе ближайший аналог, repowise, появился в марте 2026-го — позже TeaRAGs. Проект серьёзный: историю он считает и по файлам, и по функциям, а точность графа меряет против компилятора на Go и TypeScript. Но семантический поиск у него идёт по сгенерированной вики, а не по коду, а история живёт в отдельных отчётах о рисках и здоровье кода — в выдачу поиска она попадает только поправкой на свежесть файла. Моя философия заключается в том, что агент должен работать с кодом напрямую, а не с прослойкой в виде очередной документации. Именно такой инструмент я и строил с января.
Trajectory-enrichment aware RAG system (TeaRAGs) — что получилось

Единственное жёсткое требование к названию было, что оно должно набираться одной левой рукой — я очень вдохновился asdf (попробуйте сами: tearags). Дословно tea rags переводится как «чайные тряпочки», и, при определенных навыках английского, легко читается как T-rex, отсюда и логотип: тираннозавр в монокле и шляпе, с чашкой чая. Динозавр — это старый разработчик, который не пользуется агентами; конкретно этот пересел на агентов и теперь пьёт больше чая. Ну и T-rex — самый опасный хищник своей эпохи (поздний мел, 68–66 млн лет назад).
Итак, я открыл Claude Code — и понеслась. Полгода с лишним спустя TeaRAGs — это MCP-сервер, который строит из кодовой базы те самые три представления и отдаёт их любому агенту, умеющему в протокол: Claude Code, Cursor, Codex — кому угодно. Двадцать три инструмента, девять языков с нативным AST-чанкингом, двадцать три пресета ранжирования, двадцать шесть скиллов в трёх плагинах — и всё это на ноутбуке: Qdrant встроен в процесс, граф живёт в DuckDB, эмбеддинги считает локальная Ollama или ONNX (облачный провайдер — одной строкой конфига, если код отдать не страшно и есть лишние 100–200 долларов на разовую индексацию :)). Дальше пройдусь по тем же семи проблемам, с одним числом на каждую. Но сначала — как оно устроено, в один экран.

Два пути: снизу вверх — индексация (AST-чанки и эмбеддинги, обход AST и резолв вызовов, git log и blame), сверху вниз — запрос агента и досье в ответ.
Путей тут два. Первый — индексация. Код парсится tree-sitter’ом и режется строго по AST: класс — это скелет плюс методы, метод — целиком; никаких «двадцати строк с середины if», на которых обычно и теряет качество семантический поиск. Для Ruby чанкер дополнительно понимает DSL: тело Rails-модели на 70–80% состоит из деклараций, поэтому has_many, validates, scope, колбэки и делегаты выделяются не как «остаток класса», а по группам — ассоциации, валидации, скоупы. Тесты режутся как тесты, а не как код: describe/context/it сохраняются целиком, а setup из родительских контекстов подмешивается в кейс, чтобы он читался без файла. Markdown режется по заголовкам и получает оглавление: агент открывает TOC за полсотни токенов и вытягивает одну нужную секцию, а не документ целиком. Дальше каждый чанк превращается в вектор и ложится в Qdrant вместе с полезной нагрузкой.
Параллельно в фоне идёт обогащение (enrichment): к чанкам добавляются метрики — по траекториям (trajectory), и это, пожалуй, главное слово в проекте. Траектория — одно измерение жизни кода. Git-траектория отвечает за историю: кто, когда, как часто и зачем менял. Кодграф-траектория — за структуру: кто кого вызывает, кто от кого зависит и насколько глубоко. Статическая — за размер и сложность. Git была первой, но идея оказалась шире: любой источник живых метрик о коде — это ещё одна траектория, ещё один провайдер, который подключается, не затрагивая остальные. В индексе у чанка хранится всё сразу, но в ответ попадает не всё подряд: какие метрики приедут вместе с результатом, определяет выбранный агентом пресет, и ничего лишнего в комплекте не оказывается. Агенту не нужны все метаданные на каждый запрос: спросил про техдолг — получил историю и радиус поражения, спросил по имени — получил определение и соседей по файлу. Второй запрос не нужен, а контекст не забивается лишним.
Второй путь — запрос. Агент спрашивает по MCP, поиск идёт одновременно по векторам и по BM25, выдача переранжируется по сигналам траекторий и уходит обратно. Обогащение поиску не мешает: пока фон дособирает метрики, искать уже можно. Первый прогон по большому проекту занимает время, дальше переиндексируются только изменённые файлы.
Вот как выглядит проиндексированный чанк — настоящий, из индекса самого TeaRAGs, сокращённый до главного:
{ "symbolId": "Reranker#computeAdaptiveBounds", // что и где "relativePath": "src/core/domains/explore/reranker.ts", "startLine": 358, "endLine": 392, "content": "private computeAdaptiveBounds(results) { … }", // тело метода целиком "navigation": { // соседи по файлу "prevSymbolId": "Reranker#buildExtractPayload", "nextSymbolId": "Reranker#extractAllDerived" }, "methodLines": 28, "moduleLines": 949, "moduleMethodCount": 43, // статика "git": { // траектория: история "file": { "ageDays": 3, "commitCount": 30, "bugFixRate": 27, "relativeChurn": 1.55, "blameDominantAuthorPct": 83, "blameContributorCount": 3 }, "chunk": { "ageDays": 57, "commitCount": 2, "bugFixRate": 50, "blameDominantAuthorPct": 100 } }, "codegraph": { // траектория: граф "file": { "fanIn": 2, "fanOut": 4, "instability": 0.67, "transitiveImpact": 16, "isHub": false }, "chunk": { "fanIn": 1, "fanOut": 0, "pageRank": 0.00015 } }, "rankingOverlay": { // почему он здесь (пресет techDebt) "file": { "commitCount": { "value": 30, "label": "high" }, "bugFixRate": { "value": 27, "label": "healthy" }, "blameDominantAuthorPct": { "value": 83, "label": "shared" } }, "chunk": { "bugFixRate": { "value": 50, "label": "healthy" }, "blameDominantAuthorPct": { "value": 100, "label": "deep-silo" } } } }
Агент не находит логику → семантический индекс и досье. Код разбит по AST на девяти языках, поиск гибридный — dense-вектор плюс BM25. На каждый результат агент получает досье, как в JSON выше: метод целиком (файл открывать не нужно), соседние символы до и после, git-историю файла и самого чанка, структурные метрики из графа и ranking overlay с ярлыками, посчитанными для этой базы. Для точных имён есть find_symbol — lookup без эмбеддингов; outline файла на четыре десятка методов занимает около 250 токенов, чтение того же файла целиком — около 8 600, а оглавление документа — 60 вместо 1 800.

Так агент видит документ и класс до того, как решит, что читать: оглавление на 60 токенов вместо 1 800 и outline на 250 вместо 8 600. Оба вызова — с индекса самого TeaRAGs.
Индексация часами → минуты. Секрета тут нет — просто конвейер, в котором никто никого не ждёт. Сканер отдаёт файлы, восемь процессов tree-sitter параллельно режут их на AST-чанки (именно процессов, а не потоков: в worker_threads NAPI-аддон node-tree-sitter возвращает разный AST для одного и того же файла — его нативные статики общие для всех потоков процесса), чанки собираются в батчи по 256 и уходят на GPU за эмбеддингами, Qdrant принимает результат пачками. Всё остальное — git log и blame, churn по чанкам, извлечение символов для графа — крутится в фоне и целиком прячется за эмбеддингами: по профайлеру GPU занимает 92% wall time, и это единственное место, где конвейер по-настоящему ждёт. В сумме для монолита выходит 103 минуты процессорного времени за 15 минут эмбеддингов; весь прогон целиком — вместе с построением графа — занимает 22 минуты wall time на внешней GPU. Инкремент после git pull — секунды; если я не индексировал неделю, обычно не больше пяти минут на моём рабочем репозитории. Полный реиндекс нужен редко, и даже он поиск не останавливает: новая коллекция собирается рядом, а потом подменяется атомарно. Размеры пулов и батчей не нужно подбирать руками: в пакет входит тюнер, tea-rags tune, который прогоняет бенчмарк и находит оптимальные настройки именно для вашей машины, — а при установке через отдельный установочный плагин он запустится обязательно.
Схема конвейера и модель параллелизма — убедитесь сами, что это должно быть быстро

Пропорции — из профиля полного реиндекса самого TeaRAGs; на монолите форма та же, только длиннее. Как только alias подменён, поиск работает, а сигналы codegraph доезжают коротким хвостом. Размеры пулов и батчей — из моей конфигурации, все настраиваются.
Ruby номинально → Ruby всерьёз. Про чанкинг с учётом DSL и тестов уже было. Основная работа — граф вызовов: пятнадцать стратегий разрешения, двадцать DSL-грамматик (Rails-макросы, delegate, scope, DSL популярных гемов), и гемовые из них — семь — включаются только если гем есть в Gemfile.lock, так что на проекте без гема ложных рёбер от них нет. Типы берутся из YARD-аннотаций, колонки — из db/schema.rb. Отдельно — strict-режим для случаев вроде handler.call, когда в проекте сотни def call: вместо того чтобы нарисовать сотни ложных рёбер или одно случайное, граф помечает вызов как неоднозначный и запоминает число кандидатов; агент видит такие места и запрашивает их отдельно, а не принимает догадку за факт. Первый зафиксированный замер recall графа — 0,706; со временем на тестовом корпусе — Mastodon (открытая федеративная соцсеть на Rails; в моём снимке около 3 200 Ruby-файлов и 180 тысяч строк вместе с тестами) — я дотянул его до статистического потолка в 0,932. К слову, рабочий монолит я дотянул до 0,88.
Достаточно ли этого, чтобы работать и анализировать зависимости? Для агентской навигации и переранжирования — более чем: это измеренная доля, а не прикидка. В самом Mastodon не разрешаются лишь отдельные специфические DSL. Если же говорить о продакшн-разработке, то в сервисных слоях, с которыми обычно и имеет дело рядовой разработчик, метамагии почти нет, и recall там стремится к 100%; а там, где ребро построить не удалось, агент это видит — и может добрать grep’ом.
Копирует похожее → знает историю. У каждого файла и чанка около тридцати сигналов из git и графа: возраст, churn относительно размера, доля багфиксов, владение по коммитам и по blame, fan-in, PageRank, транзитивный радиус. Все они нормированы по перцентилям этой базы (отдельно для тестов) и глушатся на малых выборках. Поверх — двадцать три пресета ранжирования; пресет — это готовый вопрос к базе, и агент задаёт его одним параметром:
// «где в коде про счета наш техдолг?» mcp__tea-rags__hybrid_search({ query: "bill payment processing", rerank: "techDebt", metaOnly: true }) // «что, скорее всего, виновато в этом баге?» — query здесь текст тикета mcp__tea-rags__hybrid_search({ query: "payment marked as paid twice after retry", rerank: "bugHunt" }) // «покажи проверенный боем образец ретрая» mcp__tea-rags__hybrid_search({ query: "retry with backoff", rerank: "proven" }) // «чей это код и где силосы?» — без запроса, по всему домену mcp__tea-rags__rank_chunks({ rerank: "ownership", pathPattern: "app/billing/**", level: "file" })
Нужного вопроса нет — свои веса задаются через rerank: { custom: { … } }. Любое ранжирование можно вскрыть: overlay показывает, какие сигналы сработали, с какими значениями и что эти значения значат в этой базе.
Как агент видит ярлыки: фрагмент prime-дайджеста для индекса самого TeaRAGs
## Signal thresholds — typescript - methodLines — source: small ≤11 / large ≤41 / decomposition_candidate ≤175.7 - moduleMethodCount — source: typical ≤3 / busy ≤15 / god-module ≤30 - git.file.commitCount — source: low ≤2 / typical ≤5 / high ≤16 / extreme >32 - git.file.ageDays — source: recent ≤3 / typical ≤9 / old ≤42 / legacy ≤127 - git.file.bugFixRate — source: healthy ≤33% / concerning ≤47% / critical ≤100% - git.file.blameContributorCount — source: solo ≤1 / pair ≤1 / team ≤2 / crowd ≤3 - git.chunk.commitCount — source: low ≤1 / typical ≤2 / high ≤3 / extreme >9 - codegraph.file.fanIn — source: isolated ≤1 / typical ≤2 / popular ≤3 / hub ≤7 - codegraph.file.transitiveImpact — source: local ≤16 / regional ≤25 / systemic ≤61 - codegraph.chunk.fanOut — source: leaf ≤1 / typical ≤2 / orchestrator ≤6 / god-method ≤18
Пороги — перцентили именно этой базы: «high» по коммитам здесь — от 6 до 16, на другом проекте границы будут другими. Для тестов у каждого сигнала своя колонка порогов, в фрагменте она опущена. Этот дайджест агент получает на старте сессии, поэтому ярлык в ответе для него — не слово, а число с контекстом.
Не видит структуру → граф с четырьмя инструментами. get_callers и get_callees отвечают, кто вызывает символ и что он вызывает, по рёбрам графа. find_cycles возвращает циклические зависимости — компоненты сильной связности предвычислены, ответ занимает миллисекунды. trace_path перечисляет все пути от A до B в порядке исполнения; с rerank: dangerous каждый шаг получает историю, а пути сортируются по совокупной опасности.

Живой вызов на индексе самого TeaRAGs: три маршрута с общим хвостом, у каждого шага — коммиты, доля фиксов и авторы из overlay; dangerRanking ставит computeCollectionStats первым на всех трёх, а среди развилок самая горячая — recomputeEnrichments.
Граф монолита на три с половиной миллиона строк строится меньше чем за три минуты; Тарьян и PageRank переписаны в стриминговом виде под лимит памяти в два гигабайта — первая версия съедала всю. Для TypeScript четырнадцать стратегий, четыре из них через ts.Program и typeChecker; доля разрешённых прямых вызовов на рабочем монолите — 0,99; через локальные переменные — 0,07, и это статистический потолок без runtime-типов, а не баг. Пример, ради которого граф стоит держать: файл на две сотни строк, от которого транзитивно зависят почти пять тысяч файлов, — агент видит это до правки, а не после.
Переизобретает → reuse gate. В скилле генерации (/tea-rags:data-driven-generation), прежде чем написать ретрай, валидацию или парсер, агент обязан поискать существующий компонент или метод. Нашёл рядом — переиспользует; нашёл в другом домене — повторяет подход, но не тянет зависимость через границу; почти подходит — расширяет минимальным диффом. Готовый код прогоняется через find_similar: если в другом домене нашёлся почти-дубль, агент возвращается к гейту. Два RetryOptions из начала статьи — ровно тот случай. Сканер дивергенций по всему проекту — в планах.
Не пользуется инструментами → плагин. Инструменты — половина продукта. Вторая — двадцать шесть скиллов в трёх плагинах и четыре слоя, через которые знание попадает к агенту: prime-дайджест на старте сессии (что проиндексировано и какие пороги у сигналов на этом проекте), схема инструмента (контракт вызова), MCP-ресурсы (каталоги пресетов и сигналов, сгенерированные из живого реестра, поэтому с кодом не расходятся) и search cascade — правило выбора: имя символа → find_symbol, смысл → гибридный поиск, буквальная строка → обычный grep. Последнее — честное ограничение индекса: и эмбеддинги, и BM25 работают с токенами чанков, а не с байтами файла, поэтому маркер TODO, номер задачи или значение константы быстрее и точнее найдёт grep — и плагин прямо отправляет агента к нему, а не заставляет искать точную строку по смыслу.
Скиллы — сценарии под задачу:
Задача | Скилл |
|---|---|
«как работает оплата счетов» |
|
«падает с такой ошибкой» |
|
«насколько здоров домен» |
|
«добавь метод» |
|
брейншторм |
|
план реализации |
|
TDD |
|
верификация перед коммитом |
|
Скиллы dinopowers — обёртки над superpowers: перед брейнштормом, планом, TDD или проверкой перед коммитом они сначала снимают сигналы с индекса и только потом запускают сам процесс.
Насколько хорошо агент этим пользуется, я меряю так:
Промпты. На каждую обёртку — 12–15 обычных реплик разработчика, например: «давай обсудим, как добавить OAuth refresh flow в
auth/— что может сломаться?». Часть из них — провокации: давление пользователя, подсунутый не тот инструмент, крайние случаи.Эталон. К каждому промпту приложено, какие вызовы агент обязан сделать и в каком порядке (здесь — три поиска с пресетами hotspots, ownership и techDebt, и только потом брейншторм) и какие делать нельзя.
Два агента. Один и тот же промпт получают два одинаковых агента с одинаковым набором инструментов; разница одна — у первого подключён плагин, у второго нет.
Зачёт. Кейс засчитан, если агент сделал все обязательные вызовы в нужном порядке и ни одного запрещённого.
Результат — доля кейсов, где агент воспользовался инструментами правильно. С плагином — 100% на всех 136 кейсах. Без скилла — от 13% до 53%, смотря какая обёртка: чаще всего агент пропускает обогащение или зовёт не тот инструмент, те самые привычки из седьмой проблемы. Средняя разница — 71 процентный пункт. Это мера того, что агент инструментами пользуется, а не мера качества его ответа; второе — отдельный замер, который я ещё не сделал.
А что это мне даёт как разработчику?
Как я упоминал выше, при первом знакомстве с Claude мне было больно каждый раз объяснять агенту, что, где и как искать в моём огромном монолите, и ждать по 15–20 минут, пока агент «нагрепает» результат. С появлением семантического поиска в Roo ситуация стала лучше, но я начал сталкиваться с тем, что код, который «подсовывает» мне Roo, часто оказывается похожим по смыслу, но не всегда правильным. Мой цикл работы с агентом сводился к тому, чтобы после каждой генерации находить ошибки и давать команды на их исправление, и тут меня осенило: чем меньше я буду исправлять, тем меньше эффективнее буду работать — улучшение feedback loop и есть точка оптимизации моего рабочего процесса. Я хотел бы больше думать над решением проблемы, чем над тем, как заставить агента решить её правильно.
Если разложить этот feedback loop, в нём два слагаемых. Первое — ожидание: те самые 15–20 минут, пока агент нагрепает, это время, когда я не думаю над задачей, а жду. Второе — то, что приходит после ожидания, приходится править, и эти правки — не решение проблемы, а накладные расходы: объяснить, где искать, показать, какой из похожих кусков правильный, откатить лишнее, попросить переделать. Задача при этом не двигается — двигается агент.
Сокращать я решил оба слагаемых сразу. Ожидание сокращает индекс: ответ за секунды вместо grep-цикла, причём данные агент собирает максимально эффективно — outline вместо файла, одна секция вместо документа, досье вместо десяти открытых файлов, — так что контекст не забивается лишним и агент меньше подвержен Context Rot. Накладные расходы сокращает то, что приходит вместе с ответом: не просто похожий код, а код с историей, структурой и оценкой, чтобы правильный выбор агент делал сам, до того как принесёт результат мне. Побочный эффект — меньше галлюцинаций и меньший расход input tokens. Дальше — по пунктам, что именно из накладных расходов ушло.
Объяснять, где искать. Раньше каждая задача начиналась с абзаца в чате: «оплата счёта — это не invoice, ищи bill; сервисы лежат в billing, но контроллер не трогай…». Теперь достаточно одной фразы — «где у нас проводится оплата счёта картой» — и агент сам находит сервис, его соседей и окружение, а заодно предупреждает, что этот файл — хаб, от которого транзитивно зависит половина биллинга. Раньше это выяснялось после мержа, из упавшего CI. И это не магия: плагин даёт агенту таблицу решений — на какой вопрос какой пресет звать, — так что «оплата счёта картой, что заденет» сам собой превращается в blastRadius. Да, я слышал, что и без всяких тулзов современный Claude умеет искать код — но мой делает это в разы быстрее и точнее.
mcp__tea-rags__hybrid_search({ query: "pay bill with saved card", rerank: "blastRadius", metaOnly: true })
Выбирать из похожего правильное. На «сделай как в соседнем сервисе» агент раньше приносил первый похожий кусок, а я открывал ещё три-пять таких же и сам решал, какой из них канонический. Теперь выбор делает пресет: proven поднимает код, который давно не чинили и который живёт в вызовах. Из четырёх похожих ретраев к платёжному шлюзу первым приходит тот, что год без фиксов и с пятью вызывающими, а про остальные три overlay честно говорит: одиночки, вызовов нет.
mcp__tea-rags__hybrid_search({ query: "retry payment gateway request with backoff", rerank: "proven", pathPattern: "app/services/billing/**" })
Искать причину бага по стектрейсу и грепу. Раньше я сам переводил тикет на язык кода: какие классы, какие методы, откуда начинать греп. Теперь текст тикета уходит агенту как есть — «после ретрая счёт помечается оплаченным дважды» — и первыми приходят не похожие по словам места, а те, где чинили чаще и правили последними: обработчик ретрая, проведение платежа, проверка идемпотентности. На монолите это те самые 40 секунд против десяти минут grep-цикла из первой проблемы.
mcp__tea-rags__hybrid_search({ query: "bill marked as paid twice after payment retry", rerank: "bugHunt", metaOnly: true })
Проверять руками, кто вызывает и что заденет. Раньше — grep по имени и глазами отделять определение, тесты и комментарии от настоящих вызовов, а потом прикидывать, по какой цепочке изменение доедет до списания денег. Теперь «кто вызывает проведение платежа» — рёбра графа с выражением вызова и уверенностью, а «как доедет» — все маршруты от API до списания, и первым показан самый опасный шаг: тот, где чинили чаще всего и где у файла один владелец. Туда агент смотрит в первую очередь, остальное — по остаточному принципу.
mcp__tea-rags__get_callers({ symbolId: "Billing::PayBill#call" }) mcp__tea-rags__trace_path({ from: "Api::BillsController#pay", to: "Billing::Charge#call", rerank: "dangerous" })
Список накладных расходов конечен, а список того, что стало можно, — нет. Дальше — про это.
А что инструмент ещё может?
Что может агент | Чем это делается |
|---|---|
Провести онбординг в фичу с точек входа |
|
Собрать карту техдолга по домену |
|
Найти участки, которые нельзя трогать без второго ревьюера |
|
Оценить риск изменения до того, как его делать |
|
Показать структуру: вызовы, радиус, циклы, хабы |
|
Сказать, чей это код и где бас-фактор равен одному |
|
Отобрать код, на который можно опираться |
|
Подготовить ревью: что в PR смотреть первым |
|
Провести аудит старого кода в security-путях |
|
Найти мёртвый и брошенный код | фильтры |
Генерировать код в стиле проекта без дублей |
|
Подобрать тесты под затрагиваемое поведение |
|
Прочитать из документа только нужную секцию |
|
Отвечать на другие вопросы по коду | фильтры |
«Другие вопросы» — это, например: что сделано по задаче #4521; какие файлы в домене менялись чаще всего за месяц; кто последний трогал метод и как часто его чинят; какие методы зовут отовсюду; покажи всё похожее на этот кусок.
Я успел показать и описать далеко не весь функционал — и специально обошёл стороной внутреннее асинхронно-гексагональное устройство проекта, это тема для отдельной статьи. Но три скилла особенно достойны внимания, и вот почему. Ими я разрабатывал сам TeaRAGs — compound-разработка в чистом виде: чем совершеннее становились инструменты анализа и сам способ взаимодействия с агентом, тем быстрее я добавлял новые фичи. В какой-то момент агент начал «видеть» файлы, которые сам же написал плохо: при работе с ними агентский цикл раздувался, после каждого круга исправлений появлялись новые баги или начинали падать соседние тесты. Тогда я понял, что сложность кода проблематична не только для человека — для самого агента тоже. Я стал регулярно запускать /tea-rags:risk-assessment, чтобы видеть проблемы проекта, просил агента предложить план рефакторинга и делал этот рефакторинг. С точки зрения опытного разработчика ничего нового: декомпозиция, консистентная онтология в неймингах и унификация.
Генерация с опорой на данные — /tea-rags:data-driven-generation. Скилл — это фиксированный порядок действий, который агент обязан пройти до того, как напишет код, и после. На примере запроса «добавь в оплату счёта частичную оплату»:
Определить режим. Агент проверяет по индексу, существует ли уже такой метод и есть ли сервис, куда его добавлять. Метода нет, сервис есть — значит, режим «расширить существующее». От режима зависит, какие из следующих шагов нужны.
Выбрать стратегию письма. Агент читает ярлыки сервиса оплаты. Код часто чинили — писать оборонительно, с проверками на входе. Код старый и тихий — минимальным диффом, ничего вокруг не трогая.
Найти эталон. Не первый похожий кусок, а проверенный: соседний сервис, который давно не ломался и который вызывают. Сначала в том же поддомене, потом в домене, потом по всему проекту.
Решить, где будет жить код. Если сервис уже раздут, агент не добавляет в него ещё, а предлагает отдельное место.
Проверить, нет ли готового. Прежде чем писать округление суммы или ретрай, агент ищет существующий хелпер. Нашёл рядом — переиспользует. Нашёл далеко — повторяет подход, но не тянет зависимость через границу модулей.
Подстроиться под стиль. Агент смотрит, как пишет владелец этого файла, чтобы новый метод не выглядел чужим.
Написать код. Только теперь — со стратегией, эталоном, стилем и списком того, что переиспользовано.
Проверить себя. Каждое имя, которое агент использовал, существует в проекте, а готовый метод не оказался почти копией того, что уже лежит в соседнем модуле. Оказался — назад к шагу 5.
Оценить последствия. Кого заденет новый код. Если менялся существующий метод, агент обязан показать всех, кто его вызывает.
Итог: агент не «пишет как умеет», а проходит тот же чек-лист, который прошёл бы внимательный сеньор, — только за секунды и каждый раз.

Оценка рисков — /tea-rags:risk-assessment. Это скилл-«врач»: симптома не нужно, он осматривает весь домен и составляет карту риска. На примере запроса «насколько здоров биллинг»:
Определить область. Из слова «биллинг» агент находит, какие каталоги это в коде, — по смыслу, а не по совпадению названий.
Просканировать пятью взглядами сразу. Одним параллельным блоком агент ранжирует код домена по пяти пресетам: где чинили чаще всего, где самый горячий churn, где старый и хрупкий код, где единственный владелец, где регрессия обойдётся дороже всего. Тесты и документация исключены всегда — они меняются чаще всех и испортили бы карту.
Отдельно посмотреть на структуру. Большой тихий метод в истории правок не виден, поэтому размер и связность агент считает отдельной осью: god-методы и god-модули.
Просканировать второй раз, другим срезом. Первый скан почти всегда отдаёт весь список тому участку домена, где правок больше всего, — он заслоняет остальных. Поэтому агент обязан пройти ещё раз, исключив этот участок: так на карту попадают и тихие соседи, у которых проблемы не хуже, просто их меньше трогали.
Свести результаты. Дубли между сканами убирает сам индекс, не агент руками.
Расширить от критичных находок. Для каждой находки высшего уровня агент ищет похожие места — у одной болезни обычно несколько очагов.
Дообогатить. Есть ли у находки тесты, какое место она занимает в графе вызовов, сколько стоит починка.
Выдать карту. Critical — код, который всплыл во всех пяти пресетах, High — в четырёх, Medium — счётчик; структурные риски и структурный долг — отдельными разделами. Вердикт ставится только по паре сигналов: один сигнал — не диагноз.
Бюджет — не больше 17 вызовов на домен и 22 на проект, поэтому карта воспроизводима. Именно с неё и начинались мои планы рефакторинга.

Охота за багом — /tea-rags:bug-hunt. Скилл-«детектив»: не осматривать всё, а сойтись к одной причине как можно меньшим числом ходов. На примере тикета «после ретрая счёт помечается оплаченным дважды»:
Искать по симптому, а не по коду. Текст тикета уходит в поиск как есть, а ранжирует выдачу пресет «где чинили»: первыми приходят места, где баги уже ловили, а не те, что похожи по словам. Это сразу отсекает половину кандидатов.
Остановиться, как только есть ответ. У агента чекпоинт из трёх полей: файл, строка, почему ломается. Заполнил все три — докладывает и стоит. Так он не уходит в «ещё один поиск для уверенности», который и раздувает контекст.
Если ответа нет — один ход, а не десять. Ровно одно действие на пустое поле: посмотреть, кто вызывает подозреваемого, или проследить маршрут от контроллера до него, — и снова чекпоинт. Маршрут показывает самый опасный шаг первым, поэтому обычно хватает одного хода.
Читать в порядке подозрительности. Пресет поднимает наверх не только код с историей фиксов: всплеск правок и волатильность в нём весят больше, чем доля фиксов, так что свежий код, который ещё никто не чинил, тоже оказывается наверху. Ярлыки задают только очередь чтения — сначала код с критичной долей фиксов, потом с тревожной и высоким churn, — но не отсекают кандидатов: если баг нашёлся в свежем методе, чекпоинт заполнен и отчёт выдаётся всё равно. И если у подозреваемого много вызывающих, агент сначала проверяет, не приходит ли баг сверху.
Отчёт — ранжированный список подозреваемых с одной фразой «почему» у каждого. На монолите это те самые 40 секунд.

А что по бенчмаркам?
Их нет.
Существующие бенчмарки меряют итог — решена ли задача на SWE-bench, совпала ли дописанная строка, — а не вклад отдельного слоя и тем более не каждой из его трёх осей. Но главное даже не метрика: полноту такого инструмента трудно оценить без живой топологии кода. История правок, доля фиксов, владельцы, хабы и хотспоты есть только у кода, который годами развивают разные люди, а бенчмарк берёт снимок репозитория и выдаёт одну итоговую цифру, из которой вклад истории и графа не выделить. Чтобы замер что-то значил, нужен корпус, где эта топология есть и размечена: длинная история, настоящие баг-фиксы, модули, от которых транзитивно зависят сотни файлов, и эталон — какое место правильное и чем оно опасно. Подобрать такой репозиторий и разметить эталон — долгая работа сама по себе.
Хорошо это видно на примере свежей работы о чанкинге для RAG-автодополнения: 864 конфигурации, метрика — точное совпадение дописанной строки. Её вывод: резать код по функциям хуже, чем скользящим окном или AST-чанками с бюджетом размера, — на 3,6–5,6 процентных пункта. Для их задачи это правда, и причины названы честно: функция короче минимального чанка и не добирает бюджет промпта, а импорты и поля класса остаются за бортом. Но метрика видит только текст, попавший в промпт, и не видит того, что к чанку прилагается. Задача агента — не дописать строку, а найти правильное место и понять, чем оно опасно, и чанк для него не конечный контекст, а адрес: по целому методу приходит досье — импорты, соседи, история и граф. Бенчмарк без истории и топологии такой выигрыш не засчитает по построению: он честно отвечает на свой вопрос, но не на наш.
Собственный бенчмарк под это — месяцы на подбор корпуса, разметку эталона, валидацию и признание. Продавать я никому ничего не собираюсь, поэтому предпочитаю вкладывать время в функциональность проекта и устранение собственных болей. Найдётся соавтор с корпусом и интересом к замерам — сделаем вместе.
Золотая пуля ИИ-разработки найдена?
Ответ — нет. TeaRAGs — это инструмент, который требует в первую очередь систематического подхода, определённой дисциплины и знания теории о том, что находится под капотом и как это поддерживать. Как бы я ни хотел сделать «ультра-тулзу сразу для всего», направление у инструмента получается довольно конкретное: рассказывать ИИ-агенту о коде, давать инструменты для аналитики кода и навигации. За использование индексов приходится платить, и не только временем на индексацию (хорошо, что можно пойти заварить чаю в это время!). Индекс — это ещё одно состояние, за которым нужно следить: файлы, которые агент только что поменял, он увидит в поиске не сразу; новая версия с новыми полями требует пересчёта обогащений, смена модели эмбеддингов — полного реиндекса; переключил ветку — индекс отстал; git- и graph-сигналы доезжают после эмбеддингов, и в этом окне ответ неполный. Всё это автоматизировано — авто-индексация, дайджест на старте сессии, правила свежести для агента, — но автоматика лишь напоминает; понимать, что под капотом, всё равно приходится. А конкретно мне — ещё и разменивать своё время на то, чтобы узнать, что по кодовым метрикам больше двадцати лет существует отдельное научное сообщество…
Вот ещё момент. За время работы с TeaRAGs (а тем более его разработки) мой майндсет разработчика совершил качественный скачок. Если раньше я смотрел на код сквозь призму интуиции, то теперь чётко понимаю, что у каждого класса или метода есть своя собственная топология, своя история и определённые маркеры, которые делают код если не «ужасным», то достаточно рискованным — или превращают целую подсистему в бомбу замедленного действия. Я научился понимать структуру связей методов и классов, однозначно определять, что является хабом, а что можно трогать, не боясь сломать фичу соседнего отдела. Казалось бы, для опытного разработчика это звучит как что-то само собой разумеющееся, но если раньше мне приходилось говорить коллегам: «В прошлый раз тронули этот модуль — и пол-авторизации о$#!нуло», то теперь я могу сказать проще: «Transitive impact этого хелпера — 5000, ты точно хочешь его трогать?»
Так или иначе, есть вещи, которые TeaRAGs не может сейчас, и вещи, которые, вероятно, не сможет никогда.
Сейчас TeaRAGs ограничен следующим:
Большое количество зависимостей: Ollama, Qdrant, DuckDB — и это только начало списка. Я постарался решить этот вопрос и подвёз в проект встраиваемый Qdrant и ONNX как встраиваемый эмбеддер, но столкнулся с ограничением технологии.
Сложность установки и настройки, хотя я постарался компенсировать её отдельным плагином по установке.
Федеративный или multi-repo поиск: сейчас поддерживается индексация только git-монорепозиториев. Фича есть в планах, но лично мне она не была нужна, поэтому и не делал.
Полная глубина графа вызовов — только для трёх языков, TypeScript и Ruby, плюс недавно появился его брат~~–близнец~~ двойняшка Python. Да, здесь та же самая история: не было нужно — не делал. Зато выстроил такую архитектуру, чтобы поддержка нового языка добавлялась максимально легко.
Решение полностью локальное: будет затруднительно развернуть его и использовать «сразу для всех в компании» без доработок, которые мне самому не нужны — а значит, появятся они только под чей-то конкретный запрос.
Инструмент помогает сосредотачиваться на таких вещах, как user flow и архитектура проекта, но давать советы по архитектуре и декомпозиции не может — настраивайте свои
.claude/rules.MCP-сервер работает с любым агентом, а плагин со скиллами и search cascade — только с Claude Code: под другие харнесы его нужно адаптировать.
Список недостатков и нереализованных фич можно продолжать бесконечно, но, пожалуй, остановлюсь и добавлю, что проектирую инструмент так, чтобы им было удобно пользоваться не только агенту, но и человеку.
Я потратил на разработку проекта и ресёрч более полугода своего личного времени, хотя у меня не было никаких заказчиков, кроме собственного интереса, — и получил взамен месяцы бессонных ночей, одного рандомного контрибьютора, который принёс мне решение бага, да несколько позитивных отзывов от коллег, которые установили тулзу в момент её зарождения. Поэтому я буду бесконечно признателен за любую форму фидбека — и ещё больше, если кто-то всё же попробует проиндексировать свои собственные проекты (и да, их код не будет тайно отравлен на мои секретные сервера).
Ссылка: github.com/artk0de/TeaRAGs-MCP. Не стесняйтесь контрибьютить, проект открыт для всех; на всё, что тулза сейчас не умеет, можно завести PR
А вот чего она точно никогда не будет уметь: думать за разработчика и принимать взвешенные решения, хотя и всегда будет помогать «взвешивать».
Спасибо за внимание — и особенное спасибо тем, кто прочёл статью полностью и до конца!
P. S. Пишите в комментах, понравилась ли статья и чем её дополнить. А особенно — если нашли пробелы или несоответствия. Если хочется разобрать какой-то аспект подробнее — подготовлю материал, а его у меня, как вы поняли, очень много.

