Contents
AIエージェントとは?Railsでの活用シーン
AI エージェントは、大規模言語モデル(LLM)をバックエンドに持ち、自然言語で指示や問い合わせを受け取って自律的に処理するコンポーネントです。Rails の MVC と組み合わせることで ビジネスロジック と AI ロジック を明確に分離でき、保守性の高いコードベースが実現します。本セクションでは、代表的な業務シーンと導入メリットを解説します。
HRMS における具体的な活用例
HR システム(Human Resource Management System)では、評価コメントやヘルプデスクの問い合わせが大量に発生します。AI エージェントを組み込むことで次のような効果が期待できます。
- 自動要約・分析:従業員が入力した自由記述テキストを LLM に送信し、要約や感情スコアを取得。
- 検索性向上:要約結果やタグ付け情報をデータベースに保存することで、管理者はダッシュボードから瞬時に必要な情報へアクセス可能。
- 業務負荷軽減:手作業で行っていたレビュー工程が削減され、ヒューマンエラーも低減します。
※要約処理のコストは利用する LLM の料金表とトークン使用量に依存します。実際の見積もりは各ベンダーの公式プライシングページをご参照ください(例:OpenAI、Anthropic、Google Cloud)。
業務プロセス全般への適用ポイント
AI エージェントは HR 以外でもさまざまな業務に活用できます。以下は典型的なユースケースです。
- ナレッジベース検索:社内ドキュメントや過去のサポートチケットを LLM に問い合わせ、関連情報を即座に返す。
- 文書自動分類:経費精算書類の OCR テキストを LLM が解析し、勘定科目や承認フローへ自動振り分け。
- レポート生成:毎朝の営業サマリーやプロジェクトステータスを LLM に要約させ、Slack などの通知チャネルに配信。
Rails のバックグラウンドジョブ(例:Sidekiq)と組み合わせることで、時間がかかる LLM 呼び出しも非同期化でき、ユーザー体験を阻害しません。
RubyLLM の導入と設定
ruby_llm は コミュニティがメンテナンスしている gem で、OpenAI、Anthropic、Google Gemini など複数のプロバイダーに対する統一インターフェースを提供します。公式リポジトリや API リファレンスは以下です。
- GitHub: https://github.com/karafka/ruby_llm
- RubyGems: https://rubygems.org/gems/ruby_llm
gem のインストール手順
まず Gemfile に追加し、Bundler でインストールします。Rails 標準の依存関係と競合しにくい設計です。
|
1 2 3 |
# Gemfile gem 'ruby_llm', '~> 0.4' # 最新安定版を使用 |
|
1 2 |
$ bundle install |
次に初期化ファイルでプロバイダー情報と API キーを設定します。config/initializers/ruby_llm.rb に以下を書き込みます。
|
1 2 3 4 5 6 7 |
# config/initializers/ruby_llm.rb RubyLLM.configure do |c| c.provider = :anthropic # :openai, :gemini も選択可 c.api_key = ENV.fetch('ANTHROPIC_API_KEY') c.timeout = 30 # 秒単位のタイムアウト設定 end |
ポイント:環境変数は
.env(開発・テスト)やRails.application.credentials(本番)で安全に管理します。公式ガイドは https://guides.rubyonrails.org/security.html#custom-credentials を参照してください。
対応 LLM と選定指針
利用するモデルはプロジェクトの コスト感度、応答速度、データ保護要件 に合わせて選びます。下表は主要 3 社の特性をまとめたものです(公式情報へのリンク付き)。
| 項目 | OpenAI (GPT) | Anthropic (Claude) | Google Gemini |
|---|---|---|---|
| 生成品質 | 多様な文体・コード補完が得意 (https://platform.openai.com/docs/models/gpt-4) |
安全性と指示遵守に重点 (https://docs.anthropic.com/claude/reference) |
大規模データに基づく高速応答 (https://cloud.google.com/vertex-ai/docs/generative-ai) |
| 料金体系 | トークン単価が明示(例:$0.002 / 1k トークン) | 同様のトークン課金だが、プロンプト単価は若干低め | 無料枠あり、使用量に応じて従量課金 |
| レートリミット | プランにより変動(例:60 rpm) | 標準で 30 rpm | Google Cloud の API 制限に依存 |
| データ保持・暗号化 | オプトインでデータ保存可、暗号化は標準 | デフォルトで永続保存なし、暗号化済み | IAM と Cloud KMS による細粒度制御 |
選定のヒント
- 機密情報を扱う場合は Anthropic のデータ保持ポリシー が有利です。
- コード生成や多言語対応が重要なら OpenAI が最適です。
- 既に Google Cloud を利用している組織は Gemini に統合しやすいでしょう。
環境変数・シークレット管理のベストプラクティス
機密情報の漏洩リスクを最小化するため、以下の手順で設定します。
- dotenv-rails(開発/テスト)と Rails Encrypted Credentials(本番)を併用。
.envファイルは必ず.gitignoreに追加し、リポジトリにコミットしない。- CI/CD では GitHub Actions の
secrets機能で環境変数を注入する。
|
1 2 3 4 |
# .env.development ANTHROPIC_API_KEY=sk-ant-xxxxxxxxxxxx OPENAI_API_KEY=sk-openai-xxxxxxxxxxxx |
GitHub Actions の設定例(後述の CI パイプライン参照):
|
1 2 3 4 |
# .github/workflows/ci.yml で環境変数をエクスポート - name: Set API keys run: echo "ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }}" >> $GITHUB_ENV |
Service Object と非同期ジョブで外部 API を呼び出す
Service Object の実装例
Rails アプリでは コントローラ にロジックを詰め込まず、app/services 配下に単一責務のクラスとして切り出すことがベストプラクティスです(公式ガイド https://guides.rubyonrails.org/active_record_validations.html#service-objects 参照)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# app/services/summary_generator.rb class SummaryGenerator def initialize(text:) @text = text @client = RubyLLM::Client.new end # 要約文字列を返す。失敗時は nil を返す。 def call response = @client.completions( model: :claude_v2, prompt: "以下のテキストを要約してください:\n#{@text}", max_tokens: 150 ) response.dig('choices', 0, 'text').strip rescue RubyLLM::Error => e Rails.logger.error("[SummaryGenerator] #{e.class}: #{e.message}") nil end end |
コントローラは 入力の受け取りとジョブ起動 のみを担当します。
|
1 2 3 4 5 6 7 8 9 |
# app/controllers/summaries_controller.rb class SummariesController < ApplicationController def create summary = Summary.create!(original_text: params[:text], status: :processing) GenerateSummaryJob.perform_later(summary.id) render json: summary, status: :accepted end end |
ActiveJob / Sidekiq でのバックグラウンド処理
LLM 呼び出しは数秒以上かかることがあるため、Sidekiq と Rails の抽象層 ActiveJob を利用して非同期化します(公式ドキュメント https://guides.rubyonrails.org/active_job_basics.html、Sidekiq ガイド https://github.com/mperham/sidekiq/wiki/Active-Job)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
# app/jobs/generate_summary_job.rb class GenerateSummaryJob < ApplicationJob queue_as :default retry_on RubyLLM::Error, attempts: 3, wait: :exponential_backoff def perform(summary_id) summary = Summary.find(summary_id) generated = SummaryGenerator.new(text: summary.original_text).call if generated summary.update!(content: generated, status: :completed) else summary.update!(status: :failed) end end end |
ポイント
- retry_on により一時的な API エラーは自動リトライ。
- ジョブ完了後にステータスを更新することで、フロントエンド側で進捗表示が可能です。
テスト戦略:RSpec とモック/スタブ
LLM クライアントのモック化例
外部 API への実ネットワーク呼び出しはテスト速度と安定性を阻害します。WebMock と RSpec Mocks を組み合わせてクライアントをスタブ化しましょう(公式ガイド https://github.com/bblimke/webmock、https://relishapp.com/rspec/rspec-mocks/docs)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
# spec/services/summary_generator_spec.rb require 'rails_helper' RSpec.describe SummaryGenerator do let(:text) { "従業員の評価コメント…" } let(:client_double) { instance_double(RubyLLM::Client) } before do allow(RubyLLM::Client).to receive(:new).and_return(client_double) allow(client_double).to receive(:completions).and_return( 'choices' => [{ 'text' => "要約結果" }] ) end it "returns the trimmed summary" do result = described_class.new(text: text).call expect(result).to eq("要約結果") expect(client_double).to have_received(:completions) .with(hash_including(model: :claude_v2, prompt: include(text))) end context "when the API raises an error" do before { allow(client_double).to receive(:completions).and_raise(RubyLLM::Error) } it "logs the error and returns nil" do expect(Rails.logger).to receive(:error).with(/API error/) expect(described_class.new(text: text).call).to be_nil end end end |
エンドツーエンドなリクエストスペック
コントローラ層の動作とジョブキューイングを検証する例です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
# spec/requests/summaries_spec.rb require 'rails_helper' RSpec.describe "Summaries API", type: :request do let(:valid_params) { { text: "評価コメントサンプル" } } it "enqueues a job and returns 202 Accepted" do expect { post "/summaries", params: valid_params, as: :json }.to have_enqueued_job(GenerateSummaryJob) expect(response).to have_http_status(:accepted) end end |
テストカバレッジの指針
- Service Object の正常系・例外系を網羅。
- ActiveJob のリトライロジックも perform_now で直接呼び出し、ステータス遷移を確認。
- コントローラは request spec に留め、ルーティングやパラメータバリデーションのチェックを行う。
アーキテクチャ・CI/CD・運用ベストプラクティス
DDD / Modular Monolith のディレクトリ構成例
Rails 単一リポジトリでも ドメイン境界 を意識したフォルダ分割が可能です。以下は「要約機能」を中心に据えた例で、公式ガイド https://guides.rubyonrails.org/engines.html の概念を流用しています。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
app/ ├─ domain/ │ └─ summarization/ │ ├─ entities/ # Summary Entity(ActiveRecord ラッパー) │ ├─ services/ # SummarizeUseCase (Service Object) │ └─ repositories/ # SummaryRepository ├─ infrastructure/ │ └─ llm_clients/ │ └─ anthropic_client.rb # RubyLLM をラップしたクラス ├─ application/ │ └─ jobs/ │ └─ generate_summary_job.rb └─ controllers/ └─ summaries_controller.rb |
メリット
- ビジネスロジックは domain に集約され、インフラ変更(例:Anthropic → Gemini)時は infrastructure/llm_clients のみ差し替えれば済む。
- テスト対象が明確になるため、ユニットテストと統合テストの境界がはっきりします。
GitHub Actions による CI パイプライン
CI では 依存解決 → 静的解析 → RSpec 実行 → カバレッジアップロード の流れを自動化します(公式リファレンス https://docs.github.com/en/actions)。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 |
# .github/workflows/ci.yml name: CI on: push: branches: [main] pull_request: jobs: test: runs-on: ubuntu-latest services: redis: image: redis:7-alpine ports: ['6379:6379'] env: RAILS_ENV: test ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} steps: - uses: actions/checkout@v3 - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: '3.2' bundler-cache: true - name: Install dependencies run: bundle install --jobs 4 --retry 3 - name: Run RuboCop (コード品質) run: bundle exec rubocop - name: Run RSpec run: | bundle exec rspec --format documentation # カバレッジは SimpleCov が自動で生成 - name: Upload coverage artifact uses: actions/upload-artifact@v3 with: name: coverage-report path: coverage/ |
ポイント
- secrets に格納した API キーはテストコード内で使用しない(モック化する)。
- RuboCop と SimpleCov でコード品質とテスト網羅率を可視化。
本番運用でのトークン管理・レートリミット対策
LLM の課金は トークン単位、レートリミットはプロバイダーごとに異なるため、以下の施策で安定稼働を確保します。
- 使用量モニタリング
Prometheusにruby_llm_requests_totalとruby_llm_tokens_usedをエクスポートし、Grafana で可視化。- レートリミット制御
- Sidekiq の
sidekiq-throttlerプラグインで分単位の上限を設定(例:Anthropic は 30 rpm)。 - キャッシュ戦略
- 同一テキストの要約結果は Redis に保存し、再利用率が高いパターンでは API 呼び出しを省く。
|
1 2 3 4 5 6 7 8 9 |
# sidekiq.yml (レート制御例) :queues: - default :concurrency: 10 :limits: anthropic_rate_limit: limit: 30 # requests per minute period: 60 |
エラーハンドリングと監視
LLM 呼び出し失敗は業務フローに直結するため、統一エラークラス と外部モニタリングツールの併用が必須です。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
# app/services/summary_generator.rb (改良版) class SummaryGenerator class RateLimitExceeded < StandardError; end def call response = client.completions(...) parse(response) rescue RubyLLM::RateLimitError => e handle_rate_limit(e) rescue RubyLLM::Error => e log_and_notify(e) nil end private def handle_rate_limit(error) raise RateLimitExceeded, error.message end def log_and_notify(error) Rails.logger.warn("[SummaryGenerator] #{error.class}: #{error.message}") Sentry.capture_exception(error, tags: { llm: 'anthropic' }) end end |
- ロギングは
lograge(JSON ログ)を導入し、検索しやすい構造化ログに。 - 監視は Sentry のアラートポリシーで「RateLimitExceeded が 5 分間に 3 回以上」なら Slack に通知。公式ドキュメント https://docs.sentry.io/platforms/ruby/ を参照してください。
まとめ
- AI エージェントは Rails の MVC と相性が良く、HRMS やナレッジベースなどの自動化に最適。
- ruby_llm(コミュニティ製 gem)を用いれば、OpenAI・Anthropic・Gemini への統一インターフェースで呼び出しが可能。公式リファレンスへのリンクを添えて安全に導入しましょう。
- Service Object + ActiveJob/Sidekiq による非同期設計は、LLM の遅延やレートリミットに強く、テスト容易性も向上します。
- RSpec と WebMock で外部依存を排除した高速テストスイートを構築し、CI に組み込むことで品質を継続的に保証できます。
- DDD / Modular Monolith のディレクトリ設計 と GitHub Actions を活用すれば、コードベースの可読性とデプロイ自動化が実現します。
- 本番運用では トークン使用量モニタリング、レート制御、キャッシュ、そして Sentry でのエラーハンドリング を組み合わせてコスト最適化と安定稼働を両立させます。
これらのベストプラクティスに沿って実装すれば、Rails 開発者は 安全・高速・拡張性の高い AI エージェント を自信を持ってプロダクトへ組み込むことができます。ぜひサンプルリポジトリでコードを確認し、ハンズオンで体感してください。