【生成AIアプリのためのベクトル検索】⑤　RAG AIアプリケーションの実装〜ナレッジベースの作成

# はじめに

## 背景 〜 ベクトルデータベースとしてのApache CassandraとDataStax Astra DB

2022年からの生成AIの隆盛に対して、[Apache Cassandra](https://cassandra.apache.org/_/index.html)に対して、[DataStax](https://www.datastax.com/jp)エンジニアにより[ベクトル検索機能の拡張提案](https://cwiki.apache.org/confluence/display/CASSANDRA/CEP-30%3A+Approximate+Nearest+Neighbor%28ANN%29+Vector+Search+via+Storage-Attached+Indexes)が提案され、DataStax社のCassandraマネージドサービスである[Astra DB](https://www.datastax.com/lp/astra-registration)で利用可能になっています。
本記事シリーズは、このような動向の一環として同社から発表されたホワイトペーパーの内容に基づきます。

## 出典と日本語版

このシリーズ記事の原典は、以下で入手可能です。

https://www.datastax.com/resources/whitepaper/vector-search-for-generative-ai-apps

ブログ記事では、忠実な訳出ではなく、読みやすさを重視して、一部省略しています。
省略のない日本語版のホワイトペーパーの入手を希望される方はinfo-jp@datastax.comまでご連絡いただければ、提供させていただきます。

# RAG AIアプリケーションの実装〜ナレッジベースの作成
検索拡張生成（RAG）を用いた生成AIアプリを構築するための、基本的なフロー（図 3）とアーキテクチャダイアグラム（図 3.1）を紹介しました。ここでは、それらをさらに詳しく見てみましょう。
RAGシステムを構築するには、次の2 つのワークフローが必要です。

 - ナレッジベースの作成
 - 生成プロセスの実行

以下、それぞれについて見ていきます。
## ナレッジベースの作成
まず、RAGアプリケーションがアクセスする、エンベディングデータからなるナレッジ ベースの構築について見てみましょう。このワークフローには複数のステップがあり、その概要を図4に示します。

#### 図4. データセットをベクトルストアに変換するワークフロー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/2cdc55d8-5572-8f39-72b9-a3bf8ac9818b.png)



### 1. プロプライエタリデータの特定

初めのステップは、どのようなアプリケーションを構築するかによって異なってきます。まず、アプリケーションで使用できそうなデータセットを検討することになりますが、その際には、そのデータソースはどのような固有の情報を提供するか？、エンベディングに利用できる公開データセットはあるか？、といった点を検討する必要があります。

公開データセットの中には、LLM構築時の トレーニングに用いられたデータセットよりも新しいデータが含まれている可能性があり、新たに、そうしたデータセットを用いることも考えられます。たとえば、最近のニュース記事や最新の開発者用ドキュメントといったものがあります。


### 2. エンベディングモデルの選択

エンベディングモデルの選択において、ナレッジベース用のエンベディングモデルと最終的な応答のテキストを生成するLLM内で採用されているエンベディングモデルは、同じである必要はありません。また、ナレッジベース用のエンベディングモデルとLLMを、同じベンダー（またはオープンソースのモデルプロバイダー）から選択する必要さえありません。ただし、**ナレッジベースを構成するデータセットの作成と、そのデータセットに対するクエリの両方に、同じエンベディングモデルを用いることが重要**です。


### 3. データ設計

ベクトルデータそのものの利用だけでは十分でなく、ベクトルデータに関連するデータを含めてデータセットを設計することが必要である、いくつかの理由があります。

まず、ベクトル検索で一致する上位数件を取得した後、LLM に渡すプロンプトでは、それらのエンベディングデータに対応するエンベディング前のテキストを利用することになります。この**エンベディング前のテキスト** をベクトルデータ（エンベディング後のデータ）と一緒に保存します。

エンベディングは通常、「チャンク」と呼ばれる、テキストドキュメントを分割したセクションに対して行われます。ユースケースやデータセットの種類によっては、各チャンクと一緒にドキュメントの全文を保存することが有効な場合があります。この判断には、データセットそのものに関する知識が必要です。ドキュメントが非常に長く、多くのトピックについて説明している場合は、チャンクのテキストを使用する方が、それぞれのチャンクが別のクエリへ関連する可能性が高く、より合理的です。一方、文書がすべて特定のトピックに関するものであることがわかっている場合、完全な文書を利用する方が、LLMにとって有益な詳細情報が含まれる可能性があります。

その他の種類のメタデータとして検討の価値があるものとして、**そのデータを一意に識別するためのID**があります。ドキュメントIDは、ソースドキュメントの変更によりエンベディングデータを更新する必要があるかどうかを確認するときに利用できる場合があります。

また、**ベクトル検索を実行したいデータセットのサブセットを選択するためにフィルターできるメタデータ**を追加することも役立つ場合があります。簡単な例としては、「製品ドキュメント」や「社内 Wiki」など、さまざまなデータソースのカテゴリー情報を利用することが考えられます。 例えば、「社内 Wiki」には特定のユーザーと共有したくないデータが含まれている場合があるため、そのソースを検索結果から除外する、といった風にこのカテゴリー情報を利用することができます。


### 4. エンベディングデータの生成と保存

次に、エンベディングモデルを用いてベクトルを生成するプロセスを検討します。

このプロセスの最初のステップは、**テキストのチャンク化** です。これは、テキストをより短い文字列に分割する方法を決定するプロセスです。これは主に次の２つの理由から行われます。

まず、エンベディングモデルには入力として受け入れることのできる**テキスト量の制限**があるためです。

また、チャンク化要否の判断は、モデルが持つ制限以外にも、データ ソースとアプリケーションの両方の面からも検討する必要があります。アプリケーションがドキュメント全体をユーザーに返す必要がある場合、モデルが許す限り、できるだけ大きなチャンクを用いることは合理的です。あるいは、ドキュメント全体よりも、その中の数センテンス程度の短いチャンクの方が、一つのトピックに関する事実やアイデアを扱っている可能性が高く、例えば質疑応答システムのようなアプリケーションでは、より適しています。

さて、チャンクの長さに対するアプローチが決まったら、次は**チャンク化に関する戦略**を立てる必要があります。これには多くの方法があります。

最も簡単な方法は、**テキストを均等な長さのチャンクに分割**することです。この場合、重要な事実を説明している途中でチャンクが単語や文を分割してしまうことが生じる可能性があるため、チャンクのオーバーラップを許容すると文書内の概念を適切に捉えるのに役立ちます。

より高度な、広く用いられている戦略として、**言語の構造に基づいたチャンク化**があります。例としては、ドキュメントのセクション、段落ごと、またはセンテンスの境界を基準にしたチャンク化が挙げられます。
ドキュメントのチャンクのセットを作成したら、各チャンクのベクトルを、先の手順で定義した関連メタデータと共に、データ ストアに保存します。

#### DataStax Astra DB

DataStaxのApache CassandraマネージドサービスであるAstraDB は、ベクトル検索の機能を有しており、大規模なベクトルデータセットを操作する場合の優れた選択肢となります。そしてまた、AstraDBのベクトル検索機能は、高い可用性と柔軟なスケールを有した分散データベースApache Cassandraの拡張機能であるため、ベクトルとともに、そのベクトルに関連するメタデータを保存しておくことも容易であり、ベクトル検索のみではなく、メタデータを使った操作との組み合わせについても、利点を持っています。このような大規模データ利用のための信頼性・スケールと、データベースとしての汎用性は、AstraDBの持つ、単なるベクトルデータベースとは異なる利点です。


### 5. エンベディングデータの更新

企業が保有するデータは、時間の経過とともに変化していきます。ハルシネーションを避けるためには、新しいデータソースが利用可能になったらすぐに、エンベディングデータを更新することが重要です。

データ ソースの変更が発生するケースとして、新しいドキュメントが利用可能になった場合、また、過去にエンコード済みのコンテンツが更新された場合があります。

既存のドキュメントのエンベディングデータを更新するときは、企業が保有するデータの１オブジェクトに対して複数のエンベディングが登録されている可能性があることに注意する必要があります。データソースがテキストである場合、これはテキスト全体の長さと選択したチャンク化の戦略によって異なります。エンベディングデータを更新するときは、更新時にそのドキュメントから派生した、すべてのエンベディングデータを置き換えるように注意する必要があります。

また、すべてのエンベディングモデルの変換ロジックが決定論的であるわけではないことを覚えておくことは有益です。モデルによっては、同じテキストをエンコードする都度、異なる結果が得られる場合があります。結果として得られるベクトルは通常、類似したものにはなるでしょうが、既存のエンベディングデータを新しいエンベディングデータに置き換えるときには、置き換えるべき既存のエンベディングデータの特定のために、エンベディングベクトルの生の値を用いることは得策ではありません。代わりに、一意の識別子を使うことが考えられます。