<p>RAG(Retrieval-Augmented Generation)は、自社ドキュメントや DB のデータを AI に参照させて回答させる技術です。<br>
「ChatGPT は自社の製品情報を知らない」という問題を解決できます。<br>
本記事では Rails + PostgreSQL(pgvector 拡張)+ RubyLLM を使った RAG 実装を解説します。</p>

<h3 id="what-is-rag">RAG とは</h3>
<p>RAG の流れはシンプルです。</p>
<pre><code>ユーザーの質問
→ 質問をベクトル化(Embedding)
→ DB で類似ドキュメントを検索
→ 検索結果 + 質問を LLM に渡す
→ AI が回答を生成</code></pre>
<p>LLM のファインチューニングが不要で、データを追加・更新するだけで最新情報に対応できます。</p>

<h3 id="setup-pgvector">pgvector のセットアップ</h3>
<p>まず PostgreSQL に pgvector 拡張を追加します。</p>
<pre><code># Gemfile
gem 'neighbor'
gem 'ruby_llm'</code></pre>
<pre><code>bundle install
bin/rails generate neighbor:vector</code></pre>
<p>ドキュメントテーブルのマイグレーションを作成します。</p>
<pre><code>bin/rails generate migration CreateDocuments title:string content:text</code></pre>
<pre><code># db/migrate/XXXXXX_create_documents.rb
class CreateDocuments < ActiveRecord::Migration[8.0]
def change
create_table :documents do |t|
t.string :title
t.text :content
t.vector :embedding, limit: 1536 # OpenAI text-embedding-3-small の次元数
t.timestamps
end
add_index :documents, :embedding, using: :ivfflat,
opclass: :vector_l2_ops
end
end</code></pre>
<pre><code>bin/rails db:migrate</code></pre>

<h3 id="embedding-model">モデルに Embedding を組み込む</h3>
<p>ドキュメント保存時に自動でベクトルを生成します。</p>
<pre><code>class Document < ApplicationRecord
has_neighbors :embedding

before_save :generate_embedding

private

def generate_embedding
return if content.blank?
result = RubyLLM.embed(content, model: 'text-embedding-3-small')
self.embedding = result.vectors
end
end</code></pre>

<h3 id="search">類似ドキュメント検索</h3>
<p>ユーザーの質問をベクトル化して、近いドキュメントを取得します。</p>
<pre><code>def find_relevant_docs(query, limit: 3)
query_embedding = RubyLLM.embed(query, model: 'text-embedding-3-small').vectors
Document.nearest_neighbors(:embedding, query_embedding, distance: 'cosine').limit(limit)
end</code></pre>

<h3 id="generate-answer">RAG で回答を生成</h3>
<p>取得したドキュメントをコンテキストとして LLM に渡します。</p>
<pre><code>def ask_with_rag(question)
docs = find_relevant_docs(question)
context = docs.map { |d| "#{d.title}:
#{d.content}" }.join("

")

prompt = <<~TEXT
以下の情報を参考に質問に答えてください。

#{context}

質問: #{question}
TEXT

chat = RubyLLM.chat(model: 'claude-sonnet-4-6')
chat.ask(prompt).content
end</code></pre>

<h3 id="summary">まとめ</h3>
<p>pgvector + RubyLLM の組み合わせで、完全にオンプレ・自社データで動く AI チャットが実装できます。<br>
ドキュメントを DB に追加するだけで AI の知識が更新されるので、社内 FAQ や製品マニュアルへの Q&A 機能として非常に実用的です。</p>

<h3 id="related">関連記事</h3>
<ul>
<li><a href="/blog/javascript/rubyllm-rails-intro">RubyLLM gem 入門|Rails に ChatGPT・Claude を1時間で組み込む方法</a></li>
<li><a href="/blog/javascript/langchainrb-intro">langchainrb 入門|Ruby で LLM アプリを作る方法</a></li>
</ul>