Ragasを用いて自分のRAG環境を評価する

# 1. はじめに（動機）
- 業務で、社内ナレッジをチャットで自然言語検索するためのRAG環境を構築している。
- RAG環境はBedrock Knowledge Baseを用いている。
- 検索の際にどのLLMを使ったらよいかなどの客観的な評価を行いたい。例えば、Claude 3.5 Sonnet v1と Claude Instant v1.2 ではどれくらい回答精度に違いがあるのかを定量的に比較したい。

# 2. やったこと
- RAG環境をBedrock Knowledge Baseで作成する。
- Claude 3.5 Sonnet v1 及び Claude Instant v1.2 を用いて、質問と回答を生成する。
- Ragasの実行環境をEC2インスタンスに作成する。
- Claude 3.5 Sonnet v1 と Claude Instant v1.2の回答内容の精度をRagasで比較する。

# 3. 構成図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/189302/0198b7b1-1815-98d2-41c4-710270707099.png)

# 4. 手順
## 4.1 Bedrock Knowledge Baseの作成
- 評価の対象とするBedrock Knowledge Baseを作成する。
- 今回の本題ではないため、詳細手順は割愛する。Knowledge Baseの作成方法については、例えば「[AWSの生成AIで社内文書検索！ Bedrockのナレッジベースで簡単にRAGアプリを作ってみよう](https://qiita.com/minorun365/items/24dfb0ea3afde6ed0a56)」などを参照。
- 主な設定内容は以下の通り。
    - リージョン：us-east-1
    - ベクトルデータベース：Amazon OpenSearch Serverless
    - 埋め込みモデル：Titan Text Embeddings v2
    - 検索対象文書：以下のテキストファイル(架空の町の説明)1個のみ

``` 海月町.txt
以下は、架空の町「海月町（うみづきちょう）」の説明です：

位置：
海月町は、日本の四国地方、高知県の南西部に位置する海沿いの町です。太平洋に面し、温暖な気候と美しい海岸線が特徴です。

人口：
約23,000人（2022年現在）。夏季は観光客の流入で一時的に人口が増加します。

歴史：
江戸時代には小さな漁村でしたが、明治時代に良港として開発が進みました。戦後は観光業と水産業を中心に発展し、近年はマリンスポーツの聖地としても知られています。

有名人：
・波留幸介（はるこうすけ）：プロサーファー。世界大会で優勝経験を持つ海月町出身のサーフィンスター。
・月野みどり（つきのみどり）：ベストセラー作家。海月町を舞台にした小説で debut し、以来、故郷をモチーフにした作品を多く発表しています。

特産物：
・海月ジェラート：地元で水揚げされるクラゲを使った独特なジェラート。珍味として人気。
・深海塩：海月町沖の深海から汲み上げた海水から作る高級塩。
・月光珊瑚：海月町近海でとれる希少な青い珊瑚。ジュエリーとして人気。

交通：
最寄りの空港から車で約1時間。JR四国の駅があり、高知市から特急で約2時間。町内には路線バスとレンタサイクルサービスがあります。

事件：
2015年に起きた「海月ダイビング事故」が有名です。地元のダイビングスポットで外国人観光客が行方不明になる事故が発生。結果的に無事救助されましたが、この事件をきっかけに海洋安全管理の見直しが行われました。

スポーツチーム：
海月町を本拠地とするプロバレーボールチーム「海月ジェリーフィッシュ」があります。V.LEAGUE Division2に所属し、地元の熱烈なサポートを受けています。チームマスコットは青いクラゲのキャラクター「ジェリー」で、子供たちに人気です。
```

## 4.2 EC2インスタンスからのBedrock Knowledge Base へのアクセス
- 作業用のEC2インスタンスを作成し、Knowledge Baseにアクセスできるように設定する。
- EC2インスタンスのOSはAmazon Linux 2023を選択する。
- aws configureで、Bedrockへのアクセス権限を持つIAMユーザのAccesskey/Secretkeyを設定する。
- boto3をインストール。
```
[ec2-user@ip-10-0-0-146 ~]$ sudo yum install pip
[ec2-user@ip-10-0-0-146 ~]$ pip install boto3
```

- LLMとしてClaude 3.5 Sonnet v1を指定して、Knowledge Baseに質問する。

``` ragas01.py
import boto3
kb = boto3.client("bedrock-agent-runtime", region_name='us-east-1')

question = "海月町の特産品を教えてください。"

response = kb.retrieve_and_generate(
    input={"text": question},
    retrieveAndGenerateConfiguration={
        "type": "KNOWLEDGE_BASE",
        "knowledgeBaseConfiguration": {
            "knowledgeBaseId": "[KnowledgeBaseId]",
            "modelArn": "arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-3-5-sonnet-20240620-v1:0",
        },
    },
)
print(response)
```
- 応答内容は以下となる。（後でRagasで結果を評価する際に必要となる部分を抜粋）

|Ragasへの入力  |該当箇所  |Claude 3.5 Sonnet v1の出力値  |
|---|---|---|
|answer  |response["output"]["text"]| 海月町には3つの特徴的な特産品があります。\n\n1. 海月ジェラート：地元で水揚げされ るクラゲを使った独特なジェラートです。珍味として人気があります。\n\n2. 深海塩：海月町沖の深海から汲み上げた海水から作ら れる高級塩です。\n\n3. 月光珊瑚：海月町近海でとれる希少な青い珊瑚です。ジュエリーとして人気があります。\n\nこれらの特産 品は、海月町の海洋資源を活かした独自の製品として知られています。 |
|contexts |response['citations'][0]['retrievedReferences'][0]['content']['text']| 有名人：\r ・波留幸介（はるこうすけ）：プロサーファー。世界大会で優勝経験を持つ海月町出身のサーフィンスター。\r ・月野みどり（つきのみどり）：ベストセラー作家。海月町を舞台にした小説で debut し、以来、故郷をモチーフにした作品を多く発表しています。\r \r 特産物：\r ・海月ジェラート：地元で水揚げされるクラゲを使った独特なジェラート。 珍味として人気。\r ・深海塩：海月町沖の深海から汲み上げた海水から作る高級塩。\r ・月光珊瑚：海月町近海でとれる希少な青い珊瑚。ジュエリーとして人気。\r \r 交通：\r 最寄りの空港から車で約1時間。 |

- 同様に、上記のスクリプト(ragas01.py)のmodelArnの部分のみClaude Instant v1.2 のもの(arn:aws:bedrock:us-east-1::foundation-model/anthropic.claude-instant-v1)に変更して実行した結果が以下となる。

|Ragasへの入力  |該当箇所  |Claude Instant v1.2の出力値  |
|---|---|---|
|answer  |response["output"]["text"]|海月町の特産品は海月ジェラート、深海塩、月光珊瑚です。  |
|contexts |response['citations'][0]['retrievedReferences'][0]['content']['text']|有名人：\r ・波留幸介（はるこうすけ）：プロサーファー。世界大会で優勝経験を持つ海月町出身のサーフィンスター。\r ・月野みどり（つきのみどり）：ベストセラー作家。海月町を舞台にした小説で debut し、以来、故郷をモチーフにした作品を多く発表しています。\r \r 特産物：\r ・海月ジェラート：地元で水揚げされるクラゲを使った独特なジェラート。 珍味として人気。\r ・深海塩：海月町沖の深海から汲み上げた海水から作る高級塩。\r ・月光珊瑚：海月町近海でとれる希少な青い珊瑚。ジュエリーとして人気。\r \r 交通：\r 最寄りの空港から車で約1時間。 |

## 4.3 Ragasのインストールと設定
- [RAG精度評価の定番ツール「Ragas」にAWSのBedrockで入門しよう！（v0.2対応）](https://qiita.com/minorun365/items/2f4e238f8bbc6e393ba5)を参考に、各種資材のpip install及びRagasの実行スクリプトを作成する。

- 上記で作成したEC2インスタンスに、Ragas及び必要な資材をインストールする。
```
[ec2-user@ip-10-0-0-146 ~]$ pip install langchain langchain-core langchain-community langchain-aws
[ec2-user@ip-10-0-0-146 ~]$ pip install ragas datasets
[ec2-user@ip-10-0-0-146 ~]$ pip install nltk
```

- Ragasを実行するスクリプトを作成する。上記のQiita記事の「rag-eval.py」を参考にしてそのまま使わせて頂くが、Ragasへの入力値(question, answer, contexts)は、自分のKnowledge Baseに対してClaude 3.5 Sonnet v1 を用いて質問/回答を生成した内容を使用する。ground_truthは私の理想の回答を記載。

``` ragas02.py
# 外部ライブラリをインポート
from langchain_aws import ChatBedrockConverse, BedrockEmbeddings

from datasets import Dataset
from ragas import evaluate
from ragas.llms import LangchainLLMWrapper
from ragas.embeddings import LangchainEmbeddingsWrapper
from ragas.metrics import (
    answer_relevancy,
    faithfulness,
    context_recall,
    context_precision,
)

# LLMと埋め込みモデルを設定
llm = ChatBedrockConverse(model="anthropic.claude-3-5-sonnet-20240620-v1:0", region_name='us-east-1')
embeddings = BedrockEmbeddings(model_id="amazon.titan-embed-text-v2:0", region_name='us-east-1')

# 評価対象のデータセットを定義
dataset = Dataset.from_dict({
    "question": ["海月町の特産品を教えてください。"],
    "answer": ["海月町には3つの特徴的な特産品があります。\n\n1. 海月ジェラート：地元で水揚げされ るクラゲを使った独特なジェラートです。珍味として人気があります。\n\n2. 深海塩： 海月町沖の深海から汲み上げた海水から作ら れる高級塩です。\n\n3. 月光珊瑚：海月町近海でとれる希少な青い珊瑚です。ジュエリーとして人気があります。\n\nこれらの特産 品は、海月町の海洋資源を活かした独自の製品として知られています。"],
    "contexts": [["有名人：\r ・波留幸介（はるこうすけ）：プロサーファー。世界大会で優勝経験を持つ海月町出身のサーフィンスター。\r ・月野みどり（つきのみどり）：ベストセラー作家。海月町を舞台にした小説で debut し、以来、故郷をモチーフにした作品を多く発表しています。\r \r 特産物：\r ・海月ジェラート：地元で水揚げされるクラゲを使った独特なジェラート。 珍味として人気。\r ・深海塩：海月町沖の深海から汲み上げた海水から作る高級塩。\r ・月光珊瑚：海月町近海でとれる希少な青い珊瑚。ジュエリーとして人気。\r \r 交通：\r 最寄りの空港から車で約1時間。"]],
    "ground_truth": ["海月町の特産品は、海月ジェラート、深海塩、月光珊瑚です。"],
})

# 評価を実行
result = evaluate(
    dataset,
    metrics=[
        faithfulness, # 忠実性：背景情報と一貫性のある回答ができているか
        answer_relevancy, # 関連性：質問と関連した回答ができているか
        context_precision, # 文脈精度：質問や正解に関連した背景情報を取得できているか
        context_recall, # 文脈回収：回答に必要な背景情報をすべて取得できているか
    ],
    llm=LangchainLLMWrapper(llm),
    embeddings=LangchainEmbeddingsWrapper(embeddings),
)

# 評価結果を表示
print(result)
```
- 上記の実行結果は以下となる。

```
{'faithfulness': 0.9231, 'answer_relevancy': 0.9736, 'context_precision': 1.0000, 'context_recall': 1.0000}
```

- 同じスクリプトで、answer/contextの内容をClaude Instant v1.2 での内容に変更して実行した結果は以下となる。

```
{'faithfulness': 1.0000, 'answer_relevancy': 0.9727, 'context_precision': 1.0000, 'context_recall': 1.0000}
```

- 私の理想の回答(ground_truth)は、単純に3つの特産品を列記するだけの内容としたため、補足説明が多いClaude 3.5 Sonnet v1のほうが faithfulnessの値が低くなってしまった。

- 今回の記事では1つのデータセットでの比較しかしていないが、実際には複数のデータセットを用意して、トータルでの比較を行う必要がある。

# 5. 所感
- やっとなんとなくの雰囲気が分かったため、実際のデータの詳細検証を行っていきたい。