簡単なRAG
Simple RAG: LangChain + Ollama + FAISS を使った簡易RAG構成
Ollama・LangChain・FAISSを使い、ローカル環境でRAG(検索拡張生成)を構築する手順をまとめた実験です。
1. pyenvのインストール
Python環境のバージョン管理のため pyenv を使用します。
sudo yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel git libffi-devel
git clone https://github.com/pyenv/pyenv.git ~/.pyenv
if grep "pyenv init -" ~/.bash_profile ; then
echo "skip."
else
echo 'export PATH="$HOME/.pyenv/bin:$PATH"' >> ~/.bash_profile
echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
fi
source ~/.bash_profile
pyenv install --list
pyver="3.13.5"
pyenv install ${pyver}
pyenv global ${pyver}
pyenv rehash
2. sqlite3のビルド(ChromaDB対応)
ChromaDBやFAISS利用時の依存として最新版の sqlite3 を使用します。
curl -O https://www.sqlite.org/2025/sqlite-src-3500200.zip
unzip sqlite-src-3500200.zip
cd sqlite-src-3500200/
./configure && make
sudo make install
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH
3. プロジェクト構成
RAGプロジェクトを作成します。
mkdir rag
cd rag
# 必要なPythonライブラリ
cat <<'EOF' > requirements.txt
langchain
ollama
faiss-cpu
langchain-community
langchain-ollama
EOF
pip3.13 install -r requirements.txt
4. ドキュメント作成
検索対象となるサンプルドキュメント(Markdown形式)を用意します。
mkdir docs
cat <<'EOF' > docs/text.md
* 猫はニャー
* 犬ならワン
* 象はパオーン
* ライオンガオーッ
* 人間だもんあかさたなはやまらわおん
EOF
# モデルのダウンロード(埋め込み用と生成用)
ollama pull gemma3n
ollama pull nomic-embed-text
5. スクリプト:build_index3.py
RAGの構築と問い合わせ処理を行うPythonスクリプトです。
import glob
from langchain_community.vectorstores import FAISS
from langchain_ollama import OllamaEmbeddings, OllamaLLM
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.document_loaders import TextLoader
from langchain.chains import RetrievalQA
# ---------- 設定 ----------
EMBED_MODEL = "nomic-embed-text"
LLM_MODEL = "gemma3n"
DOC_PATTERN = "docs/*.md"
QUERY = "犬は?"
# ---------- ドキュメント読み込み ----------
file_paths = glob.glob(DOC_PATTERN)
all_docs = []
for path in file_paths:
loader = TextLoader(path, encoding="utf-8")
docs = loader.load()
for doc in docs:
doc.metadata["source"] = path
all_docs.extend(docs)
print(f"✅ 読み込んだファイル数: {len(file_paths)}")
print(f"✅ ドキュメント数: {len(all_docs)}")
# ---------- 分割処理 ----------
splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
split_docs = splitter.split_documents(all_docs)
print(f"✅ チャンク数: {len(split_docs)}")
# ---------- ベクトルストア作成 ----------
embedder = OllamaEmbeddings(model=EMBED_MODEL)
vectorstore = FAISS.from_documents(split_docs, embedder)
# ---------- RetrievalQA ----------
retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
llm = OllamaLLM(model=LLM_MODEL)
rag_chain = RetrievalQA.from_chain_type(
llm=llm,
retriever=retriever,
chain_type="stuff",
return_source_documents=True
)
# ---------- 問い合わせ ----------
print(f"\n❓ 質問: {QUERY}")
result = rag_chain.invoke({"query": QUERY})
# ---------- 出力 ----------
print("\n🧠 回答:\n", result["result"])
print("\n📚 参照ソース:")
for doc in result["source_documents"]:
print(f"- {doc.metadata.get('source', '不明')}")
print(doc.page_content[:100].replace("\n", " "), "\n---")
6. 実行
python3.13 build_index3.py
7. 実行結果(例)
✅ 読み込んだファイル数: 1
✅ ドキュメント数: 1
✅ チャンク数: 1
❓ 質問: 犬は?
🧠 回答:
犬ならワン
📚 参照ソース:
- docs/text.md
猫はニャー 犬ならワン 象はパオーン ライオンガオーッ 人間だもんあかさたなはやまらわおん
---