ハクソク

世界を動かす技術を、日本語で。

HNに聞く: ローカルでのRAGの実施方法は?

92日前

概要

  • RAG(Retrieval-Augmented Generation)のローカル実装方法の概要
  • 依存関係を最小限に抑えるための一般的なアプローチ
  • ベクターデータベースやセマンティックサーチの利用状況
  • Knowledge GraphやHypergraphの活用事例
  • 内部コードや複雑なドキュメントへの適用例

RAGをローカルで最小依存で実現する方法

  • RAGのローカル実装では、軽量なツールやライブラリの利用が主流
  • ベクターデータベース(例:FAISS、Annoy)は高性能かつ依存関係が少ないため人気
  • セマンティックサーチには、sentence-transformersOpenAI embeddingsなどの軽量モデルを活用
  • Knowledge GraphHypergraphは、大規模な構造化データ複雑な関係性が必要な場合に限定的に利用
  • 内部コードや複雑なドキュメントは、テキスト分割・埋め込み生成・インデックス化の3段階で処理

具体的な構成例

  • FAISS:C++ベースでPythonバインディングがあり、高速な類似検索が可能
  • Annoy:シンプルでインストールが容易、Pythonのみで完結
  • ChromaMilvusなどの他のベクターストアは、やや重めだが選択肢
  • sentence-transformers事前学習済みモデルで日本語も対応、小規模環境向け
  • ElasticsearchBM25やベクトル検索両対応だが、依存関係がやや多い

Knowledge Graph・Hypergraphの活用状況

  • Knowledge Graphエンティティや関係性を明示的に管理したい場合に利用
  • Hypergraph複雑な多対多関係を表現したい特殊用途に限定
  • 一般的なRAG用途では、ベクトル検索やセマンティックサーチが主流
  • GraphDBNeo4jのようなツールは、依存関係が増えるため限定的

内部コード・複雑ドキュメント対応の工夫

  • コードやドキュメントを関数・クラス単位で分割し、埋め込み生成
  • 検索時に類似度スコアで候補を絞り込みRAGモデルに渡す
  • 依存関係を減らすため、Dockerや仮想環境を活用するケースも多い
  • YAMLやJSON形式でメタデータを管理し、検索精度を補助

まとめ

  • 最小依存構成では、FAISSやAnnoy+sentence-transformersの組み合わせが主流
  • Knowledge GraphHypergraph必要性に応じて限定的に利用
  • 内部コードや複雑ドキュメントでも、テキスト分割と埋め込み検索で十分対応可能

Hackerたちの意見

コードにはベクターデータベースを使わない方がいいよ。埋め込みは遅いし、コードには向いてない。コードにはbm25+トライグラムがいい結果を出して、検索応答もサクサクだしね。
スタティック埋め込みモデルは結構速いの見つけたよ。lee101/gobedはGPUで1msだし :) でもコード用にトレーニングする必要があるかな。大きなコードのLLM埋め込みも高品質だし、要はパレートフロンティアの理想的な場所がどこかってことだね。確かに、コードの場合はbm25やrgが多いけど、検索の質が本当に重要なら、もう少し複雑な解決策も可能だよ。
AIがドキュメントへのアクセスをもっと必要としてるから、ドキュメント検索にRAGを使うのはどう思う?
同意だね。ここで誰かがgrepにハイブリッドテキスト/ベクター検索の機能を追加するドロップインを投稿してたけど、ファイルを再インデックスする必要が常にあって、面倒だった。さらに、モデルがコード検索用でない場合、ベクター検索はノイズが多くなるし、リランカーを使わないと余計にね。実際のところ、gpt-oss 20Bをループで回してripgrepにアクセスするのはかなりうまくいくよ。gpt-ossは他のツールと比べて神ツールだし、速い。
ファイルパスとシグネチャに適用したら、素晴らしい結果が出たよ。BM25と結果を融合させるとさらに良くなる。
BM25 + ベクター検索を半ダースのマイクロサービスを立ち上げずにやってくれる良いサービスやDocker知ってる?
Postgresでハイブリッド検索ができるよ。宣伝しちゃうけど: https://github.com/jankovicsandras/plpgsql_bm25 PL/pgSQLで実装されたBM25検索(Unlicense / パブリックドメイン)。リポジトリには、ハイブリッド検索用のPL/pgSQL関数(plpgsql_bm25 + pgvector)と逆順位融合のためのplpgsql_bm25rrf.sqlも含まれてるし、Jupyterノートブックの例もあるよ。
ベクター生成には、2024年4月にMeta-LLama-3-8Bを使い始めたよ。PythonとTransformersでテキストチャンクごとにRTX-A6000でね。あれは速かったけどノイズが多くて、500Wも消費した。だから1年前にM1 Ultraに切り替えて、TransformersをAppleのMLX Pythonライブラリに置き換えた。速度はほぼ同じだけど、熱とノイズは少なくなった。Llamaモデルは4k次元だから、fp16だとチャンクごとに8キロバイトで、これをnumpy.save()でSQLiteのBLOBカラムに保存してる。RTXとM1の間でベクター出力にほとんど差はないけど、検索結果を変えたり、ベクターを再生成したり、別のLLMに変えたりするほどの差はないよ。検索のためにSQLiteデータベースからすべてのベクターをnumpy.arrayに読み込んでFAISSに渡してる。Faiss-gpuはRTX6000で驚くほど速かったし、faiss-cpuはM1 Ultraで少し遅いけど、目的には十分な速さだよ(1分ごとじゃなくて、1日数回クエリを投げるだけだから)。500万チャンクでメモリ使用量は約40GBで、A6000にもM1 Ultraの128GBにも余裕で収まる。うまくいってるし、満足してるよ。
幸いなことに、私の複雑なドキュメントのほとんどはMarkdownファイルです。https://github.com/tobi/qmd/ をおすすめします。これはこういったファイルを検索するためのシンプルなCLIツールです。以前はfzfを使っていましたが、このツールの方が結果が良くて、さらにファジー検索もできるんです。ただし、コードには使っていません。
その前提を考えると、そのリンクがgolangで書き直されたgrepツールか、もしくはMarkdown用にカスタマイズされて「# ヘッディングタイトル」のマッチを重視するようなものであることを期待していました。
私たちは1日あたり約30万件の顧客対応をしているので、遅延と精度が本当に重要です。私たちはナレッジベースの上にRAGベースの内部ポータルを構築しました(基本的にはより良いFAQです)。リトリーバルの部分では、サービスのトレーサビリティと発見のために特別にカスタム検索/インデックス層(Node)を作りました。これはハイブリッドアプローチを使っていて、埋め込み + フルテキスト検索 + IVF-HNSWを組み合わせて、私たちのAPI、サービス、プロキシ、オーケストレーションリポをインデックス化し、相互参照しています。この層の上にRAGパイプラインがあり、合理的なリコールと予測可能な遅延を実現しています。ただ、コンプライアンスと可観測性はまだ問題です。毎年新しいベンダーが監査やデータの系譜、可観測性を約束して現れますが、600以上の分散システムの情報の散乱を本当に扱えるところはありません。エントロピーは増え続けています。最近は、ナレッジグラフの上によりセマンティック/論理的なKAGアプローチを試して、これらのシステムに散らばったビジネスルールをマッピングしています。目標は、実際に物事がどのように機能しているかについての高次の質問に答えることです — Palantirのような結果ですが、魔法ではなく明示的な論理を使っています。他の人たちが「純粋なRAG」を超えて、グラフベースやハイブリッド推論のセットアップに移行しているか気になります。
これについては書いたことがあります(その投稿はHNにもありました)が、主に組織としてのインフラ上でRAGを運用する視点からです。でも、クラウドサービスの一般的なコンポーネントや代替手段についても触れています。あなたが具体的に必要としていることにどれだけ役立つかは分かりませんが、こちらです: https://blog.yakkomajuri.com/blog/local-rag
ローカルRAGシステムが私のコンピュータを遅くする気がする(M1 Pro 32GBを使ってる)ので、これを防ぐためにホスティングされたものを使っています。私のビジネスではベクターデータベースを使っているので、ナレッジベースをベクタライズしてホストするために新しいデータベースを作りました。1. 私のナレッジベースはすべてMarkdownファイルです。だから、ヘッダータグで分割しています。2. 分割したものはハッシュ化され、そのハッシュ値がSQLiteに保存されます。3. ハッシュ化されたバージョンはベクタライズされ、クラウドデータベースにプッシュされます。4. 変更を加えるたびに、スクリプトを実行して分割とハッシュをチェックします。もし変更があれば、ドキュメントをアップサートします。そうでなければ何もしません。これでストアを最新の状態に保つことができます。検索にはCLIクエリを使って、ベクターストアから検索して取得しています。
Dockerイメージを使ってベクターデータベースを運用しています。そして、ローカルRAGのリトリーバルをデバッグ・ベンチマークするために、実際に何が取得されているかを示すCLIツールを作っています: ragtune explain "あなたのクエリ" --collection prod これでスコア、ソース、診断が表示されます。チャンクや埋め込みが静かに失敗しているときや、判断の基にする数値的な推定が必要なときに気づくのに役立ちます。オープンソースです: https://github.com/metawake/ragtune
私が作ったもので、これを使っています: https://github.com/libragen/libragen これはCLIツールで、RAG可能なコンテンツの離散的でバージョン管理された「ライブラリ」を作成するためのMCPサーバーです。内部ではローカルに埋め込みモデルを使用しています。コンテンツをチャンク化して、埋め込みをSQLiteに保存します。検索機能はベクトル + キーワード検索 + 再ランキングモデルを使用しています。また、任意のGitHubリポジトリを指定すると、それからRAG DBを作成します。MCPサーバーを使ってライブラリを作成・クエリすることもできます。サイト: https://www.libragen.dev/
リトリーバル段階では、高効率でCPUフレンドリーなテキスト埋め込みモデルを開発しました: https://huggingface.co/MongoDB/mdbr-leaf-ir これはそのサイズのモデルのリーダーボードで1位になっています。これは、元になったモデルと互換性があります(https://huggingface.co/Snowflake/snowflake-arctic-embed-m-v1...)。ここでセマンティック(埋め込みベース)検索とbm25、ハイブリッドの比較例が見れます: http://search-sensei.s3-website-us-east-1.amazonaws.com (注意!最初のロード時にモデルの重みとonnxランタイムのために約50MBのデータがダウンロードされますが、それ以外は電話でもスムーズに動作するはずです)このミニアプリは、セマンティック検索とbm25検索の利点を示しています。例えば、埋め込みモデルは「j lo」がジェニファー・ロペスを指すことを「知っている」んです。また、このタイプのモデルをトレーニングするためのレシピも公開していますので、興味があればどうぞ。比較的控えめなハードウェアで実行でき、トレーニングデータも非常に簡単に取得できます: https://arxiv.org/abs/2509.12539
地元のRAGを何に使ってるのか気になるなぁ。