LangChain×ChromaDB×FAISSエラー解決|ImportError・メモリ不足・デプロイ失敗の原因と対策
LangChain・ChromaDB・FAISSで発生しやすいエラーと対処策
検索拡張生成(RAG)システムの開発では、LangChain・ChromaDB・FAISSなどのライブラリを組み合わせて使います。これらのツールは便利である一方で、バージョン差異やAPI変更によるエラーが頻発します。本記事では、実開発で遭遇しやすい4つの典型的な問題と解決方法をまとめました。
1. RetrievalQAのImportError:バージョン互換性の問題
症状
ImportError: cannot import name 'RetrievalQA' from 'langchain.chains' in Python project
このエラーは、LangChainのバージョンアップデートでRetrievalQAが廃止されたり、パッケージ構成が変わったときに発生します。
RetrievalQAが廃止された理由
LangChainの0.1バージョン以降、RetrievalQAクラスは削除されました。これは以前のバージョンで広く使われていた質問応答システムですが、新しいアーキテクチャ(LCEL)の導入に伴い、より柔軟で明確なパイプライン構築の方法へシフトしました。
廃止された理由は、LangChainが「魔法のようなラッパークラス」よりも「明示的で組み立て可能なコンポーネント」を重視する設計方針に変わったためです。これにより開発者はRAGパイプラインの各ステップを詳細に制御でき、デバッグや機能追加が容易になりました。
想定される原因
LangChainは頻繁にAPIを変更しており、古いドキュメントやサンプルコードの多くは現在の最新バージョンに対応していません。特に2024年以降のバージョンでは、チェーン周りの大規模なリファクタリングが行われています。コードが古いバージョンを想定しているか、依存関係の競合が生じている可能性があります。
前提環境・必要なもの
- Python 3.8以上
- LangChain 0.1.0以降(2024年時点での最新版を推奨)
- OpenAI APIキー(またはほかのLLM)
- ベクトル化機能(OpenAI Embeddings、Hugging Face Embeddings など)
- ベクトルデータベース(FAISS、Chroma、Weaviateなど)
以下のコマンドでLangChainと依存パッケージをインストールしてください:
pip install langchain langchain-openai faiss-cpu python-dotenv
注記: langchain-openaiは独立したパッケージとなりました。OpenAI を使う場合は別途インストールが必要です。
切り分け手順
-
インストール済みのLangChainバージョンを確認する
pip show langchain -
利用可能なクラスを確認する
import langchain.chains print(dir(langchain.chains)) -
別の場所からインポートされていないか確認
# LangChainの新バージョンでの正しいインポート方法を試す from langchain_core.runnables import RunnablePassthrough from langchain.chains import create_retrieval_chain
対処方法(優先度順)
1. LCELによる新しい実装に移行する(推奨)
最新のLangChainでは、LangChain Expression Language(LCEL)を使ったパイプライン構築が標準です。以下が基本的なパターンです:
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 1. 文書をロードしてベクトル化
loader = TextLoader("document.txt")
docs = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
splits = text_splitter.split_documents(docs)
embeddings = OpenAIEmbeddings()
vectorstore = FAISS.from_documents(splits, embeddings)
# 2. Retrieverを作成
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
# 3. ドキュメントのフォーマット関数
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
# 4. プロンプトテンプレート定義
template = """以下の文脈を踏まえて質問に答えてください:
{context}
質問: {question}
答え:"""
prompt = ChatPromptTemplate.from_template(template)
# 5. LLMを定義
llm = ChatOpenAI(model="gpt-4", temperature=0)
# 6. チェーン構築(LCEL)
chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 7. 実行
result = chain.invoke("質問内容ここに入力")
print(result)
重要なポイントは以下の通りです:
retriever = vectorstore.as_retriever()でRetrieverオブジェクトを作成|パイプ演算子でコンポーネントを連結RunnablePassthrough()で質問を次のステップに受け渡す
2. ソースドキュメントを含めた実装
旧バージョンのRetrievalQAではreturn_source_documents=Trueオプションで参考文献を返していました。新しい方式では明示的に指定します:
from langchain_core.runnables import RunnableParallel
# チェーン構築(出力にソースドキュメント含む)
rag_chain = RunnableParallel(
{"response": chain, "source_documents": retriever}
)
result = rag_chain.invoke("質問内容")
print("回答:", result["response"])
print("参考文献:")
for doc in result["source_documents"]:
print(f" - {doc.metadata.get('source', 'Unknown')}: {doc.page_content[:100]}...")
3. create_retrieval_chainを使う方法
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
prompt = ChatPromptTemplate.from_template("""
質問に答えてください:
{context}
質問: {input}
""")
document_chain = create_stuff_documents_chain(llm, prompt)
retrieval_chain = create_retrieval_chain(retriever, document_chain)
4. 依存関係を明確にする
requirements.txtで明示的にバージョンを指定してください。複数のパッケージが異なるLangChainバージョンに依存していることが原因になることがあります:
langchain==0.1.0
langchain-core>=0.1.0
langchain-openai>=0.0.1
モジュールのインポート先が変わっている点にも注意してください:
| 旧バージョン | 新バージョン |
|---|---|
from langchain import OpenAI | from langchain_openai import OpenAI |
from langchain.embeddings import OpenAIEmbeddings | from langchain_openai import OpenAIEmbeddings |
from langchain.chains import RetrievalQA | LCELで直接構築(インポート不要) |
5. 仮想環境を再構築する
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install --upgrade langchain langchain-openai
旧コードの移行チェックリスト
既存のRetrievalQAを使ったコードを新しい方式に移行する場合、以下を確認してください:
- LangChainバージョンが0.1以降であるか確認
-
from langchain.chains import RetrievalQAを削除 - 各モジュールのインポート先を新体系に変更
- Retriever、プロンプト、LLMを分離して定義
- LCELのパイプ演算子で連結
- テンプレート変数(
{context}など)をプロンプトに明示 - ソースドキュメントが必要な場合は
RunnableParallelを使用 - チャット履歴が必要な場合はメモリコンポーネントを追加
それでも解決しないとき
- LangChainのGitHubリポジトリでIssueを検索し、同じエラーが報告されていないか確認してください
- ドキュメントのバージョンセレクタで、インストール済みバージョンに合わせた説明を読む
- 最後の手段として、古いバージョンをピン留めするのではなく、コードを新しいAPIに合わせることを強くお勧めします
2. RAGでのチャット履歴の不具合:メモリ管理とコンテキスト
症状
会話をしているのに、前のメッセージの内容を認識していない
新しい質問をするたびに、チャット履歴が失われたように見える
RAGシステムでは、会話を続けるのに必要なメモリ(チャット履歴)の管理が重要です。
想定される原因
RetrievalQAの基本形は単一の質問に対する回答を返すだけで、デフォルトではチャット履歴を保持しません。会話の文脈を保つには、明示的にメモリコンポーネントを追加する必要があります。また、ドキュメント検索と会話履歴の組み合わせで、プロンプトの書き方に工夫が必要になります。
切り分け手順
-
現在のメモリ設定を確認する
# ConversationalRetrievalChainを使っているか? # 明示的なメモリオブジェクトは設定されているか? print(chain.memory) # Noneなら未設定 -
プロンプトテンプレートの内容を確認する
print(chain.llm_chain.prompt.template) # chat_historyや{history}が含まれているか? -
テスト実行してログを見る
# 2つの会話を順番に実行し、2番目の回答に1番目の文脈が反映されているか確認 result1 = chain({"question": "私の名前はTaroです"}) result2 = chain({"question": "私の名前は?"}) print(result2["answer"]) # "Taro"と回答するか?
対処方法(優先度順)
1. LCELでメモリを組み込む(推奨)
LCELを使ったチェーンに会話履歴を追加する方法です:
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.memory import ConversationBufferMemory
from langchain_core.runnables import RunnablePassthrough
# メモリ初期化
memory = ConversationBufferMemory(memory_key="history", return_messages=True)
# 会話履歴を含むプロンプト
template = """あなたは親切なアシスタントです。以下の会話履歴と文脈を踏まえて、質問に答えてください。
文脈:
{context}
会話履歴:
{history}
質問: {question}
答え:"""
prompt = ChatPromptTemplate.from_template(template)
# チェーン構築
chain = (
{
"context": retriever | format_docs,
"history": lambda x: memory.buffer,
"question": RunnablePassthrough()
}
| prompt
| llm
| StrOutputParser()
)
# 実行とメモリ更新
def run_with_memory(user_input):
response = chain.invoke(user_input)
memory.save_context({"input": user_input}, {"output": response})
return response
# 使用例
print(run_with_memory("LangChainの概要は?"))
print(run_with_memory("それはどのように実装されていますか?"))
2. ConversationalRetrievalChainを使う
より簡便にチャット履歴を扱う場合、ConversationalRetrievalChainも利用できます:
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
qa_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memory,
verbose=True
)
# 複数回の会話が自動的に履歴として保持される
result1 = qa_chain({"question": "LangChainの概要は?"})
result2 = qa_chain({"question": "それはどのように実装されていますか?"})
3. カスタムプロンプトを組み込む
ConversationalRetrievalChainにカスタムプロンプトを指定して、より詳細な指示を与えることができます:
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
condense_question_prompt = ChatPromptTemplate.from_messages([
("system", "与えられた会話履歴とフォローアップの質問に基づいて、元のドキュメント検索クエリを作成してください。"),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{question}")
])
qa_prompt = ChatPromptTemplate.from_messages([
("system", "以下の文脈に基づいて質問に回答してください:\n{context}"),
MessagesPlaceholder(variable_name="chat_history"),
("human", "{question}")
])
qa_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
condense_question_prompt=condense_question_prompt,
combine_docs_chain_kwargs={"prompt": qa_prompt},
memory=memory
)
4. メモリタイプを選択する
用途に応じてメモリの種類を選択してください:
ConversationBufferMemory:全履歴を保持(短い会話向け)ConversationSummaryMemory:履歴を要約して保持(長い会話向け)ConversationBufferWindowMemory:最新N件を保持(リソース制約下向け)
from langchain.memory import ConversationBufferWindowMemory
memory = ConversationBufferWindowMemory(
k=5, # 最新5件を保持
memory_key="chat_history",
return_messages=True
)
それでも解決しないとき
- ドキュメント検索に使うクエリが、前のターンの文脈に基づいて正しく作成されているか確認する(
condense_question_promptをデバッグする) - LLMの応答が実は正しいのに、RAGシステムの外部で履歴を失っていないか確認する(例:Webアプリケーションのセッション管理)
- メモリサイズが制限されていないか、メモリオブジェクトの設定を見直す
3. ChromaDBの本番デプロイ:永続化とスケーリング
症状
開発環境ではうまく動いていたが、本番環境でベクトルデータベースが起動しない
複数のプロセスからアクセスするとデータが競合する
メモリ使用量が多すぎて、本番サーバーでスケールしない
ChromaDBはデフォルトではメモリ内に動作し、プロセス終了時にデータが失われます。
想定される原因
ChromaDBの基本的な使い方は開発向けで、永続化やマルチプロセスアクセスへの対応が十分ではありません。本番環境では、ベクトルデータの永続ストレージ、複数インスタンス間でのデータ共有、メモリ効率の向上が必要になります。
切り分け手順
-
現在のChromaDB設定を確認する
import chromadb client = chromadb.Client() # メモリのみモード collection = client.get_or_create_collection("my_collection") # プロセス終了後、このデータは消える -
永続化の有無を確認する
# 永続化ディレクトリが作成されているか? import os print(os.path.exists("./chroma_data")) -
本番環境のリソース使用状況を確認する
# メモリ使用量を監視 ps aux | grep python df -h # ディスク容量
対処方法(優先度順)
1. ChromaDBを永続化モードで起動する(推奨)
ディスクに保存されることで、プロセス再起動後もデータが残ります:
import chromadb
from chromadb.config import Settings
# 永続ディレクトリを指定
settings = Settings(
chroma_db_impl="duckdb+parquet",
persist_directory="./chroma_data",
anonymized_telemetry=False
)
client = chromadb.Client(settings)
collection = client.get_or_create_collection("my_collection")
# データを追加
collection.add(
ids=["doc1", "doc2"],
embeddings=[[1.1, 2.3], [4.5, 6.9]],
documents=["This is doc1", "This is doc2"]
)
# 明示的にデータを保存(オプション)
client.persist()
2. 本番環境用の設定を分離する
開発環境と本番環境で異なる設定を使い分けます:
import os
from chromadb.config import Settings
if os.getenv("ENVIRONMENT") == "production":
settings = Settings(
chroma_db_impl="duckdb+parquet",
persist_directory="/mnt/data/chroma", # 共有ストレージ(NFS等)
anonymized_telemetry=False
)
else:
# 開発環境ではメモリ内
settings = Settings(anonymized_telemetry=False)
client = chromadb.Client(settings)
3. HTTPサーバーモードでの運用(スケーラビリティ重視)
複数のアプリケーションがChromaDBにアクセスする場合、HTTPサーバーモードで一元管理します:
# ChromaDBサーバーを起動(別プロセス)
chroma run --path /data/chroma --port 8000
# クライアント側は遠隔接続
import chromadb
client = chromadb.HttpClient(host="localhost", port=8000)
collection = client.get_or_create_collection("my_collection")
この方式により、複数のアプリケーションインスタンスが同じベクトルDBにアクセスできます。
4. ディスク容量の計画
ベクトルデータはディスク容量を消費します。本番環境では事前に必要な容量を見積もり、ストレージを確保しておきます:
# データサイズの目安:1エンベッディング(1536次元)≈ 6KB
# 100万ドキュメント = 約6GB
それでも解決しないとき
- ChromaDBの公式ドキュメントで、使用しているバージョンの永続化方法を確認する
- ファイルシステムの権限設定が正しいか確認(書き込み権限があるか)
- ディスクI/Oのボトルネックが問題なら、SSD使用やRAID構成を検討する
- 超大規模なベクトルDBが必要な場合は、Milvus・Weaviate・Pineconeなどのエンタープライズソリューションの導入を検討する
4. RetrievalQAへのメモリ追加とカスタムプロンプト
症状
RetrievalQA.from_chain_type()にメモリを追加したいが、パラメータが見当たらない
プロンプトをカスタマイズしようとしてもうまくいかない
RetrievalQAの基本的な実装では、メモリとカスタムプロンプトの追加が直感的ではありません。
想定される原因
RetrievalQA.from_chain_type()メソッドは、シンプルなQ&A用に最適化されており、複雑な設定(メモリ、カスタムプロンプト)を直接サポートしていません。その代わり、LCEL、ConversationalRetrievalChainや、より低レベルのチェーン組み立てAPIを使う必要があります。
切り分け手順
-
現在の実装を確認する
from langchain.chains import RetrievalQA qa = RetrievalQA.from_chain_type(llm=llm, retriever=retriever) # このQAオブジェクトは memory 属性を持たない -
必要な機能を整理する
- メモリが必要か?→ LCELまたは
ConversationalRetrievalChainを使う - プロンプトのカスタマイズだけか?→ LCELで直接プロンプトを組み込む
- メモリが必要か?→ LCELまたは
対処方法(優先度順)
1. LCELでメモリとプロンプトを組み合わせる(推奨)
from langchain.memory import ConversationBufferMemory
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
memory = ConversationBufferMemory(memory_key="history", return_messages=True)
system_prompt = """You are a helpful AI assistant. Answer questions based on the following context:
{context}
Conversation history:
{history}
If you don't know the answer, say so explicitly."""
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{input}")
])
chain = (
{
"context": retriever | format_docs,
"history": lambda x: memory.buffer,
"input": RunnablePassthrough()
}
| prompt
| llm
| StrOutputParser()
)
def run_with_memory(user_input):
response = chain.invoke(user_input)
memory.save_context({"input": user_input}, {"output": response})
return response
result = run_with_memory("What is LangChain?")
print(result)
2. メモリが必要ならConversationalRetrievalChainを使う
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
qa_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
retriever=retriever,
memory=memory
)
# メモリを保持しながら複数回の質問に回答
print(qa_chain({"question": "AIとは?"}))
print(qa_chain({"question": "それはどのように学習するのか?"}))
3. メモリが不要でプロンプトのみ変更したい場合
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# カスタムプロンプトを定義
prompt_template = """以下の情報を使って、質問に答えてください。
詳細で正確な回答を心がけてください。
文脈:
{context}
質問:{question}
回答:"""
prompt = ChatPromptTemplate.from_template(prompt_template)
chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
result = chain.invoke("LangChainの基本的な使い方は?")
print(result)
4. さらに複雑なカスタマイズが必要な場合は、低レベルAPIで組み立てる
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_core.prompts import ChatPromptTemplate
system_prompt = """You are a helpful AI assistant. Answer questions based on the following context:
{context}
If you don't know the answer, say so explicitly."""
prompt = ChatPromptTemplate.from_messages([
("system", system_prompt),
("human", "{input}")
])
document_chain = create_stuff_documents_chain(llm, prompt)
retrieval_chain = create_retrieval_chain(retriever, document_chain)
result = retrieval_chain.invoke({"input": "What is LangChain?"})
print(result["answer"])
応用:Hugging Face Embeddingsを使ったローカル実装
OpenAI APIを使わずにローカルで実行したい場合、Hugging Face のモデルを使えます:
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import FAISS
from langchain_community.llms import Ollama
# ローカルEmbeddings
embeddings = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-small")
# ローカルLLM(Ollama)
llm = Ollama(model="llama2", temperature=0)
# 後は同じ方法でLCELチェーンを構築
chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
この方法なら外部APIへの依存がなく、プライベートなデータを扱う場合に有効です。
それでも解決しないとき
- LangChainの最新ドキュメントで、推奨される実装パターンを確認する
ConversationalRetrievalChainを使い、オプショナルなパラメータ(condense_question_promptなど)を段階的に追加していく- コミュニティのサンプルコード(GitHub、ブログなど)を参考にしながら、独自の実装に合わせていく
まとめ:開発を進める際のポイント
RAG開発で安定性を確保するには、以下の3点が重要です:
-
バージョンを明示的に管理する:LangChainは変化が早いため、
requirements.txtやpyproject.tomlでバージョンを固定することで、将来のエラーを回避できます。 -
**本番環境の構成を
あわせて読みたい
- LangChain × ベクトルDB で RAG システムを実装|5ステップで自社データAIを構築
- AIエージェントが会話を忘れる原因と解決方法【RAG・メモリ設計3つの実装パターン】
- LangChainで複数AIエージェント連携|CI/CD自動化【実装3ステップ】
参考ソース
- ImportError: cannot import name 'RetrievalQA' from 'langchain.chains' in Python project
- RAG model chat history does not work properly
- How to deploy chroma database (vector database) in production
- How do i add memory to RetrievalQA.from_chain_type? or, how do I add a custom prompt to ConversationalRetrievalChain?