Generators and App Conventions
Scaffold chats, a chat UI, agents, tools, and schemas with RubyLLM’s Rails generators and conventions.
Table of contents
- Quick Setup with Generator
- Adding a Chat UI
- Conventional Directory Structure
- Rails Generators for Agents, Tools, and Schemas
- Chat UI View Conventions
- Setting Up ActiveStorage
- Next Steps
After reading this guide, you will know:
- What the
ruby_llm:installandruby_llm:chat_uigenerators create for you. - How RubyLLM’s conventional app directory structure is organized.
- How to generate starter agents, tools, and schemas.
- How the generated chat UI renders messages, tool calls, and tool results.
- How to customize model names and set up ActiveStorage.
RubyLLM’s Rails generators take you from an empty app to a working chat in two commands. They write migrations, models, controllers, jobs, views, and a conventional directory layout so your team starts from one shared structure. This guide walks through each generator and the conventions the generated code relies on.
Quick Setup with Generator
The easiest way to get started is using the provided Rails generator:
bin/rails generate ruby_llm:install
The generator:
- Creates migrations for Chat, Message, ToolCall, and Model tables
- Sets up model files with appropriate
acts_asdeclarations - Installs ActiveStorage for file attachments
- Configures the database model registry
- Creates an initializer with sensible defaults
- Creates conventional AI app directories (
v1.14.0+)
After running the generator:
bin/rails db:migrate
bin/rails ruby_llm:load_models # v1.13+
Your Rails app is now AI-ready!
Adding a Chat UI
Want a ready-to-use chat interface? Run the chat UI generator:
bin/rails generate ruby_llm:chat_ui
This creates a complete chat interface with:
- Controllers: Handles chat and message creation with background processing
- Views: Modern UI with Turbo Streams for real-time updates
- Jobs: Background job for processing AI responses without blocking
- Routes: RESTful routes for chats and messages
After running the generator, start your server and visit http://localhost:3000/chats to begin chatting!
The UI generator also supports custom model names:
bin/rails generate ruby_llm:chat_ui chat:Conversation message:ChatMessage model:AIModel
Conventional Directory Structure
RubyLLM’s Rails generators now establish a default app structure:
app/
|-- agents/
|-- prompts/
|-- schemas/
`-- tools/
The install generator creates these directories with .gitkeep files so teams start from one shared convention.
These are conventions, not hard requirements:
- Agents, tools, and schemas can live anywhere in your app autoload paths.
- Prompt lookup convention: named agents automatically resolve
instructions.txt.erbfrom the agent class name when the file exists.
For prompt lookup, RubyLLM uses class name conventions:
WorkAssistant->app/prompts/work_assistant/instructions.txt.erbAdmin::SupportAgent->app/prompts/admin/support_agent/instructions.txt.erb
See the Agents guide for how agent instruction rendering works, or Prompt Rendering for direct RubyLLM.render_prompt usage.
Rails Generators for Agents, Tools, and Schemas
Alongside ruby_llm:install and ruby_llm:chat_ui, Rails apps can generate starter classes for common AI building blocks:
bin/rails generate ruby_llm:agent Support
bin/rails generate ruby_llm:tool Weather
bin/rails generate ruby_llm:schema Product
What each generator creates:
ruby_llm:agent:app/agents/support_agent.rbandapp/prompts/support_agent/instructions.txt.erbruby_llm:tool:app/tools/weather_tool.rbplus tool-specific chat UI partials underapp/views/messages/tool_callsandapp/views/messages/tool_resultsruby_llm:schema:app/schemas/product_schema.rb
Chat UI View Conventions
The generated chat UI follows one convention: each message partial uses the local that matches its partial name.
messages/_user.html.erbgetsusermessages/_assistant.html.erbgetsassistantmessages/_system.html.erbgetssystemmessages/_tool.html.erbgetstoolmessages/_tool_calls.html.erbgetstool_calls
This comes from Rails partial rendering: render @chat.messages calls to_partial_path, and Rails injects a local named after that partial.
For compatibility with model broadcasts (broadcasts_to), generated message partials also accept a message local as a fallback.
Tool Call and Tool Result Partials
Tool-specific partials are generated under app/views/messages/tool_calls and app/views/messages/tool_results:
app/views/messages/
|-- tool_calls/
| |-- _default.html.erb
| `-- _your_tool.html.erb
`-- tool_results/
|-- _default.html.erb
`-- _your_tool.html.erb
Locals passed to those partials:
messages/tool_calls/_your_tool.html.erbreceivestool_callsandtool_callmessages/tool_results/_your_tool.html.erbreceivestool
ruby_llm:tool creates _your_tool.html.erb files with the correct names so custom rendering hooks up automatically.
Using fixed locals keeps the templates dumb and predictable.
Turbo Stream templates used by the generated chat UI:
messages/create.turbo_stream.erbresets the message form forMessagesController#create.
Generator Options
The generator uses Rails-like syntax for custom model names:
# Default - creates Chat, Message, ToolCall, Model
bin/rails generate ruby_llm:install
bin/rails generate ruby_llm:install chat:Conversation message:ChatMessage
bin/rails generate ruby_llm:install chat:Discussion message:DiscussionMessage tool_call:FunctionCall model:AIModel
# Skip ActiveStorage if you don't need file attachments
bin/rails generate ruby_llm:install --skip-active-storage
The name:ClassName syntax follows Rails conventions - specify only what you want to customize.
For most apps, keep the default behavior (install ActiveStorage) so file attachments work out of the box. Use --skip-active-storage only when you’re sure you won’t send files to models.
Setting Up ActiveStorage
The generator automatically configures ActiveStorage for file attachments. If you skipped it during generation, add it manually:
bin/rails active_storage:install
bin/rails db:migrate
Then add to your Message model:
# app/models/message.rb
class Message < ApplicationRecord
acts_as_message
has_many_attached :attachments # Required for file attachments
end
This :attachments association is only required on RubyLLM message records. The ActiveStorage attachments you pass to with: from your own models can use any name.
Next Steps
- Persistence with acts_as - what the generated models do once they
acts_as_chat. - Streaming with Hotwire/Turbo - the streaming flow behind the generated chat UI.
- Agents - build on the generated agent and prompt conventions.
- Advanced Rails Configuration - provider overrides and custom contexts for the generated app.