【生成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までご連絡いただければ、提供させていただきます。

# 【生成AIアプリのためのベクトル検索】⑥ RAG AIアプリケーションの実装〜生成プロセスの実行

ここからは、前のセクションでエンコードしたデータセットを使用して LLM からの応答を生成する方法を詳細に見ていきます。


図5. クエリから応答を生成するワークフロー

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/176567/1b61370c-91d3-e086-d989-5721e82b0085.png)



## クエリのエンベディング

RAG アプリケーションにおけるテキスト生成はクエリから始まります。クエリはアプリケーションのエンド ユーザーから送信されたものを、そのまま利用する場合もあれば、それを元にLLM またはその他の手続き型コードによって新たに生成する場合もあります。

次に、クエリのテキストエンベディングを行って、クエリをベクトルにエンコードします。

ここで使用されるエンベディングモデルが、ベクトル検索用のデータセットをエンベディングするために使用されたものと同じモデルであることが重要です。そうでない場合、ベクトル検索は正しく機能しません。


## ベクトル検索

### 検索結果取得件数
次に、ベクトルデータストアからクエリに最も類似したエンベディングデータを取得することになります。まず、クエリベクトルの近くにあるベクトルを探すため、最近傍検索を行います。多くの場合、この検索は「上位N件」の一致を求めることによって行われます。この数は、エンベディングで使用したチャンクの長さ、コンテキストウィンドウ（LLM に許可される入力のサイズ）、およびユースケース等に応じて調整することになります。

### 類似度と閾値
ベクトル検索を行う際に、検索結果のみでなく、クエリベクトルと検索結果ベクトルとの間の類似性についてのスコアまたは尺度を返すように要求することがあります。このスコアがしきい値を下回る場合、検索されたドキュメントのクエリに対する一致度が十分なものでないとして、後続のプロセスで、この検索結果ドキュメントを、次のステップでは利用しないという決定を行うことができます。使用する実際のしきい値は、アプリケーション、データセット、および類似性メトリックによって異なります。スコアの範囲が、最大値 を「1」として正規化されていると仮定する場合、カットオフの値として、「0.5 ～ 0.8」の間から実験を開始するのが適切と言えるでしょう。

### 検索結果のカットオフ

ベクトル検索により、 たとえば 数百、数千件規模の一致結果を取得すると共に、それぞれのスコアを得た上で、エンベディングの生成に使用された元テキストを使って選別を行うことで、適切なカットオフを、ベクトルデータストアに任せるのではなく、アプリケーションが決定することができます。結局のところ、ベクトルストアから数学的に導き出された結果に対して、クエリの本来の目的との関連性が保たれている地点を探すことが重要です。

クエリとの関連性の高い結果ベクトルのセットを取得したら、次のプロンプトエンジニアリングのステップで使用するために、それらのベクトルとともにメタデータとして保存されている関連テキストを取得します。


## プロンプトエンジニアリング

プロンプト エンジニアリングとは、LLM に与える命令を設計することです。一部の LLM は、プロンプトを1つだけ受け取ります。あるいは、チャットモデルの場合に典型的ですが、命令は、プロンプトとして定義する必要がある複数の異なる入力項目で構成されている場合があります。このようなプロンプトの各セクションについて、以下で個別に説明します。

1つ目は**コンテキスト**です。コンテキストは、モデルに何を行わせたいかを表します。例えば、「このコードについて説明しなさい」や「カスタマーサービスボットとして質問に答えてください」等です。

加えて、LLMに対して、いくつかの**回答のサンプル**を与える場合があります。これはFew-shot学習と呼ばれ、LLM に、ユーザーの意図をより良く理解させるのに役立ちます。チャット用LLM の場合は、通常、サンプルとして会話の両サイドの情報を提供する必要があります。具体的には、ユーザーからの質問と、AIから期待する応答の両方をテキストサンプルとして与えます。

RAG アプリケーションでは、LLM への命令の中に、取得済みのベクトルエンベディングのテキスト表現を挿入することになります。このパターンでは、次のような形式のインストラクションがLLMに対して与えられます。

```
 “Use the articles below to complete the task.

Article:
<ベクトル検索で最初に一致したテキストを挿入>

Article:
<ベクトル検索で2 番目に一致したテキストを挿入> “
```

先に触れたように、タスクの指示として、上記例のような一般的な表現（”complete the task”）ではなく、より具体的な記述を用いることもできます。質疑応答システムの場合であれば、例えば、「以下の記事の事実を、質問に答えるために使用しなさい」というコンテキストを用いることができるでしょう。

また、より実践的な改良として、ベクトル検索によって選ばれたドキュメントを使って回答を生成することを指示するだけでなく、そもそも、それらのドキュメントが依頼したタスクに関連しているかどうかについても推論するように、LLM に対して異なるレベルの指示を同時に与えることもできます。
たとえば、質疑応答システムでは、先に示したLLMに対する指示の表現を次のように変更できます。「以下のドキュメントの事実を、質問に答えるために使用してください。ただし、与えられたドキュメントが質問に答えるために有益でない場合は、『わかりません』と言ってください。」

結局のところ、LLM に実行を依頼するインストラクションまたはタスクを、ユーザーの意図が明確に示されたものとすることが重要です。


## 応答プロセス

上記の過程を経て、LLM から最終的なテキスト出力を受け取ることになりますが、ほとんどの場合、ここで、後処理が行われることになります。それは例えば、AI安全性分析、品質管理解析、そしてLLMからの応答に対してアプリケーションのユースケースに固有の付加的なフィルタリングやアジャストメントなどを組み込むというものです。多くのシステムでは、この後処理のステージを、生成された応答を、後で集合的にレビューするために、そのコピーをシステムに保存する機会としても使用します。 LLM応答の後処理が完了すると、最終的な応答がユーザーに返され、一連のRAG プロセスの全体が完了します。