The missing Rails convention for LLM calls.
A Claude Skill that teaches Claude Code how to write LLM features
using the patterns Rails devs already know.
Works with ruby_llm · langchain-rb · ruby-openai · anthropic-rb
Why This Exists
Rails has conventions for email, jobs, storage, and config. But not for LLM calls. So every team ends up with something like this:
# Raw API call in a controller. No retries, no cost tracking, blocks the request. def create client = OpenAI::Client.new(access_token: ENV["OPENAI_API_KEY"]) response = client.chat(parameters: { model: "gpt-4o", messages: [{ role: "user", content: "Describe #{@product.name}" }] }) @product.update!(description: response.dig("choices", 0, "message", "content")) end
It works, but it doesn't scale:
- No retries. Rate limits and transient failures fail silently.
- No cost visibility. You can't tell what you're spending per request or per day.
- Scattered prompts. Prompt strings live inline across controllers, services, and rake tasks.
- No consistency. Every developer wires up LLM calls their own way.
Important
This skill gives Claude Code a set of Rails conventions for LLM calls so it generates consistent, structured code whenever you ask it to build AI features.
What It Looks Like
LLM::GenerateDescriptionJob.perform_later(product_id: @product.id)
Behind that one line, Claude generates this structure for you:
app/
services/llm/
product_description_service.rb # Business logic, validation, parsing
base_service.rb # Tracing, retries, cost tracking (inherited)
jobs/llm/
generate_description_job.rb # Async with typed retry rules
prompts/
product_descriptions/
generate.system.erb # Versioned in git, tested on its own
generate.text.erb
config/
llm.yml # Model routing + budget caps (like database.yml)
Each service follows the same pattern:
module LLM class ProductDescriptionService < BaseService self.task_type = :generation # Routes to the right model automatically def validate_params!(params) raise ArgumentError, "product required" unless params[:product] end def prompt_template "product_descriptions/generate" end def parse_response(response) content = response.dig(:choices, 0, :message, :content) { description: content.strip } end end end
Same structure for every LLM feature in your app.
Installation
cp -r rails-llm-integration/ your-rails-app/.claude/skills/rails-llm-integration/
Then open Claude Code in your Rails project and ask it to build a feature:
> Add AI-powered product descriptions to my app
> Set up LLM service objects with cost tracking
> Create a ticket classification service using ruby_llm
Note
Claude reads the skill's reference docs and generates code following these conventions.
Reference Docs
references/
| File | Covers |
|---|---|
client-setup.md |
ruby_llm, langchain-rb, ruby-openai, anthropic-rb |
service-patterns.md |
BaseService, Result, concerns, error types |
job-patterns.md |
Sidekiq queues, retry rules, batch processing |
proxy-routing.md |
config/llm.yml, model routing, budget caps |
eval-pipeline.md |
Braintrust, LLM-as-judge, CI gates |
prompt-management.md |
ERB templates in app/prompts/ |
testing-guide.md |
WebMock, VCR, shared examples, CI strategy |
generators.md |
llm:install and llm:service generators |
templates/
| File | Covers |
|---|---|
base_service.rb.tt |
BaseService with concerns |
base_job.rb.tt |
BaseJob with retry rules |
llm.yml.tt |
Model routing and budget config |
migrations/ |
Batches, dead letters, eval cases, experiments |
scripts/
| File | Covers |
|---|---|
audit_llm_usage.rb |
Finds anti-patterns in your codebase |
Who This Is For
Rails developers shipping LLM features to production.
Covers
| Category | Gem | Use Case |
|---|---|---|
| Recommended | ruby_llm | Multi-provider, clean DSL, ActiveRecord integration |
| RAG | langchain-rb | Vector search, pgvector, embeddings, agents |
| Direct | ruby-openai | OpenAI-only projects |
| Direct | anthropic-rb | Anthropic-only projects |
| Proxy | LiteLLM / Portkey | Multi-provider routing, cost tracking |
| Evals | Braintrust | Trace logging, quality scoring, CI gates |
Contributing
See CONTRIBUTING.md for details.
- Production-proven. Every pattern must come from real production use.
- Copy-pasteable. Code blocks should drop into a Rails app as-is.
License
MIT License. See LICENSE for details.
Claude Code is powerful. This skill makes it consistent.