cd /news/ai-agents/rubyllm-2-2-the-ai-framework-for-cod… · home › topics › ai-agents › article
[ARTICLE · art-167295] src=paolino.me ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

RubyLLM 2.2: The AI Framework for Coding Agents

RubyLLM creator Carmine Paolino released RubyLLM 2.2, an update to the Ruby AI framework built specifically for coding agents, after running a four-way test in which Codex on GPT-6 Astra built the same streaming chat app 11 times over — with RubyLLM on Rails, LangChain, the Vercel AI SDK, and plain Ruby with no framework — validated by 198 checks across providers. Paolino reported that without a framework the agent hand-signed every Bedrock request with AWS signature version 4 and decoded Amazon's binary event stream frame by frame in 93 lines for one provider, versus four lines of RubyLLM configuration, and that a 21-iteration feedback loop of watching agents stumble and fixing the gem, generators, docs and skill surfaced seven real bugs including missing Anthropic and Bedrock caching and a stale cancel that killed the next reply. The release ships a skill inside the gem so coding agents can find RubyLLM's way of handling streaming, tools, caching and token counting across 40 features and 23 providers.

by read9 min views2 publishedNov 10, 2026
RubyLLM 2.2: The AI Framework for Coding Agents
Image: Paolino (auto-discovered)

“You don’t need frameworks anymore.”

“Just let the agent write it. It’s one prompt away. Why would you depend on somebody’s gem?”

How many of you heard this this year? Yeah.

And it’s spreading. My friend Andrzej Krzywda goes to every conference and talks to everybody, and a few weeks ago he wrote that 40% of devs try to reinvent RubyLLM.

Forty percent! That’s one person’s impression. But the idea is in the air. You can feel it.

So here’s what I think about that, dear Ruby developers.

Bullshit.

Frameworks matter. And I won’t just say it. I tested it.

Same Agent. Same App. #

I took a coding agent, Codex on GPT-6 Astra, a really good one. And I gave it the same app to build four times.

With RubyLLM on Rails. With LangChain. With the Vercel AI SDK. And with no framework at all, plain Ruby, “just let the agent write it”.

A real chat app. Streaming, conversations, cancel, cost. A second provider you can switch to in the middle of a conversation. Fallbacks. Surviving a restart. Approvals. Files and a sandbox. MCP. Then Gemini, Azure, Bedrock.

Eleven steps. And after every step, a test suite reruns everything, on every provider. 198 checks at the end.

Here’s what I learned.

The agent writes it. Streaming on Bedrock? Done. Tools on Gemini? Done. Honestly, they’re amazing at it.

Then you own it. Every feature, on every provider.

Every provider streams differently. Calls tools differently. Caches differently. Counts tokens differently. Forty features. Twenty-three providers.

Want to see one feature for one provider? With no framework, the agent signed every Bedrock request by hand, AWS signature version 4. Then it decoded Amazon’s binary event stream by hand, frame by frame, checking the CRCs. Ninety-three lines. For one provider. It works.

You own this now. Forever.

And with RubyLLM:

config.bedrock_api_key = ENV["AWS_ACCESS_KEY_ID"]
config.bedrock_secret_key = ENV["AWS_SECRET_ACCESS_KEY"]
config.bedrock_region = ENV["AWS_REGION"]
config.bedrock_api_base = ENV["BEDROCK_BASE_URL"]

Four lines of configuration. Same agent wrote them. And then Bedrock does approvals, fallbacks, caching, cancel, everything the app already did.

So frameworks matter. Case closed?

Not quite.

Frameworks Matter. Only If Agents Can Use Them Well. #

An agent takes the easiest path it can see. If it can’t see your framework’s way, it writes its own. Then your framework is dead weight: a dependency, and the agent’s own code on top.

And that’s exactly what happened in my first runs. RubyLLM installed, my own skill loaded, and the agent still:

  • Wrote its own price table. With the wrong price! RubyLLM keeps a usage ledger, and chat.cost reads it.
  • Patched the gem to count usage. RubyLLM already sends an event for every call.
  • Rebuilt HTTP cancellation by hand. RubyLLM has chat.cancel .
  • Read 280 thousand tokens of my source code. To find things the guides already said.

The agent isn’t dumb. It couldn’t see the RubyLLM way from where it stood. So I showed it.

The Loop #

Agents build the same app, on RubyLLM main.

I watch where they stumble. Or where they write code they shouldn’t need.

I fix it in RubyLLM. The gem, a generator, the docs, or the skill.

Then I run it again.

And again. Twenty-one fixes.

And it found more than stumbles. It found real bugs:

  • Sandbox couldn’t see your files
  • Agent refused another provider’s model
  • Stale cancel killed the next reply
  • One huge tool result broke the chat
  • No caching on Anthropic, Bedrock
  • Refresh lost messages
  • Dead worker held its chat

If you had written your own, you would have every one of these bugs right now, and you wouldn’t even know.

Built for Coding Agents #

The new user of a framework is a coding agent. So I spent a ton of time on that this cycle. As much as on features.

RubyLLM ships a skill, inside the gem, so it matches your version. And to install any skill just using Ruby, I made a new gem, skill:

gem exec skill add "$(bundle show ruby_llm)" --skill rubyllm --agents coding

One command, and Claude Code, Codex, Cursor know the RubyLLM way. It works like npx skills. But it also adds skills to RubyLLM itself, in your Rails app, with a skills.lock. Gemfile.lock, for skills.

The docs are written so an agent finds the answer. llms.txt, Markdown for every page.

And the API stays small on purpose. One verb per meaning. Every public method is in api.txt, and CI fails when it changes without me. Every extra name is a wrong guess an agent can make.

And every fix goes out to everyone. One bundle update. That’s what a framework is.

Let’s Count #

Code you own at the end. Lines the agent wrote itself, averaged across its attempts. Tests don’t count. Generated code doesn’t count.

Lines
RubyLLM on Rails 246
Vercel AI SDK 748
LangChain 962
No framework 1,070

And every provider you add? Lines that speak one provider’s format: RubyLLM, zero. Without a framework, up to 97. Every. Single. Provider.

And yes, I tuned RubyLLM against this benchmark. On purpose. So here’s the real test.

A second app. A support desk, with documents, a sandbox, MCP tools and approvals. No agent saw it while I tuned anything.

About 420 lines of app code with RubyLLM. 867 without a framework. Half the code.

Same Bill. Half the Code. #

Now let me be honest with you, because you’d find out anyway.

It wasn’t cheaper. On the chat app it cost less, about $12 against $17 or more in earlier runs. On the support desk it cost more, between $14 and $19 against $13. Same bill, give or take. The agent still reads a lot, and you pay for every read. The new skill in 2.2 already cut the chat app’s bill by about a quarter, and I’m not done. With Claude Opus it was 1,088 lines against 2,969, and the RubyLLM run cost more, $19 against $11. I’m not going to stand here and lie to you about it.

One model at a time, one task each, a handful of attempts, fake providers in the tests. Take it for what it is.

Half the code.

And think about what happens after the agent is done.

The Agent Leaves. The Code Stays. #

After you’ve used it, the agent is out! The accountability of that code stays with you. A thousand lines of it, in your app.

And providers change every week. A new model, a new parameter, an error format that changes overnight. Who fixes your thousand lines? You do. Forever.

With a framework, somebody else fixes it, and everybody gets the fix.

And here’s what’s in this one now.

RubyLLM 2.2 #

Tools ask a person. Window or aisle? The call s, no process sits there waiting, and when they answer, execute runs again from the top. Ask first, then act.

class BookFlight < RubyLLM::Tool
  description "Books a flight"
  parameter :destination, description: "The city to fly to"

  def execute(destination:)
    seat = ask("Window or aisle?", choices: %w[window aisle]) or return "No seat chosen."
    Flights.book!(destination, seat:)
  end
end

And from any process, three verbs. Answer, approve, decline.

question = chat.pending_questions.first

question.answer "window"
question.approve remember: true
question.decline "I'll choose at the gate"

chat.complete

Let me show you the rest, one at a time.

A chat survives a deploy. One process runs it at a time, so a double click never runs a tool twice. Deploy in the middle of a reply? Chat.interrupted finds it, and the generated ResumeInterruptedChatsJob picks it up as soon as the server boots. A tool call that had started never runs again: the model hears it may have partly run, and decides.

Code in a sandbox, on your own servers. That’s thanks to mxc, my new gem. It’s Microsoft’s MXC, from Ruby, and improved. The operating system’s own isolation. The files it makes come back as attachments.

chat = RubyLLM.chat.with_code_execution(python: %w[pandas matplotlib])
chat.ask "Plot monthly revenue from this export", with: "revenue.csv"

File stores. That 40-page PDF doesn’t travel with every question anymore. The model reads it with tools, like a coding agent does.

chat = RubyLLM.chat.with_file_store

Context budgets. One giant tool result can’t break the chat.

chat.with_context_budget(per_result: 0.25, per_request: 0.7)

Skills. Your workflows, in folders, loaded only when a task needs them. In the open format every coding agent speaks.

Tests, no provider. Script what the model says, and run your real agents and pages.

stub_replies tool_call(:weather, city: "Berlin"), "It is sunny.", cost: 0.01

A generator for every building block. Agents, tools, schemas, MCP servers, skills, sandboxes, evaluations, judges. And the chat UI, with a model switcher on every message.

Twenty-three providers. 518 built in, every one checked against each provider’s own docs. One API.

There’s a lot more in What’s New in 2.2.

And where do all these features come from? I didn’t sit in a room and invent them.

Rails Came from Basecamp. RubyLLM Comes from Chat with Work. #

DHH didn’t design a web framework. He built Basecamp and pulled out what kept working.

Same for me. Everything new in 2.2 earned its place in a real product. And I think what we’ve built is amazing.

Chat with Work. AI bots that work for you. They work. You decide.

A bot starts on an event or a time, and works across your tools. What needs you comes to one Decisions dashboard. Changes ask first, unless you let them run. And every one of those chats is rows in my database. My tables, my backups, my queries. The application owns the conversation.

This Is Our Time. #

Python had the training. JavaScript had the browser. But building AI products? Products people pay for, with approvals and billing and a database you own? That’s what Rails was made for.

And now we have the framework for the AI part too.

RubyLLM 2.2. Out today.

bundle update ruby_llm
bin/rails generate ruby_llm:upgrade
bin/rails db:migrate

Still on 1.16? One release at a time. The upgrade guide has every step.

Try it, and send me what you find.

Frameworks matter.

── more in #ai-agents 4 stories · sorted by recency
── more on @rubyllm 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/rubyllm-2-2-the-ai-f…] indexed:0 read:9min 2026-11-10 · —