はじめに
初めまして、AI/ML ソリューションアーキテクトの近藤です。
RAG (Retrieval Augmented Generation, 検索拡張生成) プロジェクトを成功させる方法、連載の第二回です。本連載では、RAG の手法や AWS のサービス・機能をただ単に紹介するというよりも、実践的なナレッジを発信していくことにフォーカスしています。連載前回の 第一回 では、RAG プロジェクトが始まったらいきなり RAG を作る前に、その問題が生成 AI で本当に解ける問題なのか確かめよう、というメッセージをお伝えしました。
連載第二回の今回は、あるテーマに対して実際に RAG を作ってみて、失敗し、その失敗を改善していく過程をお見せしようと思います。また、その改善の過程で、RAG の鉄板構成としてよく用いられているベクトル検索等の文章のチャンキングを検索するという手法の懸念点についても触れます。
※ 第一回の記事内に、次回予告として連載第二回はベクトル DB や GraphDB について扱う旨が書かれていましたが、予定を変更しました。そちらの内容は次回以降にご期待ください。
builders.flash メールメンバー登録
builders.flash メールメンバー登録で、毎月の最新アップデート情報とともに、AWS を無料でお試しいただけるクレジットコードを受け取ることができます。
本記事のテーマ
みなさま AWS の開発者向けドキュメントを見たことがありますでしょうか。例えば Amazon Bedrock のページ はこちらの画像のようなものです。AWS サービスの仕様や利用方法について記載があり、AWS の利用者はほとんどが利用したことがあるかと思います。
この開発者向けドキュメントですが、サービスによっては PDF で換算して数 1,000ペ ージ分にもなる分量になっており、この中から知りたい情報を見つける作業に苦労した人も多いと思います。実際、私も AWS サービスの仕様についてドキュメントから情報を見つけるのにかなりの時間を使っています。
そこで、この開発者向けドキュメントの RAG を作り、ユーザーからの質問に答えてもらうことを目指します。例えば、「Claude 3.5 sonnetのモデル ID はなんですか ?」という質問を投げたら、「Claude 3.5 Sonnet には 2 つのモデルがあり、3.5 Sonnet (anthropic.claude-3-5-sonnet-20240620-v1:0) と 3.5 Sonnet v2 (anthropic.claude-3-5-sonnet-20241022-v2:0) です」のような回答が返ってくるイメージです。全ての AWS サービスを扱うと量が多すぎるので、今回は Amazon Bedrock に限定します。
これがうまくいけば、全てのAWS サービスに展開できることはもちろん、AWS の開発者向けドキュメント以外の技術ドキュメントにも応用できる可能性があります。

Step 1. 評価データの作成と実現性評価
Step 2. とりあえずベーシックな RAG 構成で試してみる
それではここから RAG をつくっていきましょう。ひとまず感触を掴むために、最もシンプルでベーシックな構成で試してみます。つまり、ユーザーの質問文でそのままベクトル検索をして、その結果を LLM に渡して回答してもらうというやり方です。
実装方法は Amazon Bedrock Knowledge Bases を用います。理由は、実装が楽だからです。ドキュメントを Amazon S3 経由で連携するだけで、チャンキング、ベクトル化、インデックス等、いろいろな準備をしてくれてすぐに RAG の実験を始めることができます。
Amazon Bedrock Knowledge Bases の詳細についてはこちらをご覧ください。

実験設定の詳細 :
リージョン : us-east-1
ソースドキュメント : 英語
チャンキング : 階層チャンキング (親チャンクサイズ 1500 - 子チャンクサイズ 300 - overlap 割合 60%)
検索 : ベクトル検索のみ
検索結果取得数 : 5
Embedding モデル : Titan Text Embeddings v2, 次元数 1024
推論モデル : Claude 3.5 Sonnet v2
結果
2 点 : 13/18 (68%)
1 点 : 4/18
0 点 : 1/18
完全正答率は 68% となりました。先ほどのステップで、必要な情報があれば完全正答率 100% でしたが、今回は比較的簡単な質問を用意しているので、もう少し高い正答率を期待したいです。ここから改善していきましょう。
Step 3. 結果の分析
Step 4. 考察とチャンキング検索の懸念
Step 5. 目次 RAG を試してみる
この思いついた手法(目次 RAG と名付けてみました)を試してみましょう。モデルは全て Claude 3.5 Sonnet v2 を用いています。流れは以下の通りです。
目次を LLM に渡して、ユーザーの質問に関連しそうなページ番号を推測させる
そのページの中身全部 LLM に渡して回答を作成してもらう

1 : 目次を LLM に渡して関連しそうなページ番号を推測させる
AWS の開発者向けドキュメントには次のような目次がついています。これを渡して、ユーザーの質問がどこに該当するかを LLM に推測させます。
# What is Amazon Bedrock?: 1
- What can I do with Amazon Bedrock?: 1
- How do I get started with Amazon Bedrock?: 2
- Amazon Bedrock pricing: 3
- Key terminology: 3
# Getting started: 6
- Request access to an Amazon Bedrock foundation model: 9
- (Optional tutorials) Explore Amazon Bedrock features through the console or API: 10
- Getting started in the console: 10
- Explore the text playground: 11
- Explore the image playground: 11
- Getting started with the API: 12
- Install the AWS CLI or an AWS SDK: 12
- Get credentials to grant programmatic access to a user: 13
- Try out some Amazon Bedrock API requests: 14
- Run examples with the AWS CLI: 15
- Run examples with the AWS SDK for Python (Boto3): 17
- Run examples with a SageMaker AI notebook: 21
- Working with AWS SDKs: 24
# Access foundation models: 26
- Grant permissions to request access to foundation models: 26
- Add or remove access to foundation models: 29
利用したプロンプト
利用したプロンプトはこのようなものです。何度か試行錯誤して、この形に落ち着きました。
<table_of_contents>
{{table_of_contents}}
</table_of_contents>
Bedrockのドキュメントの目次を渡しています。目次は# hogehogeが大項目、- fugafugaが小項目を表しています。
この目次を参考にユーザーのBedrockに関する質問がドキュメントのどの部分に該当するかを予想して、その範囲の最初と最後のページ番号を出力してください。
1. 目次を丁寧に読み、ユーザーの質問に該当しそうな項目の候補を全て挙げます
2. 適切な情報が含まれている可能性が高い順に候補をランクづけします
3. 合計のページ数が30ページ以内に収まるように、最終的なページの範囲を選定します
<output_format>
[[<範囲の最初のページ番号>, <範囲の最後のページ番号>],...]
</output_format>
<output_example>
[[2-10], [150-170]]
</output_example>
出力の前に<thinking>内でstep-by-stepで思考してください。
選んだ理由を<reason>内でで説明してください。
回答は<response>で囲んでください。
<question>
{{question}}
</question>
質問例に対する出力結果
例えば、「 Bedrock ガードレールではどのようなフィルタリングができますか ?」の質問を入れてみるとこのような出力になります。必要な部分を適切に選定できている感じがします。この例では 20 ページで約 36,000 文字でした。
<thinking>
1. 関連しそうな項目を探す:
- "Stop harmful content in models using Amazon Bedrock Guardrails": 510-604
- "Components of a guardrail": 515
- "Content filters": 516
- "Filter classification and blocking levels": 518
- "Filter strength": 518
- "Sensitive information filters": 522
- "Word filters": 527
- "Block images with image content filter": 534
2. ランク付け:
1位: 510-604の範囲 - ガードレールの全体像と各フィルタリング機能の詳細説明
2位: 515-534の範囲 - フィルタリングの具体的な機能説明
3. ページ数の制限を考慮:
- 510-604は94ページあり、30ページの制限を超えています
- 515-534は19ページで、フィルタリング機能の核心部分をカバーしています
最適な範囲として515-534を選択します。
</thinking>
<reason>
515-534のページ範囲を選んだ理由:
1. ガードレールのフィルタリング機能に特化した説明が含まれている
2. コンテンツフィルター、センシティブ情報フィルター、ワードフィルター、画像フィルターなど、具体的なフィルタリング方法が網羅されている
3. 30ページの制限内に収まっている
4. フィルタリングの分類、ブロックレベル、強度など、実装に必要な技術的な詳細も含まれている
</reason>
<response>
[[515, 534]]
</response>
2 . そのページの中身を全部 LLM に渡して回答を作成してもらう
関係ない情報を含む数万数万文字という大量のドキュメントを入れても、質問に答えられるということをいくつかの質問で確認しました。プロンプトは極限までシンプルにしました。
<context>
{{context}}
</context>
コンテキストを参考にユーザーの質問に回答してください。質問への回答以外は出力しないでください。
コンテキストの内容から回答できない場合は、「与えられた情報からは回答できません」と回答してください
<question>
{{question}}
</question>
結果 :
ステップ 1 、 ステップ 2 ともにいくつかの質問でうまく回答できていることが確認できたので、全質問で実行と評価を回してみます。
2 点 : 17/18 (94%)
1 点 : 0/18
0 点 : 1/18
先ほどの実験よりかなり結果が良くなっています。
余談 :
実は、LLM-as-a-Judge の評価結果では 1 点の回答が 3 つあったのですが、私が目で見て確認したところ、正解 (2 点) として問題ないと判断をしました。私が評価のために設定したプロンプトだと LLM の評価が厳しすぎて、正解に含まれている要素が1つでも抜けていたら 1 点にしてしまっています。しかし、重要な要素が含まれていれば正解としていいというケースもありますので、この評価方法は改善の余地があります。
Step 6. 考察
唯一 0 点となってしまった質問は「Claude 3.5 sonnetのモデル ID はなんですか ?」です。これは逆に Amazon Bedrock Knowledge Bases を用いた手法では正解できていた質問ですが、今回の手法では回答が難しい問題です。
というのも目次が次の画像のようになっているのですが、正解に必要な情報は “Supported foundation models” の項目にあります。しかし、何も知らない人が見たらこの項目にモデル ID の情報があると気づくことは難しいでしょう。実際 LLM は 152 ページの “Anthropic Claude models” と 273 ページの “Anthropic Claude3 models” の項目に答えがあると判断しています。
このように、目次から判別が難しい場所に情報が入っているケースは不得意なようです
ベクトル検索と両方を利用することで、双方のメリットを活かして、今回の評価データセットでも満点を取れるかもしれません。
このようにきれいな目次があるドキュメントでしか使えないというのがこの手法の限界です。技術ドキュメントは綺麗な目次が用意されていることが多い上に、正しい情報が求められるため、この手法が使えるケースが多いかもしれません。また、私は専門外ですが、法律・医療・金融なんかのきちんとした文章にも適用できるかもしれません。
また、レイテンシーが大きいというのもこの手法のデメリットです。LLM が 2 段階になっていることと、インプットの情報量が多くなっていることがその原因です。
逆に、この手法のいいところは、LLM の進化のスピードに合わせて手法が改善されていく可能性があることです。最近は LLM の進化のスピードが早いので、新しいモデルに切り替えるだけで性能が改善される可能性があるのはメリットです。

まとめ
長々と書いてきましたが、本記事で伝えたいことは2つです。
- RAG の改善はまず結果の分析からしましょう。
- ベクトル検索やチャンキング検索には限界もあるため、柔軟に手法を選びましょう。
本記事で提案している目次 RAG については、まだまだ少数のデータでしか検証していないですし、すでに限界も見えてきています。この手法の有効性を示したいわけではありません。 RAG の改善の過程をお見せして、場合によってはチャンキング検索に囚われなくてもいいということを示すことが本記事の目的でした。
この連載の続きで、もっといろいろな手法を試していく予定です。乞うご期待 !!
筆者プロフィール
アマゾンウェブサービスジャパン 合同会社
AI/ML スペシャリスト ソリューションアーキテクト
スノーボード、検索、レコメンドが好き。
最近は RAG にハマっている。
冬はスノーボードとその後の温泉のことのみ考えている。

Did you find what you were looking for today?
Let us know so we can improve the quality of the content on our pages