はじめに
RAG(Retrieval-Augmented Generation)の記事を検索すると、AWS BedrockやAzure AI Searchなどのクラウドサービスを利用した事例が多くヒットします。
しかし、初心者にとっては「裏側で具体的にどのような処理が流れているのか」が見えにくいこともあります。
そこで今回は、LlamaIndexをベースに、ローカルDB(ChromaDB)やローカルEmbedding(Ollama)を組み合わせた、技術的に基礎となるRAGの最小構成を実装してみました。AWSなどのクラウドのマネージドサービスに頼り切らず、各コンポーネントの役割を理解することを目的としています。
全体構成
今回のシステムの全体像は以下の通りです。
- Documents: 知識源となるデータ(PDFやテキストファイル)。
- Embedding (Ollama): 文書を多次元のベクトルデータに変換
- Vector DB (ChromaDB): ベクトル化したデータを保存
- Retriever (LlamaIndex): ユーザーの質問に関連する情報をDBから抽出
- LLM (Ollama): 抽出された知識をコンテキストとして受け取り、最終的な回答を生成
実装
以下のコードでは、ドキュメントの読み込みからインデックス作成、そして質問に対する回答生成までを一気通貫で行います。
環境
Ubuntu 24.04.3
Python 3.12.3
LlamaIndex 0.14.12
Ollama 0.13.5
LLM model : llama3.2:3b
Embedding model : nomic-embed-text
chromadb 1.4.0
用意したドキュメント
今回用意したドキュメントはこちらです。架空の就業規則を使用しました。
ドキュメントの参照を確認するために普通ではありえない「飲み会のルール」を入れています。
## 第1章 総則
### 第1条(目的)
本規則は、架空株式会社(以下「当社」という)の円滑な業務運営と従業員の健全な職場環境の確保を目的とする。
### 第2条(適用範囲)
本規則は、当社の全従業員に適用する。
## 第2章 勤務
### 第3条(勤務時間)
1. 勤務時間は午前9時から午後6時までとする。
2. 休憩時間は正午から午後1時までの1時間とする。
### 第4条(休日)
1. 休日は土曜日、日曜日および国民の祝日とする。
2. 業務上必要な場合は、休日出勤を命じることがある。
## 第3章 行動規範
### 第5条(服装)
従業員は、業務にふさわしい清潔な服装を着用しなければならない。
### 第6条(情報管理)
1. 業務上知り得た情報は、第三者に漏洩してはならない。
2. 個人情報の取り扱いには十分注意しなければならない。
### 第7条(ハラスメントの禁止)
従業員は、いかなるハラスメント行為も行ってはならない。
## 第4章 その他
### 第8条(規則の改定)
本規則の改定は、必要に応じて会社が行うものとする。
## 第5章 飲み会のルール
痛風の社長のためにビールはプリン体オフのものを頼まなければならない。
ソースコード
from llama_index.core import VectorStoreIndex, SimpleDirectoryReader
from llama_index.vector_stores.chroma import ChromaVectorStore
from llama_index.embeddings.ollama import OllamaEmbedding
from llama_index.llms.ollama import Ollama
import chromadb
# 1. ローカルファイルからドキュメント読み込み
documents = SimpleDirectoryReader("docs").load_data() # docs/配下にテキストやPDFを置く
# 2. ChromaDBセットアップ
chroma_client = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = chroma_client.get_or_create_collection("rag_docs")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
# 3. LlamaIndexでインデックス作成
embed_model = OllamaEmbedding(model_name="nomic-embed-text") # 任意のembeddingモデル
index = VectorStoreIndex.from_documents(
documents,
vector_store=vector_store,
embed_model=embed_model,
)
# 4. クエリ→検索→LLM生成
llm = Ollama(model="llama3.2:3b", temperature=0)
query_engine = index.as_query_engine(
llm=llm,
similarity_top_k=3, # 検索上位3件を取得
)
while True:
question = input("\n質問を入力: ")
response = query_engine.query(question)
print(f"\n回答:\n{response}")
コードの解説
1. ドキュメントの読み込み
SimpleDirectoryReader を使用して、指定したディレクトリ内のファイルをスキャンし、解析可能なデータ形式に取り込みます。
documents = SimpleDirectoryReader("docs").load_data() # docs/配下にテキストやPDFを置く
ポイント
docs/ 配下のPDF、テキスト、Markdown、Docxなどを一括で Document オブジェクトへ変換
2. ベクトルDB(ChromaDB)のセットアップ
ドキュメントを検索可能なベクトル形式で保存するための永続化ストレージを設定します。
chroma_client = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = chroma_client.get_or_create_collection("rag_docs")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
ポイント
データの永続化 PersistentClient(path="./chroma_db") により、計算されたベクトルデータを保存
3. インデックスの作成と埋め込みモデル
# 4. クエリ→検索→LLM生成
llm = Ollama(model="llama3.2:3b", temperature=0)
query_engine = index.as_query_engine(
llm=llm,
similarity_top_k=3, # 検索上位3件を取得
)
while True:
question = input("\n質問を入力: ")
response = query_engine.query(question)
print(f"\n回答:\n{response}")
ポイント
関連情報の厳選 similarity_top_k=3 により、関連度の高い上位3つの文章断片のみを抽出し、LLMの回答精度を高めます。
出力結果
$ python ragdemo01.py
2026-01-18 20:54:16,633 - INFO - Anonymized telemetry enabled. See https://docs.trychroma.com/telemetry for more information.
2026-01-18 20:54:17,627 - INFO - HTTP Request: POST http://localhost:11434/api/embed "HTTP/1.1 200 OK"
2026-01-18 20:54:17,680 - INFO - HTTP Request: POST http://localhost:11434/api/show "HTTP/1.1 200 OK"
質問を入力: 勤務時間について教えて
2026-01-18 20:54:36,410 - INFO - HTTP Request: POST http://localhost:11434/api/embed "HTTP/1.1 200 OK"
2026-01-18 20:54:50,472 - INFO - HTTP Request: POST http://localhost:11434/api/chat "HTTP/1.1 200 OK"
回答:
勤務時間は午前9時から午後6時までとする。
質問を入力: 飲み会のルールについて教えて
2026-01-18 20:55:08,303 - INFO - HTTP Request: POST http://localhost:11434/api/embed "HTTP/1.1 200 OK"
2026-01-18 20:55:09,924 - INFO - HTTP Request: POST http://localhost:11434/api/chat "HTTP/1.1 200 OK"
回答:
飲み会のルールとして、痛風の社長のためにビールはプリン体オフのものを頼まなければならないと規定されています。
質問を入力: フレキシブルタイム制について教えて
2026-01-18 20:55:26,009 - INFO - HTTP Request: POST http://localhost:11434/api/embed "HTTP/1.1 200 OK"
2026-01-18 20:55:27,081 - INFO - HTTP Request: POST http://localhost:11434/api/chat "HTTP/1.1 200 OK"
回答:
この会社では、勤務時間は午前9時から午後6時までと決められています。
2つ目の質問まではちゃんとドキュメントを参照して答えられていますが、
ドキュメントに無い3つ目の質問では代わりに勤務時間を答えてしまっています。
課題
- ドキュメントに無い質問をした際の回答の精度が低い
- 回答出力までの時間が遅い
おわりに
クラウドのRAGサービスは非常に便利ですが、今回のように各パーツを自分で選んで組み合わせることで、「どこで何が計算され、どのようにデータが流れているのか」というRAGの仕組みそのものが理解しやすくなります。
今後は、精度の向上を目指してreranking(再順位付け)の導入や、GPU・NPUを用いた処理速度の向上などを目指してきたいと思います。
参考
