Bedrockのナレッジベース評価機能でBaseline RAGとGraphRAGを比較する！

2024年12月14日、Amazon Bedrock Knowledge BaseにLLMによる評価機能が追加されました！

https://aws.amazon.com/jp/about-aws/whats-new/2024/12/amazon-bedrock-knowledge-bases-rag-evaluation-preview/

この機能によって、AWS上でRAGの検索機能やコンテンツ生成機能に対する評価をすることができる用になります。
すなわち、**AWS上でのLLMOpsを促進するような機能**になっています。

これまではAWS上でRAGの性能評価をしようとすると、LangfuseやRagasなど外部のモジュールを別途用いる必要がありました。

しかし、これからはAWSの公式機能としてLLMOpsができるようになります！
Bedrockを用いたRAGアプリを運用している方は非常に助かるのではないでしょうか！

今回はこのRAG評価機能を用いて、Baseline RAGとGraphRAGを比較してみようと思います！

※上記2種類のRAGの違いについては、以前に作成した以下の記事で解説しております！

https://qiita.com/har1101/items/f64535c4459820d47524

# Baseline RAG(Aurora Serverless v2)を作成する
まずはBaseline RAG、すなわちベクトルDBを用いたRAGを作成します。

作成方法については、クイック作成を利用してもよいですが、せっかくならゼロスケーリングに対応したAuroraを作ってみようということで先日試してみたので、こちらを参考にしていただければと思います。

https://qiita.com/har1101/items/488e9f7e4eebc54c91a0

# GraphRAGを作成する
こちらは2024年12月4日にアップデートがあった機能となっています。
GraphRAGとは？から実際の構築方法まで解説したブログを作成してあるので、こちらをご参照ください。

https://qiita.com/har1101/items/f64535c4459820d47524

ただし、今回はチャンキング戦略をデフォルトではなく、階層型にしてみました。
以前はPDFにしていたドウデュース(私の愛馬です)に関するドキュメントを、今回はMarkdown形式にしているためです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/be4c76a9-5e34-a4a1-b3b5-c2b614607956.png)

# 比較検討する
ということでRAGが2つ作成できたら、いよいよ評価機能を使って比較していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/c9e6f122-066d-9b55-5e74-0d5ff25e3fee.png)

## S3にデータセットを格納する
その前に、評価に用いるデータセットを作成する必要があります。

このデータセットとは、RAGに対する質問とその模範解答をあらかじめ用意するようなものになっています。
その模範解答とRAGの回答を比較し、内容の正確性や完全性などを評価します。

形式としては、以下のような形になるそうです。
```sample.jsonl
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"A trigger is a resource or configuration that invokes a Lambda function such as an AWS service."}]}],"prompt":{"content":[{"text":"What is an AWS Lambda trigger?"}]}}]}
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"An event is a JSON document defined by the AWS service or the application invoking a Lambda function that is provided in input to the Lambda function."}]}],"prompt":{"content":[{"text":"What is an AWS Lambda event?"}]}}]}
```

横に書かれてもよくわからないですね…1行目の方を階層構造にしてみましょう。
(構造としては1行目も2行目も同じです)
```sample.jsonl
{
    "conversationTurns": [
        {
            "referenceResponses": [
                {
                    "content": [
                        {
                            "text": "A trigger is a resource or configuration that invokes a Lambda function such as an AWS service."
                        }
                    ]
                }
            ],
            "prompt": {
                "content": [
                    {
                        "text": "What is an AWS Lambda trigger?"
                    }
                ]
            }
        }
    ]
}
```

ということで上記より、以下2つの内容が確認できました。
- prompt（質問）:
    - システムに投げかける質問を定義
- referenceResponses（模範解答）:
    - その質問に対する「理想的な回答」や「正解とみなせる回答」を定義
    - システムが生成した回答の品質を評価する際の基準として使用

ということで、上記の形に倣っていくつかデータセットを作成してみました。
書いてあることは、基本的にドキュメントの内容を要約したものになります。
(日本語に対応しているかわからなかったので一応英語にしました。)
```dodeuce.jsonl
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"Do Deuce was born on May 7, 2019, and bred at Northern Farm in Abira, Hokkaido, Japan. His sire is Heart's Cry and dam is Dust and Diamonds."}]}],"prompt":{"content":[{"text":"Please tell me about Do Deuce's breeding and pedigree."}]}}]}
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"Do Deuce's major G1 victories include the 2021 Asahi Hai Futurity Stakes, 2022 Japanese Derby (Tokyo Yushun), 2023 Arima Kinen (Grand Prix), and 2024 Tenno Sho (Autumn) and Japan Cup, totaling five G1 wins."}]}],"prompt":{"content":[{"text":"What are Do Deuce's major G1 victories?"}]}}]}
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"Do Deuce's racing style is characterized by coming from behind with a powerful late kick. He recorded the fastest final three furlongs in G1 history with 32.5 seconds in the 2024 Tenno Sho (Autumn)."}]}],"prompt":{"content":[{"text":"What is Do Deuce's racing style characteristic?"}]}}]}
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"Do Deuce's career record is 8 wins from 16 starts, with 8 wins from 13 starts in Japan and 0 wins from 3 starts overseas. His total earnings are 1,775,875,800 yen (1,753,479,000 yen in Japan, 22,396,800 yen overseas)."}]}],"prompt":{"content":[{"text":"What is Do Deuce's career record and earnings?"}]}}]}
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"Do Deuce became the seventh horse to win JRA G1 races in four consecutive years and the first male horse to achieve G1 wins from ages 2 to 5. He won the 2022 Japanese Derby in near-record time of 2:21.9 and set a G1 record for the fastest final three furlongs of 32.5 seconds in the 2024 Tenno Sho (Autumn)."}]}],"prompt":{"content":[{"text":"What are Do Deuce's major achievements and records?"}]}}]}
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"Do Deuce and Equinox met three times, with Do Deuce winning once and losing twice. Do Deuce defeated Equinox by a neck in the 2022 Japanese Derby, but finished 7th and 4th behind Equinox in the 2023 Tenno Sho (Autumn) and Japan Cup respectively."}]}],"prompt":{"content":[{"text":"What is the head-to-head record between Do Deuce and Equinox?"}]}}]}
{"conversationTurns":[{"referenceResponses":[{"content":[{"text":"Over 2400m, Do Deuce has 2 wins from 5 starts. He won the 2022 Japanese Derby in 2:21.9 (near track record), finished 4th in the Prix Niel and 19th in the Prix de l'Arc de Triomphe that same year, came 4th in the 2023 Japan Cup, and won the 2024 Japan Cup in 2:25.5."}]}],"prompt":{"content":[{"text":"What is Do Deuce's record at 2400 meters?"}]}}]}
```

そしてこのjsonlファイルをS3へ格納します。
ついでに、評価結果を格納するためのフォルダも作成しておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/835f13af-da8e-b87f-e13f-cd0b77822bdc.png)

そしてどうやらCORSの設定も必要になるみたいなので、S3バケットの「アクセス許可」タブから以下のコードを記述しておきましょう。
```cors.json
[
    {
        "AllowedHeaders": [
            "*"
        ],
        "AllowedMethods": [
            "GET",
            "PUT",
            "POST",
            "DELETE"
        ],
        "AllowedOrigins": [
            "*"
        ],
        "ExposeHeaders": [
            "Access-Control-Allow-Origin"
        ]
    }
]
```

https://docs.aws.amazon.com/bedrock/latest/userguide/model-evaluation-security-cors.html

## ナレッジベース評価基盤の構築
では本題の評価基盤です。Bedrockコンソールへ移動しましょう。

画面左のメニュー一覧から「Evaluations」を選択し、「Knowledge Bases」の評価を作成していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/e2c63f53-2ff2-4e17-6088-48278b2170e4.png)


### Baseline RAGの評価基盤構築
まずは評価の名前と、評価を行うLLMを指定します。ここではClaude 3.5 Sonnetに評価してもらうことにします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/c2c0615f-2693-e16d-6240-8bc38ea0c4ee.png)

続いては、ナレッジベースのどこを評価するのか選択します。
検索部分のみ or 検索と出力両方 のどちらかを選んで評価対象とすることができます。今回は両方対象とします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/997a834d-9dab-2ae9-8406-6f966a445719.png)

次は以下の中から任意の評価指標を選択します。
- 品質(Quality)
    - 有用性(Helpfulness)
質問に対する回答がどれだけ有用で総合的であるかを測定します。
    - 正確さ(Correctness)
質問に対する回答がどれだけ正確であるかを測定します。
    - 論理的一貫性(Logical coherence)
回答に論理的なギャップや矛盾がないかどうかを測定します。
    - 忠実性(Faithfulness)
応答の正確性と、応答がハルシネーションをどの程度回避できるかを測定します。
    - 完全性(Completeness)
質問のすべての部分に回答がどの程度完全であるかを測定します。
- 責任あるAI
    - 有害(Harmfulness)
憎悪、侮辱、暴力を用いた反応がどれほど有害であるかを測定します。
    - 拒否(Refusal)
質問に答えることを拒否する回答がどれだけ回避的であるかを測定します。
    - ステレオタイプ化(Stereotyping)
回答における個人またはグループに関する一般化された記述を測定します。

今回はせっかくなので全部やりましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/d95d6612-d175-1a06-049d-750e5020202b.png)

次に、データセットと評価結果を格納するS3 URIを指定します。先ほど作成したやつですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/76b9407c-552d-26f9-710f-760232cbab45.png)

最後にサービスロールを作成します。ここでは新規作成することにします。
ロールを作成すると、「既存のロールを使用する」の選択に自動で変わります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/ccd418a7-a1e6-3cb1-2aac-449a9bee4933.png)

実際に必要なロールとしては以下の通り。

https://docs.aws.amazon.com/bedrock/latest/userguide/rag-eval-service-roles.html

ここまで来たら作成しましょう！

:::note warn
Aurora Serverless v2を使用している際の注意事項です。

1.自動停止している状態で評価基盤を作成しようとするとエラーが発生します。
ちょっと待って再実行すれば作成できるようになると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/0dfc6c17-2aa2-186f-4e81-227de5ee52dc.png)

2.**Aurora Serverless v2の自動停止時間が15分などになっていると、評価中にAuroraが止まってしまい、Statusが進行中から変わらなくなってしまいます**。
この場合、もう一度評価基盤を作り直さないとダメなので要注意です。

私の場合は1時間等にしておけばOKでした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/58ad1a45-d60a-6553-8dbd-755057ee9a93.png)

:::

### GraphRAGの評価基盤構築
手順としては同じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/ade764ee-180b-b833-89f4-72eda29bbf30.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/8a910a46-5937-6af2-585c-ccc6d8d94151.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/f93df18c-81c5-5c62-9a57-adbb7ce89e85.png)

実際の運用のときは、データソースバケット・評価結果格納バケットは分けておいたほうが良さそうですね。
ここでは同じにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/46dc886d-48a5-fd9e-971c-311f1933c50b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/f4411de8-727e-6cbe-777b-0e4be0830a9a.png)

Neptune Analyticsは自動停止機能がないので、Aurora Serverlessの時のような心配はしなくてOKです。

ただ、私の環境だと構築完了するまでに死ぬほど時間がかかりました…1時間経っても終わらないことありますか…？(結局1時間半かかりました)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/68e9dc2c-ed24-0c04-3ab3-79f030c240f0.png)


## 2つの評価基盤を比較する
作成した2つの評価基盤を選択した状態で、「Compare」を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/d2672b0c-0126-78eb-b945-89e7c2a89aed.png)

すると2つを比較したグラフが出てきて、一目で特徴がわかるようになっています。
評価指標の各スコアは0から1の間で数値化され、1に近いほど良いとされています。

全体的にBaseline RAGの方が良い結果となっていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/1b6a4edc-8122-0499-5f4e-92ec65016aab.png)

また、評価指標1つ1つの出力精度を比較することもできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/de7822d0-200d-b26e-69da-4f244696fe6e.png)

### もっと細かく結果を見る
今度は個別に1つずつ見ていきます。

Baseline RAGの評価としては、論理的一貫性(Logical coherence)と忠実性(Faithfulness)がかなり高いですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/1e18be3e-59c6-4f12-a60e-1c4fbcd8c9e8.png)

そして、GraphRAGの評価としても同じように論理的一貫性と忠実性が高い結果となりました。
全体的にBaseline RAGの方が優秀な結果になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/1650a74b-921c-6d64-a05c-fc36702b514c.png)

そして各評価指標ごとのスコア分布を確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/9cc07716-976e-ced2-ab41-736fd774e724.png)

その下に、先ほど作成したデータセットごとの評価がなされています。
- Conversation input：先ほど作成したデータセットの質問
- Generation output：ナレッジベースが生成した回答
- Ground truth：先ほど設定した理想の回答

こうやって1つ1つの質問に対して明確に数値と実際の出力を元に評価を出してくれると、LLMOpsをする上ではかなりやりやすそうですよね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/d2a7dc1f-d3c7-ba71-eed2-bb6992adf75e.png)

と、思っていたのですが、**よくよく見ると「なんか精度おかしくない？」と思うことがありました。**

例えばBaseline RAGにおけるConversation6が典型的なのですが、「答えが見つかりませんでした」と言っているのに、忠実性(Faithfulness)では評価スコアが1になっています。

忠実性とは、取得したテキストチャンクに対するハルシネーションを避けることを意味しています。

しかしここでは、「質問に該当する答えが存在しません」と返ってきています。これって立派なハルシネーションだと思うのですが…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3616210/43f45aaa-b984-5c4e-1d9e-0ccb6fa289bf.png)

以上を踏まえて、各質問に対する評価がどうなっているかは一度人間の目で確かめる必要がありそうです。
(LLMの評価をどう評価するか問題の解消は難しそうですね…)

# まとめ
プレビュー版ですが、LLMOpsを推進できそうな機能が追加されたのは喜ばしいことです！

**ただ、まだ正直実用的ではないようにも感じています。**
それは評価の精度的にも、評価基盤を作る時間的にも、です。
基盤作るのに1時間半は待ってられない…他のサービスもこんなものなのでしょうか…

プレビュー版とのことなので、GAされる頃にはもっと精度良くかつ使い勝手の良いものになっていれば良いですね〜
良い機能ですし個人的にはすごく気になるので、今後もウォッチしていきます！