Внедрение AI в Rails Монолит: Секреты, Как не Сломать 7-летний Код

Внедрение AI в Rails Монолит: Секреты, Как не Сломать 7-летний Код

Почему ваш «динозавр» Rails требует срочной модернизации AI

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

Однако реальность такова, что именно старые, насыщенные данными проекты получают наибольшую выгоду от интеграции AI. У вас уже есть контекст, история пользователей и сложная бизнес-логика. Задача состоит не в том, чтобы переписать проект, а в том, чтобы научить AI agent работать внутри существующих ограничений. ↓

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

В сообществе Ruby on Rails долгое время бытовало мнение, что для AI-задач необходимо поднимать отдельный микросервис на Python. Это создавало барьер: нужно дублировать логику авторизации, настраивать обмен данными и следить за синхронизацией. Но опыт показывает, что внедрение AI прямо в монолит — это не только возможно, но и архитектурно правильнее для систем со сложным разграничением прав доступа.

  • Контекст данных: Ваш монолит уже знает всё о пользователе.
  • Безопасность: Логика Pundit или CanCanCan уже написана и оттестирована.
  • Инфраструктура: Не нужно плодить новые сервисы и усложнять деплой.

Где разместить AI, чтобы не обрушить унаследованную систему?

Главный страх при работе с legacy code — сломать то, что работает годами. Когда речь заходит о мульти-тенантных приложениях (multi-tenant), где данные разных клиентов строго изолированы, риск утечки информации через «галлюцинации» нейросети кажется критическим.

Представьте ситуацию: вы разрабатываете SaaS для социальных работников (как в кейсе Mon Ami). У вас есть чувствительные данные пациентов, многоуровневая авторизация и сложный поиск. Если вы создадите внешний AI-сервис, вам придется заново учить его, кому и что можно показывать. Это огромная дыра в безопасности. ↓

Решение лежит в использовании паттерна RAG (Retrieval-Augmented Generation) и Function Calling внутри самого Rails-приложения.

Вместо того чтобы давать AI прямой доступ к базе данных, вы предоставляете ему набор инструментов (Tools). Это методы Ruby, обернутые в интерфейс, понятный языковой модели. AI не выполняет SQL-запросы. Он «просит» ваше приложение выполнить поиск или действие.

Вот как это выглядит концептуально:

  1. Пользователь задает вопрос.
  2. AI анализирует вопрос и решает, какой инструмент нужен (например, findpatientrecords).
  3. Rails-приложение перехватывает этот запрос.
  4. Выполняется стандартный Ruby-код с проверкой политик доступа (Pundit).
  5. Результат (безопасный и отфильтрованный) возвращается в AI для генерации ответа.

Таким образом, Ruby on Rails выступает в роли строгого контролера, а AI — лишь в роли интерфейса.

Курс AI для разработчиков. Увеличиваем производительность разработчиков за счет внедрения AI-инструментов.

Архитектурная дилемма: Sidekiq, микросервис или инкапсуляция?

Архитектурная дилемма: Sidekiq, микросервис или инкапсуляция AI?
Архитектурная дилемма: Sidekiq, микросервис или инкапсуляция AI?

При проектировании AI-фич в монолите возникает вопрос производительности. Запросы к LLM (Large Language Models) могут занимать от нескольких секунд до минуты. Блокировать основной поток выполнения (puma worker) на такое время недопустимо.

Традиционный подход Rails-разработчика — вынести всё в фоновые задачи через Sidekiq. Это отлично работает для генерации отчетов или анализа текста в фоне. Но что делать, если вам нужен интерактивный чат-бот? ↓

Здесь мы сталкиваемся с необходимостью выбора правильного инструментария.

Если вы создаете AI agent, который должен отвечать в реальном времени, использование чистого Sidekiq может добавить лишнюю задержку. В то же время, создание отдельного микросервиса на Python (FastAPI + LangChain) возвращает нас к проблеме дублирования бизнес-логики.

Оптимальный путь для Rails-монолита — инкапсуляция логики в Service Objects с использованием гемов, поддерживающих стриминг (streaming) ответов, или использование асинхронных вызовов внутри Ruby.

Вам не нужно переписывать систему на микросервисы. Достаточно выделить слой абстракции для работы с LLM. Это позволяет:

  • Использовать существующие модели ActiveRecord.
  • Применять скоупы (scopes) для фильтрации данных.
  • Логировать действия агента через стандартный Rails logger.

Пример архитектурного решения: использование выделенного AgentController, который стримит ответ клиенту (через Server Sent Events или ActionCable), обрабатывая запросы к LLM в реальном времени, но обращаясь к базе данных через строгие методы моделей.

Стек для AI: Выбираем между Python и Ruby, интегрируем данные

Многие разработчики считают, что AI — это исключительно территория Python. Действительно, экосистема Python богата библиотеками. Однако, для интеграции в Rails-приложение использование Python часто избыточно.

Современные Ruby-гемы позволяют работать с LLM так же эффективно, сохраняя при этом чистоту архитектуры. Одним из таких инструментов является rubyllm. Он предоставляет удобную абстракцию над API провайдеров (OpenAI, Anthropic) и позволяет описывать инструменты (Tools) на нативном Ruby. ↓

Рассмотрим, как настроить окружение для работы с AI в существующем проекте.

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

# config/initializers/rubyllm.rb

RubyLLM.configure do |config|
# Безопасное извлечение ключей из Rails credentials
config.openaiapikey = Rails.application.credentials.dig(:openaiapikey)
config.anthropicapikey = Rails.application.credentials.dig(:anthropicapikey)

# Использование нового API для ассоциаций (рекомендуется для агентов)
config.usenewactsas = true

# Настройка таймаутов для долгих ответов от LLM
config.timeout = 120 # секунд
end

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

Внутри Rails вы просто вызываете методы:

# Пример использования внутри сервисного объекта
response = RubyLLM::Client.chat(
  messages: [{ role: "user", content: "Найди клиента по имени Иван" }],
  tools: [SearchTool.definition], # Определение вашего инструмента
  model: "gpt-4-turbo"
)

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

Изоляция и асинхронность: Как обеспечить безопасность и скорость

Изоляция и асинхронность: Как обеспечить безопасность и скорость AI.
Изоляция и асинхронность: Как обеспечить безопасность и скорость AI.

Безопасность — это главный аргумент противников внедрения AI в корпоративные системы. "Что если AI покажет данные одного клиента другому?" — это валидный страх.

В Rails-монолите решение этой проблемы уже существует. Это ваши политики доступа (например, Pundit) и механизмы поиска (например, Algolia или Elasticsearch). Секрет безопасного внедрения AI заключается в том, чтобы заставить агента использовать эти механизмы, а не обходить их. ↓

Рассмотрим схему безопасного взаимодействия.

Когда AI решает, что ему нужно получить данные, он не делает User.all. Он вызывает функцию, которую вы ему предоставили. И именно внутри этой функции происходит магия безопасности.

Предположим, у вас есть сложный поиск через Algolia, который уже настроен на фильтрацию по tenantid.

class PatientSearchTool
  # Метод, который будет вызывать AI
  def self.call(query, context)
    user = context[:currentuser]
    
    # 1. Проверка прав доступа через Pundit
    return "Access Denied" unless Pundit.policy(user, Patient).index?

    # 2. Поиск с учетом тенанта пользователя (Scoping)
    results = Patient.search(
      query,
      filters: "tenantid:#{user.tenantid}"
    )

    # 3. Форматирование результатов для LLM
    results.map { |r| "ID: #{r.id}, Name: #{r.name}" }.join("\n")
  end
end

В этой схеме:

  • Изоляция: AI никогда не получает прямого доступа к базе.
  • Контекст: Вы передаете currentuser в инструмент, гарантируя, что поиск выполняется от имени конкретного пользователя.
  • Скорость: Использование Algolia или Elasticsearch внутри инструмента обеспечивает мгновенный отклик даже на больших объемах данных, что критично для UX чат-бота.

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

План: От внедрения до деплоя AI в старый код за 6 шагов

Внедрение AI в legacy code может показаться пугающим, но если разбить процесс на этапы, задача становится вполне тривиальной. Главное — двигаться итеративно и не пытаться сделать всё сразу. ↓

Вот пошаговый план действий для вашего Rails-приложения:

  1. Определите узкую проблему.
    Не пытайтесь сделать "AI для всего". Найдите конкретную боль. Например: "Поиск по базе знаний занимает много времени" или "Менеджерам сложно сводить данные по клиентам". В кейсе Mon Ami это был поиск записей клиентов, который требовал сложной логики.
  2. Выберите гем-адаптер.
    Установите rubyllm или langchainrb. Настройте доступ к API (OpenAI/Anthropic) через Rails.application.credentials. Не храните ключи в коде.
  3. Опишите инструменты (Tools).
    Создайте Ruby-классы для действий, которые должен выполнять AI. Начните с чтения данных (Read-only). Например, инструмент для поиска по документации или поиска пользователя.
    • Важно: Встройте проверку Pundit или currentuser внутрь каждого инструмента.
  4. Интегрируйте поиск.
    Если у вас много данных, LLM не сможет обработать их все в контексте. Используйте существующий поиск (Algolia, pg_search) как инструмент для RAG. Агент должен сначала "найти" информацию, а потом "ответить" на основе найденного.
  5. Создайте UI и контроллер.
    Сделайте простой интерфейс чата. Контроллер должен принимать сообщение, инициализировать клиента LLM с контекстом текущего пользователя и передавать список доступных инструментов.
  6. Тестирование и мониторинг.
    Запустите пилотную версию на ограниченной группе пользователей. Логируйте все запросы и ответы (без чувствительных данных), чтобы понимать, как пользователи взаимодействуют с AI.

Как видите, вам не нужно быть экспертом в Data Science, чтобы внедрить AI agent в Rails-приложение. Ваш опыт работы с архитектурой монолита, понимание бизнес-логики и умение работать с инструментами вроде Pundit и Algolia — это и есть ключ к созданию надежной и полезной AI-системы. Не бойтесь "динозавров", учите их новым трюкам.