Умная кодогенерация: как AI-платформа помогает строить микросервисную архитектуру

b1512cebbe53af6e4babed6e9442e284.png

Привет! Я лид backend-разработки red_mad_robot, Илья Трусов, и я создал инструмент Meroving, который помогает оптимизировать рутинные задачи, а иногда полностью их делает за разработчика. Например, монотонное написание однотипного кода или бесконечное перекладывание JSON-файлов. О том, как создавался и зачем нужен Meroving, рассказываю в статье.

Какие проблемы хотелось решить с помощью Meroving

  1. Переиспользование кода
    С накоплением опыта и проектов появляется база кода, которая часто копируется из одного проекта в другой с минимальными изменениями. Это сложно поддерживать, но мы всё же пытаемся переиспользовать старые наработки.

  2. Создание единой архитектуры
    Хотелось разработать универсальный подход с едиными стандартами: общепринятыми архитектурными решениями, единой структурой проекта, линтерами, настройками и лучшими практиками разработки, и при этом не писать код.

  3. Оптимизация затрат и времени на разработку
    В современных реалиях сроки разработки сокращаются. То, на что раньше уходил год, теперь нужно сделать за несколько месяцев. Это требует автоматизации и оптимизации процесса.

Meroving — это инструмент, который позволяет из одного конфигурационного файла автоматически собирать микросервисную архитектуру. Название проекта отсылает к фильму «Матрица»: как и персонаж Меровинген, наш инструмент умеет управлять другими.

Meroving распространяется разными способами:

  • Через обычный бинарник с командой go install.

  • Через Docker-образ, который позволяет поднять контейнер и работать через него.

В конфигурационном файле указываем нужные микросервисы и их зависимости, например, разные базы данных, мониторинг, брокер сообщений, подключение к другим микросервисам по gRPC, вводим команду meroving build — и получаем готовую микросервисную архитектуру.

Архитектура микросервисов

Meroving строит архитектуру микросервисов на основе нескольких ключевых подходов:

  1. Разделение на слои
    Каждый слой абстрагирован и может быть переиспользован в других сервисах.

  2. Service locator pattern
    Мы используем паттерн локатора, который автоматически прокидывает зависимости между слоями. Это особенно важно при кодогенерации, где сложно сделать это вручную.

  3. Синхронное и асинхронное взаимодействие
    Мы используем gRPC, Protocol Buffers и разные шины данных для обмена сообщениями между микросервисами.

Одна из основных сложностей микросервисов — управление распределёнными транзакциями. Многие проекты используют двухфазные или трёхфазные коммиты, но мы выбрали подход Saga с хореографией.

В чём его суть? Каждый микросервис отвечает только за свои действия и не зависит от центрального оркестратора. Это повышает устойчивость системы, так как нет единой точки отказа.

Кроме того, мы используем CDC (Change Data Capture), например, с помощью инструмента Debezium для PostgreSQL и Change Streams для MongoDB. CDC позволяет отслеживать изменения в базе данных и автоматически передавать их в шину данных, устраняя необходимость ручного вызова событий из микросервисов. Конфигурационные файлы коннекторов, воркера, топики и обработчики данных генерируются автоматически.

Что умеет Meroving

Gateways

  1. Генерирует структуру, конфигурацию и запуск.

  2. Конфигурирует timeout, cache, rate limiting, cors, security как глобальные настройки, так и отдельно для каждого метода.

  3. Подключает провайдеры: Sentry, Jaeger, Prometheus, Grafana и т. д.

  4. Подключает gRPC-клиенты микросервисов.

  5. Генерирует сервер из Swagger.

  6. Создаёт обработчики из Swagger с возможностью агрегации данных из множества сервисов, возврата моковых данных и т. д.

  7. Создаёт структуру мапперов данных из gRPC-клиентов в Swagger-структуры.

Микросервисы

  1. Генерирует структуру, конфигурацию и запуск.

  2. Генерирует gRPC-сервер из protobuf.

  3. Подключает провайдеры: PostgreSQL, MongoDB, Redis, Kafka, Sentry, Jaeger, Prometheus, Grafana и т. д.

  4. Собирает обвязку для запуска тестов и генерирует сами тесты.

  5. Генерирует коннекторы, воркеры, топики, обработчики для CDC Debezium и MongoDB Change Streams.

  6. Генерирует Saga для отката транзакций (создаёт топики, обработчики в юзкейсах и конфигурирует шину данных).

  7. Генерирует из схем полноценный ORM с использованием entgo.

И ещё много чего

  1. Поднимает инфраструктуру в Yandex Cloud на основе используемых зависимостей в микросервисах.

  2. Собирает локальный деплой со всеми инфраструктуктурными зависимостями.

  3. Генерирует GitLab CI/CD.

  4. Генерирует Helm Charts и Ingress для деплоя в Kubernetes.

  5. Интегрируется с Vault для хранения секретов.

  6. Генерирует линтеры.

  7. Создаёт changelog на основе Swagger для вашего гейтвея с учётом изменений.

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

Одна команда — и вы запускаете:

  • Локальное окружение с 2–10 микросервисами для отладки.

  • Линтеры и автоформатирование кода.

  • Тесты, которые автоматически создаются и не требуют настройки инфраструктуры для запуска.

Да, для запуска 100 микросервисов потребуется мощное оборудование, но на этапе отладки этого и не требуется. А вот запуск нескольких ключевых сервисов одной командой значительно упрощает работу.

Все файлы с .gen.go полностью сгенерированы Meroving

Все файлы с .gen.go полностью сгенерированы Meroving

Что остаётся разработчикам

  1. Написать эндпоинт в шлюзе и микросервисе.

  2. Реализовать мапперы данных между gRPC и Swagger.

  3. Сосредоточиться на бизнес-логике микросервиса.

  4. Выпить кофе. Обо всём остальном позаботится Meroving.

Автоматизируем написание кода

Итак, мы решили проблему с автоматизацией инфраструктуры и микросервисной архитектуры, но остаётся вопрос: как автоматизировать написание бизнес-кода?

Попробуем пойти по классическому пути:

  • GenAI (GPT или другая LLM).

  • RAG (Retrieval-Augmented Generation), где мы индексируем весь код, векторизуем его, а затем используем для генерации нужного результата.

Но это не работает. Вот почему:

  1. Код ≠ Текст
    В отличие от текста, код не имеет смысла на уровне отдельных строк, он требует контекста. Одна функция может зависеть от десятков других функций, библиотек и конфигураций. Без этого контекста AI не понимает сути происходящего.

  2. Разные стили и подходы
    Одна и та же задача может быть решена множеством способов с использованием разных архитектур, библиотек и подходов.

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

  3. Многообразие файлов
    В проекте есть не только код (Go, Python и т. д.), но и конфигурационные файлы (YAML, JSON), HTML-шаблоны и многое другое.

    Для человека эти элементы взаимосвязаны, но для AI это разные типы данных, которые сложно анализировать в комплексе.

Одна из ключевых возможностей платформы — кодогенерация. Мы используем два подхода для этого процесса:

  1. Шаблоны — заранее подготовленные структуры файлов, которые могут быть адаптированы под разные проекты. Этот подход позволяет ускорить процесс разработки, но требует определённой настройки и поддержки.

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

Чтобы AI корректно генерировал код, нужно предоставить ему полный контекст проекта:

  • Зависимости кода: построить дерево зависимостей функций, классов и библиотек, включая open-source и внутренние библиотеки.

  • Объяснение кода: пользовательский промпт может содержать как код, так и смысловой запрос. Наша AI-платформа должна уметь искать и по совпадениям в кодовой базе, и по смысловым совпадениям.

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

Собственная AI-платформа

Чтобы исключить все эти проблемы, мы решили разработать контекстный движок. Вот что он должен уметь:

eebe131bb5c3f977c79a53d481fc7b71.png

В результате у нас получилась собственная AI-платформа, которая локально запускает API к LLM, сервер для эмбеддингов с API и векторную базу данных. А ещё запускает Meroving — он уже интегрирован по API с AI-платформой.

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

После запуска всей обвязки запускается Watcher — компонент, который следит за изменениями в коде:

  • Автоматически переиндексирует файлы при изменении.

  • Обновляет векторную базу данных и дерево зависимостей проекта в реальном времени.

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

Возникает логичный вопрос: почему мы не передаём сразу весь код? Он плохо векторизуется. Для стандартного RAG качество 40% — это плохо, но для работы с кодом это буквально подарок. Поэтому мы оптимизируем процесс, отправляя только минимальный, но точный контекст со всеми зависимостями для понимания промпта и выполнения задачи.

Важно отметить суть двухэтапного подхода. После построения дерева зависимостей кода мы векторизуем сам код, передаем его в LLM, которая объясняет, что он делает за счёт полного контекста. Далее мы векторизуем сам код и его объяснение, связываем два объекта и сохраняем в векторные базы.

После получения ответа от LLM он структурируется (например, в формате JSON), что позволяет улучшить качество и точность генерации. Также на помощь приходят агенты, которые могут обрабатывать как входные данные, так и выходные из LLM.

Преимущества Meroving для разработчиков

Можно сфокусироваться на бизнес-коде

AI-платформа и Meroving берут на себя всю рутинную работу: сборку микросервисов, настройку инфраструктуры, построение зависимостей. Разработчику остаётся писать только бизнес-логику, что повышает продуктивность и снижает вероятность ошибок.

Снижаются требования к квалификации специалистов

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

Преимущества Meroving для бизнеса

Быстрый старт проектов

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

Снижение затрат

Меньше разработчиков — меньше расходов даже с учетом затрат на инфраструктуру. За счёт автоматизации часть работы может быть делегирована младшим специалистам или полностью передана AI.

Оптимизация ресурсов

Модульная структура и гибкая конфигурация Meroving позволяют эффективно распределять задачи: локальная инфраструктура решает несложные задачи, а удалённые ресурсы повышают качество работы AI-платформы. Такой подход снижает затраты на ресурсы для AI платформы.

Ценные советы

Наш подход — это не серебряная пуля для всех и каждого. Meroving, о котором мы говорим, — это наш внутренний инструмент, который продолжает активно развиваться и пока не готов к публичному использованию. Мы делимся опытом создания фреймворков, ориентируясь на современные тенденции в AI и LLM, чтобы показать, что подобные задачи по силам не только крупным корпорациям, но и небольшим командам.

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

  1. Не бойтесь разрабатывать свои инструменты

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

  1. Не конкурируйте с гигантами

Не стоит пытаться разрабатывать низкоуровневые библиотеки или конкурировать с крупными корпорациями и open-source сообществом. Фокусируйтесь на создании внутренних инструментов, которые оптимизируют вашу работу.

  1. RAG-платформы для кода — будущее разработки

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

Над материалом работали:

текст — Илья Трусов;

редактура — Алина Ладыгина;

иллюстрации — Юля Ефимова.

Хотите больше узнать о нашей разработке — подписывайтесь на Telegram-канал red_mad_dev

© Habrahabr.ru