𝗜 𝗕𝘂𝗶𝗹𝘁 𝗥𝗔𝗚 𝗙𝗿𝗼𝗺 𝗦𝗰𝗿𝗮𝘁𝗰𝗵 𝗶𝗻 𝗣𝘆𝘁𝗵𝗼𝗻 𝘁𝗼 𝗨𝗻𝗱𝗲𝗿𝘀𝘁𝗮𝗻𝗱 𝗜𝘁
私は半年間、本番環境でLangChainを使用していました。しかし、それがどのように機能しているのかを説明することができませんでした。なぜ特定の指標を選んだのか、テキストがどのようにベクトルに変換されるのかも分かりませんでした。ライブラリがロジックを隠してしまっていたのです。
これを解決するために、私はフレームワークを削除しました。そして、500行のプレーンなPythonを使って、RAGパイプラインをゼロから書き直しました。
スタックを手動で構築することで学んだことを、以下にまとめます。
ブラックボックス化の問題
高レベルなライブラリを使用すると、コントロールを失います。モデルが事実をハルシネーション(捏造)したり、誤った引用を行ったりする場面に遭遇しましたが、そのエラーがチャンカーにあるのか、埋め込みモデルにあるのか、あるいはプロンプトにあるのかを判断できませんでした。
自分で構築すれば、すべてのレイヤーを検査できます。LLMに送信される正確なチャンクをプリントして確認できますし、文章がどこで区切られているかも正確に把握できます。
RAGの5つのレイヤー
RAGは単一のアルゴリズムではありません。5つの異なるプロセスが積み重なったものです。
- チャンキング (Chunking): テキストをどのように分割するかを決定する。
- 埋め込み (Embedding): テキストを数学的な数値に変換する。
- 検索 (Retrieval): 適切な断片を見つけ出す。
- プロンプト構築 (Prompt Construction): モデルの振る舞いを指示する。
- 生成 (Generation): 最終的な回答を得る。
構築から得た教訓
1. チャンキングが最も重要なステップである
ほとんどのチュートリアルでは、この工程が省略されています。オーバーラップ(重なり)を使用しないと、境界部分でコンテキストが失われてしまいます。私は文字レベルのオーバーラップを持つスライディングウィンドウを使用しました。これにより、モデルが2つのチャンク間のつながりを確実に把握できるようになります。
2. 距離指標が重要である
検索結果が悪い原因を特定するために、何時間もデバッグしました。問題はデータではなく、指標にありました。ChromaDBのデフォルトはL2距離ですが、セマンティック検索にはコサイン類似度(Cosine similarity)が必要です。たった一行のコードで、すべてが変わりました。
3. プロンプトには制約が必要である
LLMは「補完するもの」であり、「神託(オラクル)」ではありません。曖昧な質問をすると、回答を捏造してしまいます。私は厳格な拒絶テンプレートを使用することを学びました。モデルに対して「コンテキストに答えが含まれていない場合は、わからないと答えてください」と指示しました。これにより、ハルシネーションを40%から5%にまで抑えることができました。
4. リクエストをバッチ処理する
チャンクごとに1つのHTTPリクエストを送信すると時間がかかります。バッチで送信すれば、はるかに高速になります。これにより、ローカルモデルの処理をパイプライン化できます。
5. ボトムアップでテストする
最後にテストを書くのではなく、まずチャンカーをテストしてください。次にエンベッダー、その次にストアをテストします。最後にまとめてテストしようとすると、ロジックではなくバグをテストすることになってしまいます。
もし自分のAIスタックを真に理解できていないと感じるなら、自分で構築してみてください。コードを書くことが目的ではありません。考えることこそが目的です。
Optional learning community: https://t.me/GyaanSetuAi
