AIコーディング 2026.05.16

LangChain×ChromaDB×FAISSエラー解決|ImportError・メモリ不足・デプロイ失敗の原因と対策

タグ:LangChain / ChromaDB / RAG / エラー対処 / ベクトルDB

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 を使う場合は別途インストールが必要です。

切り分け手順

  1. インストール済みのLangChainバージョンを確認する

    pip show langchain
    
  2. 利用可能なクラスを確認する

    import langchain.chains
    print(dir(langchain.chains))
    
  3. 別の場所からインポートされていないか確認

    # 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 OpenAIfrom langchain_openai import OpenAI
from langchain.embeddings import OpenAIEmbeddingsfrom langchain_openai import OpenAIEmbeddings
from langchain.chains import RetrievalQALCELで直接構築(インポート不要)

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の基本形は単一の質問に対する回答を返すだけで、デフォルトではチャット履歴を保持しません。会話の文脈を保つには、明示的にメモリコンポーネントを追加する必要があります。また、ドキュメント検索と会話履歴の組み合わせで、プロンプトの書き方に工夫が必要になります。

切り分け手順

  1. 現在のメモリ設定を確認する

    # ConversationalRetrievalChainを使っているか?
    # 明示的なメモリオブジェクトは設定されているか?
    print(chain.memory)  # Noneなら未設定
    
  2. プロンプトテンプレートの内容を確認する

    print(chain.llm_chain.prompt.template)
    # chat_historyや{history}が含まれているか?
    
  3. テスト実行してログを見る

    # 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の基本的な使い方は開発向けで、永続化やマルチプロセスアクセスへの対応が十分ではありません。本番環境では、ベクトルデータの永続ストレージ、複数インスタンス間でのデータ共有、メモリ効率の向上が必要になります。

切り分け手順

  1. 現在のChromaDB設定を確認する

    import chromadb
    client = chromadb.Client()  # メモリのみモード
    collection = client.get_or_create_collection("my_collection")
    # プロセス終了後、このデータは消える
    
  2. 永続化の有無を確認する

    # 永続化ディレクトリが作成されているか?
    import os
    print(os.path.exists("./chroma_data"))
    
  3. 本番環境のリソース使用状況を確認する

    # メモリ使用量を監視
    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を使う必要があります。

切り分け手順

  1. 現在の実装を確認する

    from langchain.chains import RetrievalQA
    qa = RetrievalQA.from_chain_type(llm=llm, retriever=retriever)
    # このQAオブジェクトは memory 属性を持たない
    
  2. 必要な機能を整理する

    • メモリが必要か?→ LCELまたはConversationalRetrievalChainを使う
    • プロンプトのカスタマイズだけか?→ 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点が重要です:

  1. バージョンを明示的に管理する:LangChainは変化が早いため、requirements.txtpyproject.tomlでバージョンを固定することで、将来のエラーを回避できます。

  2. **本番環境の構成を


あわせて読みたい

参考ソース