問題の概要:ローカルRAG構築時の典型的なエラーと課題
ローカル環境でRetrieval-Augmented Generation(RAG)システムを構築する際、多くの開発者が以下のような問題に直面します。
- Ollamaサーバーが起動しない、またはモデルのダウンロードに失敗する
- ChromaDBの永続化ストレージへのアクセス権限エラー
- LangChainのバージョン互換性問題によるインポートエラー
- ドキュメントの埋め込み生成時のメモリ不足エラー
- 検索結果とLLMの応答が無関係になる「コンテキスト喪失」問題
具体的なエラーメッセージ例:
Error: Could not connect to Ollama server. Is it running?
PermissionError: [Errno 13] Permission denied: './chroma_db'
ImportError: cannot import name 'RecursiveCharacterTextSplitter' from 'langchain.text_splitter'
RuntimeError: CUDA out of memory. Tried to allocate 2.00 GiB
原因の解説:なぜこれらの問題が発生するのか
1. 環境設定の不備
Ollamaはデフォルトでlocalhost:11434でサービスを提供しますが、ファイアウォール設定やポート競合により接続できないことがあります。また、モデルファイルのダウンロードには安定したインターネット接続と十分なディスク容量(7Bパラメータモデルで約4GB)が必要です。
2. 依存関係のバージョン競合
LangChainは開発が活発でAPIが頻繁に変更されます。特に2023年後半から2024年にかけて大きな破壊的変更があり、古いチュートリアルのコードが動作しなくなることがよくあります。
3. リソース制限
ローカル環境でのLLM実行は、CPU/GPUメモリ、RAM、ストレージ速度に大きく依存します。特に埋め込みモデルの実行と大規模ドキュメントの処理では、リソース不足が顕著になります。
4. ベクトル検索の精度問題
適切なテキスト分割(chunking)戦略や埋め込みモデルの選択を誤ると、検索された文脈が質問と無関係になり、LLMが正確な回答を生成できません。
解決方法:ステップバイステップ構築ガイド
ステップ1:環境構築と依存関係のインストール
まず、Python仮想環境を作成し、適切なバージョンのパッケージをインストールします。
# 仮想環境の作成と有効化
python -m venv rag_env
source rag_env/bin/activate # Windows: rag_envScriptsactivate
# 必要なパッケージのインストール(2024年現在の安定版)
pip install langchain==0.1.0
pip install langchain-community==0.0.10
pip install chromadb==0.4.22
pip install sentence-transformers==2.2.2
pip install pypdf==3.17.4 # PDF処理用
pip install ollama==0.1.9
ステップ2:Ollamaのセットアップとモデルダウンロード
Ollamaをインストールし、LLMモデルを取得します。接続エラーが発生した場合のトラブルシューティングも含みます。
# Ollamaのインストール(macOS/Linux)
curl -fsSL https://ollama.ai/install.sh | sh
# Windowsの場合は公式サイトからインストーラーをダウンロード
# Ollamaサービスの起動と確認
ollama serve &
# 別ターミナルで以下を実行
ollama pull llama3.2:3b # 軽量モデル(ローカル環境向け)
# よくあるエラーと解決策:
# エラー "Error: could not connect to Ollama server"
# 解決策: サービスが起動しているか確認
ps aux | grep ollama
# または明示的にポートを指定
OLLAMA_HOST=127.0.0.1:11434 ollama serve
ステップ3:ChromaDBの設定と永続化ストレージ
パーミッションエラーを避けるため、適切なディレクトリ構造でChromaDBを初期化します。
import os
from chromadb.config import Settings
import chromadb
# 永続化ディレクトリの作成(パーミッション問題を回避)
persist_directory = "./chroma_db_rag"
os.makedirs(persist_directory, exist_ok=True)
# ChromaDBクライアントの設定
chroma_settings = Settings(
chroma_db_impl="duckdb+parquet",
persist_directory=persist_directory,
anonymized_telemetry=False # テレメトリを無効化
)
# クライアントの作成
client = chromadb.Client(chroma_settings)
# コレクションの作成(存在しない場合)
collection_name = "rag_documents"
try:
collection = client.get_collection(collection_name)
print(f"既存のコレクション '{collection_name}' を読み込みました")
except:
collection = client.create_collection(
name=collection_name,
metadata={"hnsw:space": "cosine"} # コサイン類似度を使用
)
print(f"新規コレクション '{collection_name}' を作成しました")
ステップ4:ドキュメント処理パイプラインの構築
適切なテキスト分割と埋め込み生成を行い、メモリ不足を回避します。
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.document_loaders import PyPDFLoader
from langchain_community.embeddings import HuggingFaceEmbeddings
# 1. ドキュメントの読み込み
loader = PyPDFLoader("./sample_document.pdf")
documents = loader.load()
# 2. テキスト分割(チャンキング)
# 小さなチャンクでメモリ使用量を制御
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 各チャンクの文字数
chunk_overlap=50, # チャンク間のオーバーラップ
length_function=len,
separators=["nn", "n", "。", "、", " ", ""]
)
chunks = text_splitter.split_documents(documents)
print(f"ドキュメントを {len(chunks)} 個のチャンクに分割しました")
# 3. 軽量な埋め込みモデルの使用(メモリ節約)
embedding_model = HuggingFaceEmbeddings(
model_name="intfloat/multilingual-e5-small", # 多言語対応軽量モデル
model_kwargs={'device': 'cpu'}, # GPUメモリ不足の場合はCPUを使用
encode_kwargs={'normalize_embeddings': True}
)
# 4. ベクトルストアへの保存
from langchain_community.vectorstores import Chroma
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embedding_model,
client=client,
collection_name=collection_name,
persist_directory=persist_directory
)
# 永続化
vectorstore.persist()
print("ベクトルストアを永続化しました")
ステップ5:RAGチェーンの実装と最適化
LangChainの最新APIを使用して、効率的なRAGパイプラインを構築します。
from langchain_community.llms import Ollama
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 1. Ollama LLMの初期化
llm = Ollama(
model="llama3.2:3b",
temperature=0.1, # 創造性を低く設定(事実ベースの回答向け)
num_predict=512, # 最大トークン数
base_url="http://localhost:11434" # 明示的にURLを指定
)
# 2. カスタムプロンプトテンプレート(日本語最適化)
prompt_template = """以下の文脈に基づいて質問に答えてください。
文脈から答えがわからない場合は、「わかりません」と答えてください。
文脈:
{context}
質問: {question}
回答(日本語で):"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
# 3. リトリーバーの設定(類似度スコアでフィルタリング)
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={
"k": 3, # 上位3件を取得
"score_threshold": 0.3 # 類似度スコアの閾値
}
)
# 4. RAGチェーンの構築
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # シンプルな実装
retriever=retriever,
chain_type_kwargs={"prompt": PROMPT},
return_source_documents=True # ソースドキュメントも返す
)
# 5. クエリの実行例
query = "このドキュメントの主なテーマは何ですか?"
result = qa_chain({"query": query})
print(f"質問: {query}")
print(f"回答: {result['result']}")
print(f"参考にしたソース数: {len(result['source_documents'])}")
コード例・コマンド例:完全な実装スクリプト
#!/usr/bin/env python3
"""
完全なRAGシステム実装スクリプト
Ollama + ChromaDB + LangChain
"""
import os
import sys
from pathlib import Path
class LocalRAGSystem:
def __init__(self, model_name="llama3.2:3b", persist_dir="./chroma_db_rag"):
self.model_name = model_name
self.persist_dir = persist_dir
self.setup_environment()
def setup_environment(self):
"""環境変数とディレクトリの設定"""
os.environ["TOKENIZERS_PARALLELISM"] = "false" # トークナイザーの警告を抑制
Path(self.persist_dir).mkdir(exist_ok=True)
def process_documents(self, doc_paths):
"""ドキュメントを処理してベクトルストアを作成"""
from langchain_community.document_loaders import (
PyPDFLoader, TextLoader, DirectoryLoader
)
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.vectorstores import Chroma
# ドキュメントの読み込み
documents = []
for path in doc_paths:
if path.endswith('.pdf'):
loader = PyPDFLoader(path)
elif path.endswith('.txt'):
loader = TextLoader(path, encoding='utf-8')
else:
continue
documents.extend(loader.load())
# テキスト分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=100,
separators=["nn", "n", "。", ".", "?", "!", " ", ""]
)
chunks = text_splitter.split_documents(documents)
# 埋め込みモデル
embeddings = HuggingFaceEmbeddings(
model_name="intfloat/multilingual-e5-small",
model_kwargs={'device': 'cpu'}
)
# ベクトルストアの作成
self.vectorstore = Chroma.from_documents(
chunks,
embeddings,
persist_directory=self.persist_dir,
collection_name="rag_docs"
)
def query(self, question, k_results=3):
"""質問に対して回答を生成"""
from langchain_community.llms import Ollama
from langchain.chains import RetrievalQA
# リトリーバーの取得
retriever = self.vectorstore.as_retriever(
search_kwargs={"k": k_results}
)
# LLMの初期化
llm = Ollama(
model=self.model_name,
temperature=0.1,
base_url="http://localhost:11434"
)
# QAチェーン
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
return qa_chain({"query": question})
# 使用例
if __name__ == "__main__":
# RAGシステムの初期化
rag = LocalRAGSystem()
# ドキュメントの処理(PDFやテキストファイル)
documents = ["./data/sample1.pdf", "./data/sample2.txt"]
rag.process_documents(documents)
# 質問の実行
while True:
user_query = input("n質問を入力してください(終了するには 'exit' と入力): ")
if user_query.lower() == 'exit':
break
result = rag.query(user_query)
print(f"n回答: {result['result']}")
print(f"n参照されたソース:")
for i, doc in enumerate(result['source_documents'][:2]):
print(f"{i+1}. {doc.page_content[:200]}...")
まとめ・補足情報
パフォーマンス最適化のヒント
1. チャンクサイズの調整: 質問応答タスクでは500-1000文字、要約タスクでは1000-2000文字が目安です。
2. モデルの選択: ローカル環境では、Llama 3.2 3B、Phi-3-mini、Gemma 2Bなどの軽量モデルが推奨されます。
3. メモリ管理: 大きなドキュメントを処理する場合は、バッチ処理を実装し、定期的にガベージコレクションを実行します。
よくあるエラーと即時解決策
# エラー1: "OSError: [Errno 98] Address already in use"
# 解決: Ollamaのポートが競合している
sudo lsof -i :11434
sudo kill -9 [PID]
ollama serve
# エラー2: "ValueError: Expected embedding dimension 384, got 768"
# 解決: 埋め込みモデルとベクトルDBの次元が不一致
# 新しいコレクションを作成するか、同じ埋め込みモデルを使用
# エラー3: "RuntimeError: generator raised StopIteration"
# 解決: Python 3.7+の互換性問題。async/awaitの使用を確認
次のステップ:高度な機能の追加
基本的なRAGシステムが動作したら、以下の機能を追加することで精度を向上できます:
- 再ランキング: CohereやBGEランカーを使用して検索結果を再評価
- マルチホップ検索: 複数の関連ドキュメントを段階的に検索
- HyDE(Hypothetical Document Embeddings): 仮想的な回答を生成して検索クエリを改善
- エージェント機能: ツール使用や複数ステップの推論を可能に
ローカルRAGシステムの構築は、初期設定に課題がありますが、一度構築すればデータプライバシーを保ちつつ、カスタマイズ性の高いAIアシスタントを実現できます。本ガイドで紹介したトラブルシューティング手法を参考に、安定したシステム構築を進めてください。