DMM.博士 通信 Vol.3 - RAGシステム環境の落とし穴

サムネイル

はじめに

DMM.博士通信の3回目の投稿になります。前回の投稿が2025年1月でしたので随分間が空いてしまいました。前回までの流れは以下の記事をお読みいただければと思います。

前回のおさらい

前回までで、Slack上で働くChatGPT様式のサービスをAzure OpenAIで構築しており、分散型RAGサテライトシステムの1stケースとして、社内規定RAGを構築し社内提供しているとお話ししてきました。次回予告としては2ndケースについて取り上げる流れで記載していました。

2ndケースでも社内規定RAGと同じくAmazon Bedrockで構築しその構成もほぼ同じものですが、扱うデータは Google Sites となり定型のテキストとは性質が異なります。 そのためRAG APIサーバーは社内規定RAGとは別に分離したものとなります。ですがこのデータの扱いについてのお話しは次回にして、今回はRAGシステム開発中に遭遇した困り事について取り上げたいと思います。

困ったこととは...

RAGにデータを読み込ませるために複数のコンテナで処理をしているのですが、ある条件では次のようなエラーがでてしまい処理が止まるというものです。

ModuleNotFoundError: No module named 'faiss.swigfaiss_avx2'
ModuleNotFoundError: No module named 'faiss.swigfaiss_avx512'

Faiss が AVX2またはAVX512 を使おうとしているがモジュールがみつからないというもの。 ローカルでは問題なく動いていたはずなのにクラウドでは動くこともあれば動かないこともあったりする。 またクラウドで生成したインデックスデータを取り込もうとするとローカルでもこのエラーが出たりします。

AVX2(AVX512) とはインテル、AMDが自社プロセッサに採用しているベクトル演算命令の拡張セットです。 勘の良い方はお気付きかと思いますが、これはアーキテクチャの一貫性に起因する問題です。

RAGシステム構成

前回構築したRAGも、次回とりあげる2ndケースRAGも基本的な構成は同じで次のとおりです。 これらはローカル環境では docker compose up でそれぞれ別のコンテナとして起動します。 クラウド環境ではAWS Fargateで各コンテナとして起動します。

  • Data Collector

    • Playwrightで実装しており、対象のWebサイトをクロールして取得したHTMLデータを加工処理して、所定のデータ形式に変換してからS3にアップロードします。
  • Indexer

    • Data Collector が作成したデータを読み込みチャンク分割処理などしてVectorstoreで扱うインデックスデータファイルを作成します。
  • Vectorstore & Retriever

    • 実装はFaissを採用しています。Indexer が作成したインデックスを読みこませ、プログラム正常起動時にはRAG APIとして機能します。

シーケンス図にすると次のようになっています。

シーケンス図

問題の発生順序

データの流れとしては Data Collector → Indexer → Vectorstore の順に進みます。 Data CollectorはテキストあるいはJSONなど可読性のあるデータを出力しますが、Indexer はバイナリデータを出力します。

大抵の場合、まずはローカル環境のDockerで一連の処理を行い、RAG APIが機能することを確認してからクラウド環境へデプロイします。 クラウド環境で問題が発生するときは、インデックスを読み込んだRAG APIが起動するときです。

なぜ発生するのか

疑わしい点は、AWS Fargateで起動するコンテナのアーキテクチャの明示指定の有無と、対象アーキテクチャ(x86_64・AMD64・ARM)の確認にあります。 明示的に指定していない場合、AVX対応状況の異なる計算機リソースが割り当てられている可能性があります。 そのためx86_64で統一して揃えておくことでAVXサポートを有効にして安定させることができます。

Faiss は実行環境がAVXをサポートしていなければAVXを有効にしませんが、x86_64アーキテクチャ上ではAVX2あるいはAVX512を使おうとします。

またFaissの振る舞いについてはこちらのソースコード内にコメント記載がありますので確認しておくとよいでしょう。

def dependable_faiss_import(no_avx2: Optional[bool] = None) -> Any:
    """
    Import faiss if available, otherwise raise error.
    If FAISS_NO_AVX2 environment variable is set, it will be considered
    to load FAISS with no AVX2 optimization.

    Args:
        no_avx2: Load FAISS strictly with no AVX2 optimization
            so that the vectorstore is portable and compatible with other devices.
    """
    if no_avx2 is None and "FAISS_NO_AVX2" in os.environ:
        no_avx2 = bool(os.getenv("FAISS_NO_AVX2"))

    try:
        if no_avx2:
            from faiss import swigfaiss as faiss
        else:
            import faiss

例えばIndexerでAVX512をサポートして起動したとき(次のようなログ出力で確認できる)、出力されたインデックスファイルを読み込むvectorstoreがAVX2サポートで起動していれば不整合が生じます。

INFO 2024-12-19 06:31:29,739 <loader.py.<module>> - Loading faiss with AVX512 support.

vectorstoreがARMで起動していたときも同様でおそらく次のエラーをみるでしょう。

No module named 'faiss.swigfaiss_avx512'

ローカル開発環境との関係

仮にクラウド環境(ここではFargate)で問題なく動作していたとして、ローカル環境では問題になることがあります。

私たちの開発メンバーはもれなく開発機にMacを使用しておりローカル開発機はARMとなっていました。 クラウド環境がx86_64でインデックス生成していた場合、ローカル環境でこのインデックスをそのまま扱うとエラーになります。

やはりここでもDockerにアーキテクチャ指定をすることが大切になります。 platform に linux/x86_64 を指定してクラウド環境とアーキテクチャを揃えると良いでしょう。

おわりに

今回は LangChain + Faiss で RAG 開発をする際に生じた問題について記載してみました。 今となってはRAG構築はマネージドサービスに寄せるケースが多いようにも思いますので、 少し賞味期限切れかもしれませんが、何かの参考になれば幸いです。