- Дата публикации
Как одна библиотека почти заменила RAG и векторный поиск по коду
Что появилось / что изменилось
Команда ecom.tech долго искала способ автоматически вытаскивать из кода REST‑эндпоинты и Kafka‑топики, чтобы потом кормить их LLM и собирать документацию. Они прошли через Tree‑sitter, классический и гибридный RAG, grep с монструозными регэкспами — и в итоге упростили всё до одной библиотеки Grep‑AST.
Ключевые изменения по сравнению с их прошлым стеком:
- Вместо тяжёлого RAG‑пайплайна с эмбеддером gte‑multilingual‑base и BM‑25 теперь используется лёгкий поиск по абстрактному синтаксическому дереву (AST).
- Средняя точность по всем языкам в RAG‑подходе была около 85%. Grep‑AST команда внедрила именно потому, что RAG всё равно пропускал нужные файлы и работал медленно. В новом варианте они добились более надёжного покрытия без постоянного тюнинга.
- Сильно сократились накладные расходы: больше не нужно считать эмбеддинги для сотен репозиториев, поднимать отдельный векторный стор и держать гибридный поиск плюс ре‑ранжирование.
- Поддержка правил стала проще. Вместо горы хрупких регулярных выражений команда описывает несколько паттернов на уровне AST для каждого языка и фреймворка.
Важно: конкретных чисел по ускорению и стоимости авторы не приводят. Можно честно сказать только одно: Grep‑AST уменьшил сложность системы и убрал тяжёлый векторный поиск из контура.
Как это работает
Задача ecom.tech — сделать reverse engineering документации: восстановить описание REST API и Kafka по живому коду. Репозитории пёстрые: Golang, Kotlin, Ruby, Elixir, PHP, плюс разные фреймворки — от Gin и Echo до Spring, Rails, Phoenix и Laravel.
При этом архитектурные стандарты внутри компании жёсткие. Точки входа в API и Kafka всегда выглядят предсказуемо:
-
REST:
- Go: хендлеры, привязанные через
http.HandleFunc,GET/POSTв Gin/Echo. - Kotlin: контроллеры Spring с
@RestController,@RequestMapping, либо маршруты Ktorrouting { get("/") { ... } }. - Ruby: контроллеры Rails и
routes.rbсresourcesилиget. - Elixir:
Phoenix.Router, макросыget "/", PageController, :index. - PHP: маршруты в
routes/api.phpLaravel или аннотации в контроллерах Symfony.
- Go: хендлеры, привязанные через
-
Kafka:
- Go:
producer.SendMessageиз Sarama,consumer.Subscribe. - Kotlin/Spring:
KafkaTemplate.send,@KafkaListener. - Ruby:
producer.produceв ruby‑kafka. - Elixir:
KafkaEx.produce,GenConsumer. - PHP:
KafkaProducer::sendповерхphp‑rdkafka, консьюмер‑группы.
- Go:
На бумаге это похоже на задачу «найти по строке». На практике всё ломают обёртки, базовые контроллеры, DSL и метапрограммирование, особенно в Ruby и Elixir. Для парсера вроде Tree‑sitter вызовы фреймворка выглядят как обычные функции, и он либо пропускает реальные эндпоинты, либо ловит шум.
Grep‑AST решает это иначе:
- Строит AST кода для нужного языка.
- Даёт возможность описывать поисковые паттерны не как строки, а как шаблоны по дереву: «функция с такой аннотацией», «вызов метода с таким именем и сигнатурой», «маршрут внутри такого блока».
- Применяет эти паттерны ко всем файлам репозитория и возвращает только те участки, которые действительно являются точками входа, а не комментариями или строковыми литералами.
Команда пробовала делать что‑то похожее руками на Tree‑sitter, но споткнулась о поддержку: для каждого языка и фреймворка приходилось писать собственные правила, зависящие от стиля кода и версии библиотек. Grep‑AST дал им более удобный слой поверх AST и уменьшил количество кода, который нужно обслуживать.
Что это значит для вас
Если у вас десятки или сотни микросервисов на разных языках и фреймворках, и вы хотите искать в коде не по словам, а по смыслу конструкции — Grep‑AST выглядит как здравый вариант.
Где библиотека особенно полезна:
- Автоматическая документация. Как у ecom.tech: находите все REST‑эндпоинты и Kafka‑топики, выдёргиваете нужные куски кода и дальше уже подключаете LLM.
- Codebase‑поиск для внутренних инструментов. Например, нужно быстро найти все места, где сервис открывает HTTP‑порт или пишет в конкретный Kafka‑топик.
- Аудит архитектурных правил. Можно проверить, что все новые контроллеры используют нужную базовую абстракцию или что продюсеры Kafka создаются только через один пакет.
Где Grep‑AST вам почти точно не поможет:
- Если у вас маленький монолит на одном языке, обычный
grepили поиск IDE будет дешевле по времени внедрения. - Если в коде нет устойчивых архитектурных паттернов, а каждый сервис пишет HTTP и Kafka по‑своему, придётся тратить время на настройку паттернов AST.
- Если вы уже живёте в экосистеме полноценного code search (Sourcegraph и аналоги) и вас устраивает качество, переход на Grep‑AST не даст радикальной выгоды.
Про доступность: авторы нашли библиотеку на GitHub, она открыта. Для использования VPN не нужен, ограничений по России нет.
Место на рынке
Если сравнивать Grep‑AST с тем, что чаще всего используют для поиска по коду, картина такая:
-
Против RAG + эмбеддинги (gte‑multilingual‑base) + BM‑25:
- Плюсы Grep‑AST: проще инфраструктура, нет векторного стора, предсказуемое поведение, не нужно постоянно дообучать и перенастраивать пайплайн. В кейсе ecom.tech RAG давал 85% точности и всё равно пропускал нужные файлы.
- Минусы: AST‑подход жёстко завязан на конкретные языки и фреймворки. Если вы ищете «по смыслу» в произвольном тексте или документации, векторный поиск всё ещё лучше.
-
Против Tree‑sitter напрямую:
- Плюсы Grep‑AST: менее больная поддержка паттернов, особенно когда поверх фреймворков наращены DSL и обёртки. Tree‑sitter в их кейсе либо терял реальные эндпоинты, либо давал много ложных срабатываний.
- Минусы: вы всё равно живёте в мире AST и должны понимать, как устроено дерево для каждого языка.
-
Против обычного
grepи регэкспов:- Плюсы Grep‑AST: не нужно «выкручивать» регулярки, чтобы отфильтровать комментарии, строки, импорты и легаси‑хаос. Форматирование строк, перенос аннотаций, дополнительные декораторы больше не ломают поиск.
- Минусы: входной порог выше, чем у
grep. Для маленьких проектов это может быть оверкилл.
Если вы строите свои инструменты вокруг LLM и хотите, чтобы модель видела не случайные файлы, а реальные точки входа в системы, Grep‑AST — честный способ выкинуть часть сложной RAG‑машины и оставить только то, что действительно опирается на структуру кода.