{"slug": "rubyllm-2-2-the-ai-framework-for-coding-agents", "title": "RubyLLM 2.2: The AI Framework for Coding Agents", "summary": "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.", "body_md": "“You don’t need frameworks anymore.”\n\n“Just let the agent write it. It’s one prompt away. Why would you depend on somebody’s gem?”\n\nHow many of you heard this this year? Yeah.\n\nAnd it’s spreading. My friend [Andrzej Krzywda](https://x.com/andrzejkrzywda/status/2112865294075265072) goes to every conference and talks to everybody, and a few weeks ago he wrote that 40% of devs try to reinvent RubyLLM.\n\nForty percent! That’s one person’s impression. But the idea is in the air. You can feel it.\n\nSo here’s what I think about that, dear Ruby developers.\n\nBullshit.\n\nFrameworks matter. And I won’t just say it. I tested it.\n\n## Same Agent. Same App.\n\nI took a coding agent, Codex on GPT-6 Astra, a really good one. And I gave it the same app to build four times.\n\nWith RubyLLM on Rails. With LangChain. With the Vercel AI SDK. And with no framework at all, plain Ruby, “just let the agent write it”.\n\nA 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.\n\nEleven steps. And after every step, a test suite reruns everything, on every provider. 198 checks at the end.\n\nHere’s what I learned.\n\nThe agent writes it. Streaming on Bedrock? Done. Tools on Gemini? Done. Honestly, they’re amazing at it.\n\nThen you own it. Every feature, on every provider.\n\nEvery provider streams differently. Calls tools differently. Caches differently. Counts tokens differently. [Forty features. Twenty-three providers.](https://rubyllm.com/provider-coverage/)\n\nWant 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.\n\nYou own this now. Forever.\n\nAnd with RubyLLM:\n\n```\nconfig.bedrock_api_key = ENV[\"AWS_ACCESS_KEY_ID\"]\nconfig.bedrock_secret_key = ENV[\"AWS_SECRET_ACCESS_KEY\"]\nconfig.bedrock_region = ENV[\"AWS_REGION\"]\nconfig.bedrock_api_base = ENV[\"BEDROCK_BASE_URL\"]\n```\n\nFour lines of configuration. Same agent wrote them. And then Bedrock does approvals, fallbacks, caching, cancel, everything the app already did.\n\nSo frameworks matter. Case closed?\n\nNot quite.\n\n## Frameworks Matter. Only If Agents Can Use Them Well.\n\nAn 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.\n\nAnd that’s exactly what happened in my first runs. RubyLLM installed, my own skill loaded, and the agent still:\n\n- Wrote its own price table. With the wrong price! RubyLLM keeps a usage ledger, and `chat.cost` reads it.\n- Patched the gem to count usage. RubyLLM already sends an event for every call.\n- Rebuilt HTTP cancellation by hand. RubyLLM has `chat.cancel` .\n- Read 280 thousand tokens of my source code. To find things the guides already said.\n\nThe agent isn’t dumb. It couldn’t see the RubyLLM way from where it stood. So I showed it.\n\n## The Loop\n\nAgents build the same app, on RubyLLM main.\n\nI watch where they stumble. Or where they write code they shouldn’t need.\n\nI fix it in RubyLLM. The gem, a generator, the docs, or the skill.\n\nThen I run it again.\n\nAnd again. Twenty-one fixes.\n\nAnd it found more than stumbles. It found real bugs:\n\n- ~~Sandbox couldn’t see your files~~\n- ~~Agent refused another provider’s model~~\n- ~~Stale cancel killed the next reply~~\n- ~~One huge tool result broke the chat~~\n- ~~No caching on Anthropic, Bedrock~~\n- ~~Refresh lost messages~~\n- ~~Dead worker held its chat~~\n\nIf you had written your own, you would have every one of these bugs right now, and you wouldn’t even know.\n\n## Built for Coding Agents\n\nThe 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.\n\nRubyLLM 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](https://github.com/crmne/skill):\n\n```\ngem exec skill add \"$(bundle show ruby_llm)\" --skill rubyllm --agents coding\n```\n\nOne 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.\n\nThe docs are written so an agent finds the answer. `llms.txt`, Markdown for every page.\n\nAnd 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.\n\nAnd every fix goes out to everyone. One `bundle update`. That’s what a framework is.\n\n## Let’s Count\n\nCode you own at the end. Lines the agent wrote itself, averaged across its attempts. Tests don’t count. Generated code doesn’t count.\n\n|  | Lines | \n|---|---|\n| **RubyLLM on Rails** | **246** | \n| Vercel AI SDK | 748 | \n| LangChain | 962 | \n| No framework | 1,070 | \n\nAnd every provider you add? Lines that speak one provider’s format: RubyLLM, zero. Without a framework, up to 97. Every. Single. Provider.\n\nAnd yes, I tuned RubyLLM against this benchmark. On purpose. So here’s the real test.\n\nA second app. A support desk, with documents, a sandbox, MCP tools and approvals. No agent saw it while I tuned anything.\n\nAbout 420 lines of app code with RubyLLM. 867 without a framework. Half the code.\n\n## Same Bill. Half the Code.\n\nNow let me be honest with you, because you’d find out anyway.\n\nIt 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.\n\nOne model at a time, one task each, a handful of attempts, fake providers in the tests. Take it for what it is.\n\nHalf the code.\n\nAnd think about what happens after the agent is done.\n\n## The Agent Leaves. The Code Stays.\n\nAfter you’ve used it, the agent is out! The accountability of that code stays with you. A thousand lines of it, in your app.\n\nAnd providers change every week. A new model, a new parameter, an error format that changes overnight. Who fixes your thousand lines? You do. Forever.\n\nWith a framework, somebody else fixes it, and everybody gets the fix.\n\nAnd here’s what’s in this one now.\n\n## RubyLLM 2.2\n\nTools ask a person. Window or aisle? The call pauses, no process sits there waiting, and when they answer, `execute` runs again from the top. Ask first, then act.\n\n```\nclass BookFlight < RubyLLM::Tool\n  description \"Books a flight\"\n  parameter :destination, description: \"The city to fly to\"\n\n  def execute(destination:)\n    seat = ask(\"Window or aisle?\", choices: %w[window aisle]) or return \"No seat chosen.\"\n    Flights.book!(destination, seat:)\n  end\nend\n```\n\nAnd from any process, three verbs. Answer, approve, decline.\n\n```\nquestion = chat.pending_questions.first\n\nquestion.answer \"window\"\nquestion.approve remember: true\nquestion.decline \"I'll choose at the gate\"\n\nchat.complete\n```\n\nLet me show you the rest, one at a time.\n\n**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.\n\n**Code in a sandbox, on your own servers.** That’s thanks to [mxc](https://crmne.github.io/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.\n\n```\nchat = RubyLLM.chat.with_code_execution(python: %w[pandas matplotlib])\nchat.ask \"Plot monthly revenue from this export\", with: \"revenue.csv\"\n```\n\n**File stores.** That 40-page PDF doesn’t travel with every question anymore. The model reads it with tools, like a coding agent does.\n\n```\nchat = RubyLLM.chat.with_file_store\n```\n\n**Context budgets.** One giant tool result can’t break the chat.\n\n```\nchat.with_context_budget(per_result: 0.25, per_request: 0.7)\n```\n\n**Skills.** Your workflows, in folders, loaded only when a task needs them. In the open format every coding agent speaks.\n\n**Tests, no provider.** Script what the model says, and run your real agents and pages.\n\n```\nstub_replies tool_call(:weather, city: \"Berlin\"), \"It is sunny.\", cost: 0.01\n```\n\n**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.\n\nTwenty-three providers. 518 built in, every one checked against each provider’s own docs. One API.\n\nThere’s a lot more in [What’s New in 2.2](https://rubyllm.com/whats-new-in-2-2/).\n\nAnd where do all these features come from? I didn’t sit in a room and invent them.\n\n## Rails Came from Basecamp. RubyLLM Comes from Chat with Work.\n\nDHH didn’t design a web framework. He built Basecamp and pulled out what kept working.\n\nSame for me. Everything new in 2.2 earned its place in a real product. And I think what we’ve built is amazing.\n\n[Chat with Work](https://chatwithwork.com). AI bots that work for you. They work. You decide.\n\nA 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.\n\n## This Is Our Time.\n\nPython 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.\n\nAnd now we have the framework for the AI part too.\n\nRubyLLM 2.2. Out today.\n\n```\nbundle update ruby_llm\nbin/rails generate ruby_llm:upgrade\nbin/rails db:migrate\n```\n\nStill on 1.16? One release at a time. [The upgrade guide](https://rubyllm.com/upgrading/) has every step.\n\nTry it, and send me what you find.\n\nFrameworks matter.", "url": "https://wpnews.pro/news/rubyllm-2-2-the-ai-framework-for-coding-agents", "canonical_source": "https://paolino.me/rubyllm-2-2/", "published_at": "2026-11-10 22:15:00+00:00", "updated_at": "2026-11-10 22:51:55.120962+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "ai-tools", "large-language-models", "artificial-intelligence"], "entities": ["RubyLLM", "Carmine Paolino", "LangChain", "Vercel AI SDK", "Codex", "GPT-6 Astra", "Amazon Bedrock", "Andrzej Krzywda"], "also_reported_by": [], "alternates": {"html": "https://wpnews.pro/news/rubyllm-2-2-the-ai-framework-for-coding-agents", "markdown": "https://wpnews.pro/news/rubyllm-2-2-the-ai-framework-for-coding-agents.md", "text": "https://wpnews.pro/news/rubyllm-2-2-the-ai-framework-for-coding-agents.txt", "jsonld": "https://wpnews.pro/news/rubyllm-2-2-the-ai-framework-for-coding-agents.jsonld"}}