【LLM全般】ローカル環境でOllama+ChromaDB+LangChainを使ったRAGシステム構築手順とよくあるエラー解決法

ローカルLLMとRAG構築における問題の概要

生成AIの実践的なアプリケーションとして注目されるRAG(Retrieval-Augmented Generation)を、ローカル環境でOllama、ChromaDB、LangChainを使って構築しようとする際、開発者、特に初心者から中級者の方は様々なエラーや課題に直面します。これらは、環境構築の複雑さ、ライブラリ間のバージョン非互換性、メモリ不足、そして設定ミスに起因することがほとんどです。具体的には、「モデルのダウンロード失敗」、「ベクトルデータベースとの接続エラー」、「LangChainのDeprecationWarning」、「CUDA/GPU関連のエラー」などが頻発します。本記事では、これらの問題を未然に防ぎ、スムーズにRAGシステムを構築するための実践的な手順とトラブルシューティングを詳細に解説します。

RAG構築失敗の主な原因

ローカルRAG構築の失敗は、主に以下の4つのカテゴリに分類されます。

1. 環境構築と依存関係の不整合

Ollama、ChromaDB、LangChainはそれぞれ独立して開発が進んでおり、使用するバージョンによって互換性が失われることがあります。特にLangChainは更新が頻繁で、わずか数ヶ月でAPIが非推奨(deprecated)となることが珍しくありません。古いチュートリアルに沿ってコードを書くと、ImportErrorDeprecationWarning が発生します。

2. リソース(メモリ/ストレージ)不足

Ollamaで7Bパラメータ規模のLLMを動かすには、少なくとも8GB以上のRAMが必要です。さらに、文書を embedding 化してChromaDBに保存するプロセスでは、追加のメモリを消費します。ストレージについても、モデルファイル自体が数GBのサイズとなるため、空き容量不足がダウンロードエラーの原因となります。

3. モデルやEmbedding関数の不適切な選択

Ollamaで利用可能なモデルと、LangChainでChromaDBと連携させるための embedding モデルは別物です。ここを混同すると、「入力されたテキストをベクトル化できない」というエラーが発生します。また、英語用に最適化された embedding モデルで日本語テキストを処理すると、検索精度が著しく低下します。

4. ネットワークとプロキシ設定

Ollamaの初期モデルダウンロードや、Hugging Faceからの embedding モデルの取得時、企業内ネットワークなどではプロキシ設定が阻害要因となることがあります。エラーメッセージが接続タイムアウトを示す場合、この可能性が高いです。

ステップバイステップ解決手順

以下に、Ubuntu 20.04/22.04 または WSL2 環境を想定した、堅牢な構築手順を示します。各ステップで確認すべきポイントを明記します。

ステップ1: 基盤環境の準備

まず、Python仮想環境を作成し、基本的なツールをインストールします。これにより、プロジェクトごとの依存関係を分離できます。

# Python仮想環境の作成と有効化
python3 -m venv rag_venv
source rag_venv/bin/activate  # Windows: rag_venvScriptsactivate

# 必須ツールのインストール(Ollama)
# Ubuntu/WSLの場合
curl -fsSL https://ollama.com/install.sh | sh

# インストール確認
ollama --version
# 期待する出力: "ollama version x.x.x"

トラブルシューティング: curl: (7) Failed to connect to ollama.com port 443 というエラーが出る場合は、プロキシ環境下にいる可能性があります。シェルのプロキシ設定を確認してください。

ステップ2: LLMモデルのダウンロードと起動 (Ollama)

軽量で性能バランスの取れたモデル(例: Llama 3.1 8B)をプルします。まずはCPUモードで動作確認します。

# モデルのダウンロード(初回実行時)
ollama pull llama3.1:8b

# モデルが正常にダウンロードされたかリストで確認
ollama list

# モデルをサーバーモードで起動(別ターミナルで実行推奨)
ollama serve
# 別のターミナルで対話的に動作確認
ollama run llama3.1:8b

トラブルシューティング: Error: pull model manifest: ... no such host はネットワーク問題。 Error: insufficient RAM available はメモリ不足です。後者の場合は、より小さいモデル(例: llama3.2:3bmistral:7b-instruct)を試してください。

ステップ3: Pythonライブラリのインストールとバージョン固定

互換性が保証されるバージョンの組み合わせをインストールします。以下は2024年後半時点で安定した組み合わせです。

pip install --upgrade pip
pip install langchain==0.1.10
pip install langchain-community==0.0.20
pip install chromadb==0.4.22
pip install sentence-transformers==2.2.2
pip install pypdf  # PDF読み込み用
pip install langchain-chroma  # LangChainとChromaDBの統合用

重要: langchain のバージョンによっては、Chroma のインポート方法が from langchain.vectorstores import Chroma から from langchain_chroma import Chroma に変わっています。本記事では後者を使用します。

ステップ4: 日本語対応Embeddingモデルの準備

日本語テキストのベクトル化には、多言語対応または日本語特化のモデルが必須です。intfloat/multilingual-e5-large は強力ですが重いため、ローカルでは sentence-transformers モデルが現実的です。

from langchain.embeddings import HuggingFaceEmbeddings

# 軽量で日本語性能が比較的良好なモデル
model_name = "intfloat/multilingual-e5-small" # または "cl-nagoya/sup-simcse-ja-large"
embeddings = HuggingFaceEmbeddings(
    model_name=model_name,
    model_kwargs={'device': 'cpu'}, # GPUがあれば 'cuda'
    encode_kwargs={'normalize_embeddings': True}
)

# テスト
test_vector = embeddings.embed_query("これはテスト文章です。")
print(f"ベクトル次元数: {len(test_vector)}")

エラー例と解決: OSError: Unable to load weights from pytorch checkpoint file は、モデルファイルのダウンロード失敗。インターネット接続を確認し、場合によっては手動でHugging Faceからダウンロードする必要があります。

ステップ5: 文書の読み込み、分割、ベクトルDBへの格納

PDFやテキストファイルを読み込み、適切なサイズに分割してChromaDBに保存します。

from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_chroma import Chroma

# 1. 文書の読み込み(例: sample.pdf)
loader = PyPDFLoader("./sample.pdf") # ファイルパスを指定
documents = loader.load()

# 2. 文書の分割(チャンキング)
text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,  # 1チャンクの文字数(目安)
    chunk_overlap=50, # 重複文字数
    separators=["nn", "n", "。", "、", " ", ""] # 日本語に配慮
)
split_docs = text_splitter.split_documents(documents)
print(f"元のドキュメント数: {len(documents)}, 分割後チャンク数: {len(split_docs)}")

# 3. ChromaDBへの永続化保存
persist_directory = "./chroma_db"
vectordb = Chroma.from_documents(
    documents=split_docs,
    embedding=embeddings,
    persist_directory=persist_directory
)
vectordb.persist() # ディスクに保存
print(f"ベクトルDBが {persist_directory} に保存されました。")

ステップ6: Ollama LLMと連携したRAGチェーンの構築

保存したベクトルDBから類似文書を検索し、その結果をコンテキストとしてLLMに渡すチェーンを構築します。

from langchain.llms import Ollama
from langchain.chains import RetrievalQA
from langchain.callbacks.manager import CallbackManager
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler

# 1. 保存済みのベクトルDBを読み込み
vectordb = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings
)
retriever = vectordb.as_retriever(search_kwargs={"k": 3}) # 上位3件を取得

# 2. Ollama LLMの設定
llm = Ollama(
    model="llama3.1:8b",
    callback_manager=CallbackManager([StreamingStdOutCallbackHandler()]),
    temperature=0.2 # 創造性を低めに設定
)

# 3. RAGチェーンの作成
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff", # 単純にコンテキストを連結
    retriever=retriever,
    return_source_documents=True, # ソース文書も返す
    verbose=True # 処理の詳細を表示
)

# 4. 質問の実行
query = "ドキュメントで説明されている主要な技術は何ですか?"
result = qa_chain({"query": query})
print("nn回答:", result["result"])
print("n参照されたソース文書:")
for doc in result["source_documents"][:2]: # 上位2件を表示
    print(f"- {doc.page_content[:200]}...")

よくあるエラー: ConnectionError: HTTPConnectionPool(host='localhost', port=11434): Max retries exceeded は、ollama serve が起動していないか、別のポートを使用しています。Ollamaサーバーの起動状態を確認してください。

まとめと補足情報

Ollama、ChromaDB、LangChainを用いたローカルRAGシステムの構築は、環境の初期設定とライブラリのバージョン管理が成功の鍵を握ります。本記事で紹介した手順とバージョン組み合わせは、現在の安定性を考慮したものです。構築後は、以下の点をさらに調整することで、システムの性能を向上させることができます。

  • チャンキング戦略の最適化: chunk_sizechunk_overlap は、文書の性質(技術文書、会話録、ニュース記事など)に応じて調整が必要です。小さすぎると文脈が失われ、大きすぎると検索精度とLLMの処理負荷が高まります。
  • 高度な検索手法の導入: 単純な類似度検索(similarity_search)に加え、MMR (Max Marginal Relevance) を用いることで、検索結果の多様性を保ちつつ関連性を高められます。
  • プロンプトの改良: RetrievalQA のデフォルトプロンプトをカスタマイズし、「答えがわからない場合は『わかりません』と答える」などの指示を加えることで、LLMの幻覚(hallucination)を抑制できます。

ローカル環境で完全に動作するRAGシステムを構築することは、データの機密性を保ちながら、特定の知識に基づいた高精度なAIアシスタントを開発するための強力な第一歩となります。本記事がその実現の助けとなれば幸いです。

この記事は役に立ちましたか?