Rails engine for integrating rcrewai into your Rails applications. Provides ActiveRecord persistence, background job integration, generators, and a web UI for managing AI crews and agents.
- ActiveRecord Integration: Persist crews, agents, tasks, and executions in your database
- Background Job Support: Works with any ActiveJob adapter (Sidekiq, Resque, Delayed Job, etc.)
- Rails Generators: Quickly scaffold new crews and agents
- Web UI: Monitor and manage crews through a built-in interface
- Rails-Specific Tools: Pre-built tools for ActiveRecord, ActionMailer, Rails cache, and more
- Configuration: Flexible configuration through Rails initializers
- Runs on rcrewai 0.10, with features through 0.9 exposed on the models (see rcrewai 0.10, 0.9, 0.8 and 0.7):
- Agent config: reasoning, per-agent LLM, rate limiting, context-window trimming, cognitive memory
- Task output: structured output schemas, guardrails, file output, multimodal attachments
- Crew:
before_kickoff/after_kickoffhooks, planning, theconsensualprocess, batch execution - Knowledge (RAG) sources and Flow persistence
- Checkpointing with resume, LLM interceptors, and the Bedrock / Snowflake / OpenAI-compatible providers
- Concurrent tool calls, with a per-agent opt-out
Every crew execution is traced as a tree of spans — crew, agent, task, LLM call, and tool call — carrying timings, token counts, and cost.
- Trace view at
/rcrewai/executions/:id/observation: a waterfall of the run, with prompts, tool arguments, and errors on each span. - Cost and performance at
/rcrewai/observations/costs: spend and token totals across recent executions. - Live monitoring: the trace view updates over Turbo Streams while a run is in progress.
Configure it in config/initializers/rcrewai.rb:
config.observation_enabled = true
config.observation_capture_prompts = :truncated # :none | :truncated | :full
config.observation_prompt_max_bytes = 4_096
config.observation_flush_mode = :batched # :batched | :immediate
config.observation_retention_days = 30Prompt text is truncated by default: full prompts can be large and may contain personal
data. Set :full only when you need lossless replay.
Prune old spans with the bundled rake task:
rake rcrewai:observation:prune # uses observation_retention_days
rake rcrewai:observation:prune DAYS=7Token and cost data depend on rcrewai's streaming execution path. Usage events are
only emitted when an agent runs via ToolRunner, which passes a stream: to the LLM
client — not via LegacyReactRunner, which does not. ToolRunner is selected when the
tools have JSON schemas and the LLM client reports supports_native_tools?. OpenAI,
Anthropic, and Google all report true, so cost capture works normally with them. With a
provider or configuration that falls back to LegacyReactRunner (for example Ollama
without native tools), the cost dashboard will be empty rather than showing an error.
Agent-level tracing requires rcrewai >= 0.7.1, the version that threads the event stream down to agent execution. On earlier versions traces contain only crew-level spans.
Add this line to your application's Gemfile:
gem 'rcrewai-rails'And then execute:
$ bundle installRun the installation generator:
$ rails generate rcrewai:rails:install
$ rails db:migrateThis will:
- Create the necessary database migrations
- Add an initializer file for configuration
- Mount the engine routes in
config/routes.rb
The install generator above is for new installs — it creates every table, so running it against an app that already has the RcrewAI tables will fail on duplicates.
If you are already running rcrewai-rails, pull in only the migrations you are missing using the standard Rails engine task:
$ rails rcrew_ai_rails:install:migrations
$ rails db:migrate(The task name comes from the engine's railtie name, rcrew_ai_rails.)
Rails copies only the migrations your app does not already have. Upgrading to 0.9.0 from 0.8.x adds one:
| Migration | Purpose |
|---|---|
013_add_parallel_tools_to_rcrewai_agents |
rcrewai_agents.parallel_tools — per-agent opt-out from concurrent tool calls |
Upgrading from 0.7.x adds one more:
| Migration | Purpose |
|---|---|
012_create_rcrewai_checkpoints |
rcrewai_checkpoints, rcrewai_crews.checkpoint_enabled, and run_id/parent_run_id on executions — checkpointing and resume |
Upgrading from 0.6.x adds two more:
| Migration | Purpose |
|---|---|
010_create_rcrewai_spans |
rcrewai_spans and rcrewai_span_events — the observation engine's trace tree |
011_add_observation_rollups_to_rcrewai_executions |
total_cost_usd, total_tokens, span_count, error_count on executions |
All are additive: no existing column or table is changed, and nothing is
dropped. Existing crews, agents, tasks, and executions are unaffected, and
observation is enabled by default once the tables exist. To upgrade the gem
without turning tracing on, set config.observation_enabled = false in
config/initializers/rcrewai.rb before migrating.
If your schema was created by hand (the install generator did not copy a
migration before 0.7.0, so this is likely), review the copied migrations before
running db:migrate and delete any whose tables you already have.
If you need to mount the routes manually, add this to your config/routes.rb:
Rails.application.routes.draw do
mount RcrewAI::Rails::Engine => '/rcrewai'
# Your other routes...
endThis makes the web UI available at /rcrewai and API endpoints at /rcrewai/api/v1/.
Configure RcrewAI Rails in config/initializers/rcrewai.rb:
RcrewAI::Rails.configure do |config|
# ActiveJob queue for background processing
config.job_queue_name = "default"
# Enable/disable web UI
config.enable_web_ui = true
# Use async execution by default
config.async_execution = true
# Default LLM settings
config.default_llm_provider = "openai"
config.default_llm_model = "gpt-4"
# Logging
config.enable_logging = true
config.log_level = :info
end
# Configure the base rcrewai gem. It reads provider keys such as OPENAI_API_KEY
# and ANTHROPIC_API_KEY from the environment on its own, so this is only needed
# for values set in code. Note the capital C: `RcrewAI.configure` is the engine's
# configuration, which has no provider settings. `validate: false` stops a missing
# key from failing boot in places that don't call an LLM, such as assets:precompile.
RCrewAI.configure(validate: false) do |config|
config.openai_api_key = ENV["OPENAI_API_KEY"]
# Add other LLM provider keys as needed
endGenerate a new crew with agents:
$ rails generate rcrewai:rails:crew research_team sequential \
--agents researcher analyst writer \
--description "Research team for market analysis"This creates a crew class in app/crews/research_team_crew.rb.
class ResearchCrew
include RcrewAI::Rails::CrewBuilder
crew_name "research_team"
crew_description "AI-powered research team"
process_type :sequential # :sequential, :hierarchical, or :consensual
def setup_agents
@researcher = create_agent("researcher",
role: "Senior Research Analyst",
goal: "Uncover insights and trends",
backstory: "Expert researcher with years of experience"
)
@writer = create_agent("writer",
role: "Content Writer",
goal: "Create compelling reports",
backstory: "Skilled writer specializing in technical content"
)
end
def setup_tasks
@research_task = create_task("Research latest AI trends",
expected_output: "Comprehensive research report",
position: 1
)
assign_agent_to_task(@researcher, @research_task)
@writing_task = create_task("Write executive summary",
expected_output: "2-page executive summary",
position: 2
)
assign_agent_to_task(@writer, @writing_task)
add_task_dependency(@writing_task, @research_task)
end
end
# Execute the crew
crew = ResearchCrew.new
execution = crew.execute(topic: "AI in Healthcare")The tools are not loaded with the engine, so require the ones you use:
require "rcrewai/rails/tools/active_record_tool"
require "rcrewai/rails/tools/rails_cache_tool"
require "rcrewai/rails/tools/action_mailer_tool"
class DataAnalystAgent
include RcrewAI::Rails::AgentBuilder
agent_role "Data Analyst"
agent_goal "Analyze application data"
tools(
RcrewAI::Rails::Tools::ActiveRecordTool.new(
model_class: User,
allowed_methods: [:count, :where, :pluck]
),
RcrewAI::Rails::Tools::RailsCacheTool.new,
RcrewAI::Rails::Tools::ActionMailerTool.new(
mailer_class: ReportMailer,
allowed_methods: [:send_report]
)
)
endPass the tools as separate arguments, not as one array: tools [a, b] nests the
array and the agent fails when it lists its tools.
Access the web UI at /rcrewai to:
- View all crews and their configurations
- Monitor execution status and logs
- Start new executions
- View execution history and results
Executions run through ActiveJob by default, using whatever adapter your Rails app is configured with:
# Async execution (default)
crew.execute_async(inputs)
# Sync execution
crew.execute_sync(inputs)
# Custom job options
RcrewAI::Rails::CrewExecutionJob.set(wait: 5.minutes).perform_later(crew, inputs)This engine requires rcrewai ~> 0.10.0. The
upgrade needs no migration. rcrewai 0.10 adds live streaming features to its
Anthropic client, and most of them work at the client level, so the engine has no
settings for them:
| rcrewai 0.10 feature | In this engine |
|---|---|
| Keep-alive connection for streaming requests | Applies automatically to Anthropic agents that stream |
config.base_url for Anthropic |
Set it in RCrewAI.configure |
RCrewAI::CancelToken / chat(cancel:) |
Not exposed. Execution#cancel! only updates the record's status and does not stop a running stream |
chat(model:) per-call override |
Not exposed. Use an agent's llm_config to choose its model |
cached_tokens on usage |
Not recorded. The observation engine does not store it on spans or rollups |
To use cancellation or per-call models, call an rcrewai client directly from your own code.
Current Claude models: these reject the temperature that rcrewai sends by
default, so turn it off globally. A per-agent llm_config can't remove it.
RCrewAI.configure(validate: false) { |config| config.temperature = nil }rcrewai 0.9 runs a turn's tool calls concurrently, so a turn costs the slowest call rather than the sum. This is on by default and needs no configuration.
Opt an agent out when its tools are not safe to run in parallel, or must not fan out against a rate-limited API:
agent.update!(parallel_tools: false)Left nil (the default), the gem's own default applies. The observation
engine attributes concurrent tool calls correctly — each tool span is matched
to its result by call_id, not by arrival order.
A crew run can record durable per-task state, so an interrupted run resumes
instead of re-executing (and re-paying for) the tasks that already finished.
Checkpoints are written to rcrewai_checkpoints after each task settles.
# Globally, in config/initializers/rcrewai.rb
config.checkpoint_enabled = true
# ...or per crew
crew.update!(checkpoint_enabled: true)
execution = crew.executions.order(:id).last
execution.run_id # => the checkpointed run
# Resume it: completed tasks are replayed, the rest execute.
crew.resume_sync(execution) # or resume_async(execution)
crew.resumable_executions # executions that recorded a run idA resumed run gets its own run_id and records the original in
parent_run_id, leaving the first run's record intact:
store = RcrewAI::Rails::ActiveRecordCheckpointStore.new
RCrewAI::Checkpoint.lineage(store, resumed.run_id)
# => ["<original run id>", "<resumed run id>"]Any object responding to save/load/list/delete can replace the store via
config.checkpoint_store.
Checkpointing needs the rcrewai_checkpoints table, so run the migration when
you enable it (see Upgrading an existing install).
If the table is missing, the job raises CheckpointTableMissing up front rather
than failing partway through a run.
Hooks that run around every LLM request the engine's agents make — useful for
logging, request tagging, or signing (AWS SigV4 for Bedrock, for instance).
Return a replacement value to modify the payload or result, or nil to leave it
untouched. A hook that raises is reported and skipped, so instrumentation can
never break a run.
config.llm_before_request = lambda do |payload, context|
Rails.logger.info("[llm] -> #{context[:provider]}/#{context[:model]}")
payload
end
config.llm_after_response = lambda do |result, context|
Rails.logger.info("[llm] <- #{context[:duration_ms]}ms")
result
endAlongside openai, anthropic, google, azure and ollama, rcrewai 0.8
adds four, set as an agent's llm_config provider (or
config.default_llm_provider):
| Provider | Notes |
|---|---|
openai_compatible |
Any OpenAI-format endpoint (Groq, Together, Fireworks, vLLM, OpenRouter, a self-hosted gateway). Requires RCrewAI's base_url. |
bedrock |
AWS Bedrock via the Converse API. Requires aws_region. |
snowflake |
Snowflake Cortex inference. Requires snowflake_account. |
openai_responses |
OpenAI's Responses API. Non-streaming only. |
rcrewai 0.8 stamps every event with the id of its enclosing run span, and the observation collector uses it to keep concurrent runs of the same agent apart. Previously both runs shared one span stack, so one run's tool call could nest under the other's iteration.
These capabilities are configured through columns on the persisted models and forwarded to the core objects at build time. All are off/absent by default, so existing records are unaffected — set only what you need.
| Column | Effect |
|---|---|
max_rpm |
Rate-limit the agent's LLM calls (requests per minute) |
reasoning / max_reasoning_attempts |
Run a planning/reasoning pass before answering |
respect_context_window |
Trim history to fit the model's context window |
parallel_tools |
Run a turn's tool calls concurrently (rcrewai 0.9; nil uses the gem default of on) |
llm_config (JSON) |
Per-agent LLM override, e.g. { "provider": "anthropic", "model": "claude-opus-5-5" } (see the temperature note) |
memory_enabled + memory_scope + memory_short_term_limit |
Enable cognitive memory (see below) |
agent = crew.agents.create!(
name: "researcher", role: "Researcher", goal: "Find facts",
reasoning: true,
max_rpm: 30,
# Current Claude models also need RCrewAI's temperature set to nil; see "rcrewai 0.10".
llm_config: { provider: "anthropic", model: "claude-opus-5-5" },
memory_enabled: true, memory_scope: "research", memory_short_term_limit: 20
)Agent memory (rcrewai 0.6+): set memory_enabled: true to turn on cognitive
memory. memory_scope isolates an agent's memories; memory_short_term_limit
caps recent-execution recall. The embedder and store are objects, so configure
them once in the initializer:
RcrewAI::Rails.configure do |config|
config.default_memory_embedder = RCrewAI::Knowledge::Embedder.new
config.default_memory_store = RCrewAI::Memory::SqliteStore.new(path: "db/rcrewai_memory.sqlite3")
end| Column | Effect |
|---|---|
output_schema (JSON) |
Validate/coerce the result against a JSON schema (structured output) |
guardrail_class + guardrail_method_name + guardrail_max_retries |
Validate/transform output, retrying on failure |
output_file + create_directory + markdown |
Write the result to disk |
attachments (JSON) |
Multimodal image inputs, e.g. [{ "type": "image", "url": "https://…" }] |
A guardrail is resolved from a host class: guardrail_class names a class whose
guardrail_method_name accepts the output and returns [ok, value_or_error].
| Column | Effect |
|---|---|
process_type |
"sequential", "hierarchical", or "consensual" |
consensus_agents |
Number of proposers for the consensual process (default 3) |
planning / planning_llm |
Run a planner pass before execution |
before_kickoff_class/_method, after_kickoff_class/_method |
Lifecycle hooks resolved from host classes |
Batch execution (rcrewai kickoff_for_each parity) runs the crew once per
input set, one Execution per input grouped by a shared batch_id:
result = crew.execute_batch_sync([{ topic: "a" }, { topic: "b" }])
crew.batch_executions(result[:batch_id]) # the runs, in order
crew.execute_batch_async(inputs_list) # enqueue N jobs, returns the batch_idAttach sources to an agent (role-specific) or a crew (shared with all its agents):
agent.knowledge_sources.create!(source_type: "url", value: "https://example.com/doc")
crew.knowledge_sources.create!(source_type: "string", value: "Reference text…")
# source_type: "string" | "file" | "pdf" | "csv" | "url"Active sources are embedded lazily at execution. See the memory initializer above for embedder configuration.
Define Flow subclasses in your app (Ruby); the engine persists their state and
runs. Pass RcrewAI::Rails::ActiveRecordStateStore so flows resume from the DB,
and use FlowRun.execute to track a kickoff:
run = RcrewAI::Rails::FlowRun.execute(MyFlow, inputs: { topic: "ruby" })
run.status # "completed" / "failed"
run.result # the final flow state
RcrewAI::Rails::FlowState.find_by(state_id: run.state_id) # the persisted stateThe gem provides these ActiveRecord models:
RcrewAI::Rails::Crew- Crew configurationsRcrewAI::Rails::Agent- Agent definitionsRcrewAI::Rails::Task- Task definitionsRcrewAI::Rails::Execution- Execution historyRcrewAI::Rails::ExecutionLog- Detailed execution logsRcrewAI::Rails::KnowledgeSource- Knowledge (RAG) sources, owned by an agent or a crewRcrewAI::Rails::FlowState- Persisted rcrewai Flow state (resume flows across restarts)RcrewAI::Rails::FlowRun- Flow-run tracking (status, inputs, result, timing)RcrewAI::Rails::Span/SpanEvent- The observation engine's trace treeRcrewAI::Rails::Checkpoint- Persisted crew checkpoints (resume and lineage)
The engine provides JSON API endpoints:
GET /rcrewai/api/v1/crews
GET /rcrewai/api/v1/crews/:id
POST /rcrewai/api/v1/crews
POST /rcrewai/api/v1/crews/:id/execute
GET /rcrewai/api/v1/executions
GET /rcrewai/api/v1/executions/:id
GET /rcrewai/api/v1/executions/:id/status
GET /rcrewai/api/v1/executions/:id/logs
After checking out the repo, run:
$ bundle install
$ bundle exec rspecTo install this gem onto your local machine:
$ bundle exec rake installBug reports and pull requests are welcome on GitHub.
The gem is available as open source under the terms of the MIT License.