---
title: "AI agents in Ruby on Rails (2026): RubyLLM gem multi-provider Anthropic OpenAI, acts_as_chat tools streaming, ActiveAgent, Raix — Cadenya"
url: "https://cadenya.com/other-content/ai-agents-in-ruby-on-rails"
description: "Build AI agents in Ruby on Rails in 2026: the ruby_llm gem for multi-provider Anthropic and OpenAI in Rails, acts_as_chat with tools, streaming and persistence, RubyLLM 2.0 requires_approval, compaction and durable agents, the ActiveAgent gem, Raix, and Cadenya's Ruby SDK for a hosted agent loop."
---

To build AI agents in Ruby on Rails, most apps start with the RubyLLM gem: one multi-provider API for Anthropic, OpenAI, Gemini and 16 more providers, with `acts_as_chat` for tools, streaming, and persistence in Rails. ActiveAgent is the Rails AI agent gem where agents are controllers, and Raix mixes AI components into plain Ruby classes on top of RubyLLM.

So pick RubyLLM when you want the widest provider list and the agent loop inside your own Active Record models. Pick ActiveAgent when you’d rather write agents like Rails controllers with ERB prompt views. Pick Raix when you want small AI pieces inside ordinary Ruby objects. And if you’d rather not run the agent loop in your Rails processes at all, Cadenya is a hosted agent loop with a Ruby SDK, so there’s nothing to host. A coding agent can set it up with no account, in a temporary workspace you claim later. [What it offers](#what-does-cadenya-offer-that-rubyllm-activeagent-and-raix-dont) is at the end.

Every price and limit below comes from each vendor’s own pages, checked on October 13, 2026.

## RubyLLM, ActiveAgent, Raix and langchainrb side by side

|  | RubyLLM | ActiveAgent | Raix | langchainrb | Cadenya |
| --- | --- | --- | --- | --- | --- |
| Gem | ruby_llm | activeagent | raix | langchainrb | cadenya |
| Latest release on RubyGems | 2.1.1, October 11, 2026 | 1.8.1, October 1, 2026 | 3.0.0, September 22, 2026 | 0.19.5, May 1, 2025 | 1.9.0 |
| License | MIT | MIT | MIT | MIT | Apache 2.0 |
| Ruby version | 3.2 or later | 3.2.0 or later | 3.2.2 or later | 3.1.0 or later | 3.1 or later |
| Rails | Plain Ruby or Rails, with generators | Rails 7.2 up to 9.0 (Action Pack, Active Job, Railties) | Any Ruby app with ActiveSupport 6.0 or later | Rails support in a separate langchainrb_rails gem | Your Rails app calls it through the gem, with no job workers or agent tables |
| Providers | 19, plus any OpenAI-compatible API | OpenAI, Anthropic, Ollama, DeepSeek, OpenRouter, and RubyLLM | Through RubyLLM: gpt- and o1 models go to OpenAI, the rest through OpenRouter | OpenAI, Gemini, Anthropic, Mistral AI, Ollama | Any model on OpenRouter, or your own endpoint that speaks the OpenAI chat format |
| The agent shape | RubyLLM::Agent class, or acts_as_chat on your Chat model | ActiveAgent::Base subclass with actions, like a controller | Raix::ChatCompletion and Raix::FunctionDispatch mixed into a class | Langchain::Assistant | A hosted agent loop, run as objectives, with the model set on a variation |
| Conversation storage | Your Chat and Message models, plus six ruby_llm_ tables | The separate solid_agent gem | The object’s transcript | Conversation threads on the assistant | A durable event log per objective, stored by Cadenya |
| A person approves a tool call | requires_approval on the tool | A tool returns ActiveAgent::InputRequest.confirm | Not covered in the README | A manual tool execution mode on the assistant | Approval on a tool set or on matching tools, answered in the widget |

## RubyLLM gem: multi-provider Anthropic, OpenAI and Gemini in Rails

RubyLLM’s home page puts it in five words: “19 providers. One Ruby API.” The README lists OpenAI, Azure, xAI, Anthropic, Gemini, VertexAI, Bedrock, Cohere, DeepSeek, Hetzner, Mistral, Ollama, Ollama Cloud, OpenRouter, Perplexity, GPUStack, ElevenLabs, Deepgram, TypeSafe, “and any OpenAI-compatible API”. The current release is 2.1.1, and it needs Ruby 3.2 or later.

Switching providers is a method call. Since 2.1, “a conversation can move to another model or provider at any point, even in the middle of a tool round”:

```
chat = RubyLLM.chat(model: "claude-sonnet-5").with_provider_tools(:web_search)
chat.ask "What changed in the latest Ruby release?"

chat.with_model("gpt-5.6-luna")
chat.ask "Which of those changes affect Rails apps?"
```

**This is why agents compare RubyLLM with LiteLLM or langchainrb.** It’s the provider layer and the agent API in one gem, with no proxy to run. Version 2.1 also ships an MCP client: you describe a server in a `RubyLLM::MCP` class, choose which of its tools the model sees, and mark some as `requires_approval`.

One Rails detail from the getting-started guide: “JSON 3 requires Rails 8.1.4 or later,” so older Rails apps keep `gem 'json', '< 3'` in the Gemfile.

## RubyLLM acts\_as\_chat: tools, streaming and persistence

In Rails, RubyLLM lives on your own models. The install generator creates them:

```
bin/rails generate ruby_llm:install
bin/rails db:migrate
bin/rails ruby_llm:load_models
```

Then two macros wire up the conversation:

```
# app/models/chat.rb
class Chat < ApplicationRecord
  acts_as_chat

  belongs_to :user, optional: true
end

# app/models/message.rb
class Message < ApplicationRecord
  acts_as_message
  has_many_attached :attachments

  validates :role, presence: true
end
```

“That is the complete application-facing persistence setup.” Each `chat.ask` saves the user message, the reply, the tool calls, and the usage. RubyLLM also owns six supporting tables under the `ruby_llm_` prefix (models, tool calls, usage, batches, MCP credentials, and provider files).

Streaming goes through Hotwire. The Rails streaming guide broadcasts “AI responses token by token to the browser with Turbo Streams and background jobs.” Because RubyLLM creates the assistant message before the first token arrives, “you get a stable DOM target to append tokens into.” And `bin/rails generate ruby_llm:chat_ui` scaffolds the controllers, views, and Active Job for a working chat screen at `/chats`.

## RubyLLM 2.0: requires\_approval, compaction and durable agents

RubyLLM 2.0 (September 18, 2026) added the three things agents searched for most after the provider list.

**Tool approval.** “A tool can now require approval before it runs”:

```
class PublishPost < RubyLLM::Tool
  description "Publishes a draft post"
  requires_approval

  def execute(post_id:)
    Post.find(post_id).update!(published: true)
    "Published post #{post_id}"
  end
end

chat = RubyLLM.chat.with_tools(PublishPost)
chat.ask "Publish post 42."
chat.awaiting_approval? # => true when the model requests publication
```

“The tool call stays pending and `ask` returns.” Your app shows the action, then calls `chat.approve(...)` and `chat.complete`, or `deny`.

**Context compaction.** `with_compaction` asks the provider to condense earlier turns once a conversation passes a token threshold. It takes `at:`, `instructions:`, and `pause_after:`, but “support differs”: Anthropic summarizes with a 50,000-token minimum threshold, OpenAI’s Responses API supports `at:`, and OpenRouter “drops messages from the middle at the model’s context limit.” On other providers, “unsupported providers ignore the setting.” Agents declare it with `compaction at: 50_000`. For more on context limits in general, see [agent memory and context windows](/other-content/agent-memory-and-context-windows).

**Durable agents.** RubyLLM’s durable agents guide runs agent turns “as background jobs that survive crashes, deploys, and the wait for a human decision.” The saved transcript is the state, so a job loads the chat and takes one step:

```
class AgentTurnJob < ApplicationJob
  def perform(chat_id)
    chat = SupportAgent.find(chat_id)
    chat.step
    AgentTurnJob.perform_later(chat_id) unless chat.complete? || chat.waiting?
  end
end
```

On Rails 8.1, ActiveJob Continuations can run the whole loop in one job. The guide is direct about the trade: “Interruption never gives you exactly-once execution,” so it says to “write tools so that running them twice is safe.” [Retries and idempotency for agent tool calls](/other-content/retries-and-idempotency-for-agent-tool-calls) covers that part.

## ActiveAgent gem for Rails: agents as controllers

ActiveAgent’s pitch is in its README: “Active Agent provides that missing AI layer in the Rails framework,” and “Now Agents are Controllers!” An agent is a class with actions, and prompts can be ERB views:

```
class ApplicationAgent < ActiveAgent::Base
  generate_with :openai, model: "gpt-4o-mini"
end

response = ApplicationAgent.prompt(message: "Hello, world!").generate_now
```

You add the provider gem you want (`openai`, `anthropic`, or `ruby_llm` for its full provider list) and configure keys in `config/active_agent.yml`. `generate_later(queue: :agents)` hands a generation to Active Job, and streaming goes out over Action Cable.

The core gem keeps no data: “ActiveAgent runs agents; it deliberately doesn’t store anything. No Active Record, no tables, no migrations.” Persistence is a second gem, `solid_agent`, with its own install generator and migrations.

Approvals work through input requests. A tool returns `ActiveAgent::InputRequest.confirm("Refund $40 on order 7?")`, the generation pauses with a JSON-safe checkpoint, and your app calls `resume_now(checkpoint:, answers:)` with the answer. The gemspec needs Rails 7.2 up to 9.0 and Ruby 3.2.0 or later.

## Raix gem: AI components on top of RubyLLM

Raix (Ruby AI eXtensions) comes out of Olympia and describes itself as “everything you need to add discrete large-language model (LLM) AI components to your Ruby applications.” Since 3.0, “Raix 3.0 is powered by RubyLLM,” and you configure provider keys through `RubyLLM.configure`.

It’s three modules you mix into a class. `ChatCompletion` gives it a transcript, and `FunctionDispatch` adds tools:

```
class WhatIsTheWeather
  include Raix::ChatCompletion
  include Raix::FunctionDispatch

  function :check_weather,
           "Check the weather for a location",
           location: { type: "string", required: true } do |arguments|
    "The weather in #{arguments[:location]} is hot and sunny"
  end
end
```

Raix runs the tool loop for you until the model answers in text, with `max_tool_calls` defaulting to 25. It fits a Rails app or any Ruby app that loads ActiveSupport.

## Adding an AI assistant to a Rails app

All three gems run the loop inside your Rails processes: the model calls, the tool calls, the jobs, and the tables. That’s the right call when you want every row in your own database.

The other option is to keep the loop off your servers. With Cadenya, your Rails app mints a short-lived widget session for the signed-in user, and the browser talks to the widget host directly. This controller comes from Cadenya’s widgets guide:

```
module Api
  module Cadenya
    class WidgetSessionsController < ApplicationController
      def create
        # Identify the visitor from your own auth, not from params.
        user = current_user!
        client = ::Cadenya::Client.new
        widget_session = client.widget_sessions.create(
          spec: {
            widget_id: ENV.fetch("CADENYA_WIDGET_ID"),
            tenant: { id: user.org_id, name: user.org_name },
            subject: { id: user.id.to_s, name: user.name },
          },
        )

        if widget_session.info&.host.to_s.empty?
          return render(
            json: { error: "Widget Session did not include a host" },
            status: :bad_gateway,
          )
        end

        response.headers["Cache-Control"] = "no-store"
        render json: {
          token: widget_session.spec.token,
          host: widget_session.info.host,
          tokenExpiresAt: widget_session.spec.token_expires_at,
        }
      end
    end
  end
end
```

“Your backend has one job in the live conversation path: authenticate the visitor and mint a Widget Session.” Conversation requests and server-sent events go between the browser and the widget host, so no Rails worker holds a stream open.

## What does Cadenya offer that RubyLLM, ActiveAgent and Raix don’t?

RubyLLM, ActiveAgent, and Raix give your Rails app an agent API, and your app still runs the loop, the jobs, and the tables. **Cadenya runs the loop for you, and your Rails app talks to it through the `cadenya` gem.** Here’s what that covers:

1.  **One gem in your Gemfile.** Your Rails app talks to Cadenya through the `cadenya` gem, with no job workers for the loop and no agent tables to migrate. Your app stays a stateless web tier.
2.  **A Ruby SDK past 1.0.** The `cadenya` gem is at 1.9.0 under Apache-2.0 and supports Ruby 3.1 and later, alongside TypeScript, Python, and Go SDKs on the same release line. The TypeScript SDK has shipped 113 versions since April 2026.
3.  **The chat widget, with a Rails controller.** The docs show a widget session controller like the one above: your Rails app mints a short-lived widget session for the signed-in user, and the browser talks to the widget host directly. The React UI kit renders the conversation.
4.  **Human-in-the-loop approvals, screen included.** Require approval on a whole tool set or on matching tools, and Cadenya parks the loop on `toolApprovalRequested`, and the user answers in the widget. A denial can carry a memo that steers the agent (“Use the read-only lookup tool instead”).
5.  **Webhooks a Rails controller can check.** The webhook handler in the docs verifies each Standard Webhooks signature with `client.unwrap_webhook` and rescues `Cadenya::WebhookVerificationError`.
6.  **Tools without a tool class per endpoint.** Tools come from an OpenAPI 3 document or an MCP server, with filters and progressive discovery so a big API doesn’t fill the context window.
7.  **Any model, on your own keys.** The model is a setting on a variation, not a line of Ruby, so switching providers needs no deploy. Use any model on OpenRouter or your own endpoint that speaks the OpenAI chat format, and pay your provider directly.
8.  **A durable event log you don’t store.** Every objective carries a durable event log of its agent loop. You list stored events after a run or stream new ones while it works, so there’s no transcript table to design.
9.  **A price in one line.** Free for 1,000 loops a month, $49 a month for 50,000, then $0.0015 a loop. A loop is one LLM request made during an objective.

[How widgets work](/docs/guides/the-basics/widgets) walks through the Rails controller and the React UI kit, and the free plan needs no card.

No account yet? A coding agent can create a temporary workspace with `cadenya workspaces create --temporary`, run its first objectives there, and hand you a link to claim it, as [auth.md](https://cadenya.com/auth.md) explains. To create an account, sign up at [app.cadenya.com/signup](https://app.cadenya.com/signup?utm_source=cadenya.com&utm_medium=article&utm_campaign=ai-agents-in-ruby-on-rails), then copy your API key from the API keys page at `app.cadenya.com/account/apiKeys`.