💡
onnxruntime地獄からの脱出!LangChain × Ollama × ChromaでローカルRAG環境を構築するまでの戦い
はじめに
こんにちは。今回はローカル環境で LangChain + Ollama + Chroma を使って RAG(Retrieval-Augmented Generation)を構築しようとしたら、onnxruntime
との終わりなき戦いに巻き込まれた話を記録します。
LangChain + Ollama の構成は非常に魅力的なのですが、内部で勝手に onnxruntime
を呼び出す chromadb
の仕様に悩まされました。
やりたかったこと
- LangChain を使って Markdown ドキュメントをベクトル化
- Ollama で埋め込み生成(
nomic-embed-text
モデル) - Chroma に保存して質問検索できるようにする
- Dockerやクラウドを使わず、ローカル環境で完結
環境構成
- OS: Windows 11
- Python: 3.11
- LangChain:
langchain
,langchain-community
,langchain-ollama
- Ollama: ローカルで動作
- chromadb:
0.4.x
- Embedding model:
nomic-embed-text
最初のコードと初回エラー
from langchain.vectorstores import Chroma
from langchain.embeddings import OllamaEmbeddings
from langchain.document_loaders import DirectoryLoader, TextLoader
...
embedding = OllamaEmbeddings(model="nomic-embed-text")
vectordb = Chroma.from_documents(docs, embedding=embedding, persist_directory="chroma_db")
すると最初にぶち当たったのがこちら:
ImportError: DLL load failed while importing onnxruntime_pybind11_state
onnxruntime
なんて使ってないのに…なぜ…
onnxruntimeとの戦い(第1ステージ)
chromadb
は DefaultEmbeddingFunction()
を import 時点で勝手に呼ぶ仕様になっており、
たとえ使わなくても onnxruntime
が必要になる地獄構成でした。
空ドキュメントでもonnxruntime呼ばれる(第2ステージ)
docs = [] # 空でも
Chroma.from_documents(docs, ...) # ← ここで呼ばれる
→ 空のドキュメントでも内部で DefaultEmbeddingFunction()
が呼ばれ、onnxruntime
が爆発!
CHROMA_NO_DEFAULT_EMBEDDINGS
が効かない罠(第3ステージ)
環境変数 CHROMA_NO_DEFAULT_EMBEDDINGS=True
を設定しても効果なし!
なぜなら:
-
import chromadb
のタイミングで環境変数が設定されていないと無効になる - →
os.environ
で最上部に書く必要がある
真の解決策と動作確認
retriever.py
の最上部で封じる
✅ 1. import os
os.environ["CHROMA_NO_DEFAULT_EMBEDDINGS"] = "True"
✅ 2. UTF-8 読み込み指定
from functools import partial
loader = DirectoryLoader(..., loader_cls=partial(TextLoader, encoding="utf-8"))
✅ 3. モデルをPull
ollama pull nomic-embed-text
✅ 4. 完全勝利のログ
✅ Vector store created in 3.90 seconds
まとめ:学びと教訓
学び | 対策 |
---|---|
chromadb は import だけで onnxruntime を呼ぶことがある |
CHROMA_NO_DEFAULT_EMBEDDINGS=True を os.environ で最上部に |
chunks=[] でも DefaultEmbeddingFunction() が実行される |
早期 return で防御 |
ollama のモデルは明示的に pull が必要 |
ollama pull nomic-embed-text |
TextLoader のエンコーディングに注意 |
encoding='utf-8' を明示すること |
最終的な動作コード(コピペOK)
import os
os.environ["CHROMA_NO_DEFAULT_EMBEDDINGS"] = "True"
from langchain_community.vectorstores import Chroma
from langchain_ollama import OllamaEmbeddings
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from functools import partial
import time
CHROMA_PATH = "chroma_db"
DOCUMENTS_PATH = "documents"
embedding = OllamaEmbeddings(model="nomic-embed-text")
def create_vector_store():
loader = DirectoryLoader(DOCUMENTS_PATH, glob="**/*.md", loader_cls=partial(TextLoader, encoding="utf-8"))
docs = loader.load()
if not docs:
return
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)
if not chunks:
return
vectordb = Chroma.from_documents(chunks, embedding=embedding, persist_directory=CHROMA_PATH)
print("✅ Vector store created")
def get_relevant_docs(question: str, k=3):
vectordb = Chroma(persist_directory=CHROMA_PATH, embedding_function=embedding)
docs = vectordb.similarity_search(question, k=k)
return [doc.page_content for doc in docs]
おわりに
LangChain は便利ですが、ちょっとした仕様の違いでハマりポイントも多いです。
この記事が同じように苦しむ人の助けになれば幸いです🙏
💬 コメント歓迎!
質問・補足・改善案などあればぜひコメント欄で教えてください!
Discussion