Flowise + VectaraでPDFなどからRAGを活用したQnAアプリを作成する

# はじめに

## FlowiseAIとは

Flowiseは、GUIを活用したAIアプリケーションを作成することができるツールです。最近、ノーコードという文脈で様々なツールが展開されていますが、FlowiseAIもコーディングを必要とせず、機能と機能をラインで接続するだけで、様々な機能を作成することが可能となっています。YCombinatorからも出資を受けているそうで、これからが楽しみなサービスとも言えます。

https://www.restack.io/docs/flowise-ai-knowledge-flowise-ai-yc-product

## RAGとは何か

GPTなどをはじめ、言語モデルは学習した情報をもとに回答を生成していますが、一般に公開されていない情報や、知識、概念について答えるための手法として、Fini-tuning、RAG（Retrieval-Augmented Generation）という手法など様々な手法があります。Finie-tuningはすでにある言語モデルを再学習する手法ですが、一般的には、言語モデルそのものをカスタマイズするため、大量のデータが必要で調整が難しいという点や、モデルの規模が大きくなるなどデメリットも存在します。LoRA（Low-Rank Adaptation（低ランク適応）と呼ばれる少ない計算量でファインチューニングするような手法も存在していますが、よりシンプルに実装するためには、RAG（Retrieval-Augmented Generation（検索拡張生成）と呼ばれる仕組みを使うことが一般的です。RAGでは、ユーザーの質問内容にLLMが答えられそうな情報ソースを小さいサイズの塊として抽出して活用する仕組みである「ベクトル検索」と呼ばれる技術が活躍します。

# RAGの得意不得意

RAGはベクターデータにした知識データベースを活用することで、意味として近いものを取り出すことは得意ですが、関係が緩いものなどを取り出すことは非常に難しいと言えます。〜に関係しているものは何か？や、〜は〜によってどうなったか？など、回答につながる言葉などが質問に含まれていないケースでは、偏りのある結果が得られてしまうなどの問題点があります。それらを計結するために、2024.2にMicrosoftから発表されて、2024.7にGithubに公開されたことで注目されているGraphRAGなどの仕組みを活用することなどによって実装が可能となります。

## RAGを使う流れをPythonでみてみる(ここではChromaをDBとして利用しています)

0.事前準備

```python
!pip install langchain=="0.1.14" langchain-openai=="0.1.1"
!pip install gpt_index==0.4.1
!pip install --upgrade chromadb
```

```python
import os
from google.colab import userdata
os.environ['OPENAI_API_KEY'] = userdata.get('OPENAI_API_KEY') # Set your openai-api-key
from langchain_openai import ChatOpenAI
from langchain.document_loaders import TextLoader, DirectoryLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings.openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
```

```python
from google.colab import drive
drive.mount('/content/drive')
%cd "drive/My Drive/demodata"
%ls
```

1.PDFやHTMLなど各文書からテキスト抽出を行い、ページ単位や一定の文字数毎などにテキストを分割し、テキストチャンクを作ります。(チャンクに分割する理由は、LLMのトークン制限に引っかからないため)

```python
loader = DirectoryLoader(
    "/content/drive/My Drive/demodata/",
    glob="**/*.txt",
    loader_cls=TextLoader,
    loader_kwargs={'autodetect_encoding': True}
)
data = loader.load()
print(data)

text_splitter = CharacterTextSplitter(
    separator='\n\n',
    chunk_size=900,
    chunk_overlap=0,
    length_function=len
)
documents = text_splitter.create_documents([doc.page_content for doc in data])
```

2.テキストチャンク毎にEmbeddingします。OpenAIのtext-embedding-ada-002などによって、各テキストチャンクは、1536次元のベクトルデータに変換されます。変換された1536次元のベクトルデータは、その生成元となったテキストチャックとペアでDBに保存され、のちの検索に備えます。

```python
with open("/content/drive/My Drive/demodata/output.txt", "w", encoding="utf-8") as file:
    for text in documents:
        file.write(text.page_content)
        file.write('\n--------------------------------------\n')

db = Chroma.from_documents(
    documents=documents,
    embedding=OpenAIEmbeddings(),
    persist_directory='testdb'
)
if db:
    db.persist()
    db = None
else:
    print("Chroma DB has not been initialized.")
```

3.ユーザから質問文が入力されると、質問文を事前準備の時と同じ方法でEmbeddingし、質問文のベクトルデータを取得する。
事前準備で作成したDB中の各ベクトルデータの中から、質問文のベクトルデータに近いものを上位N個抽出します。(LangChainでは、デフォルトでN=4が設定されています)

```python
embeddings = OpenAIEmbeddings()
loaded_db = Chroma(
    persist_directory='/content/drive/My Drive/demodata/testdb',
    embedding_function=embeddings
)

```

4.N個の抽出されたベクトルデータの作成元となる各テキストチャンクを参照。これらN個のテキストチャンク、および、『これらを参照してユーザの質問文に回答する』ことを指示するプロンプトをLLMに入力し、LLMから回答を得ます。
(LangChainでは、引数chain_typeで回答作成時のN個のテキストチャンクの利用方法をRetrievalQAのchain_type引数で指定可能。chain_typeには、"stuff", "map_reduce", "map_rerank", "refine"のいずれかを指定する)

```python
LLM = ChatOpenAI(
    model_name='gpt-3.5-turbo',
    temperature=0
)
retriever = loaded_db.as_retriever()
qa = RetrievalQA.from_chain_type(
    llm=LLM,
    chain_type='stuff',
    retriever=retriever,
    #chain_type_kwargs=chain_type_kwargs
)
result = qa("--質問--")
print(result["result"])


```

|title  |description  |
|---|---|
|Refine|すべての文書に順番にアクセス。それぞれの文章を評価しながら反復的に更新|
|Map Reduce|各文章にLLMを個別に適用、その出力を新しい文章として活用|
|Map Rerank|各文章に初期プロンプトを実行し、答えに対する確信度をスコアとして出力する|


## 続々と登場しているベクター形式のデータストア

AI関連の仕組みが数多く展開されていく中で、様々なSaaSなどが登場しています。中でもベクトル型のデータストアは、RAGなどの構築には欠かせず、非常に多くのSaaSサービスが存在しています。下記に一部をまとめてみます。

https://medium.com/google-cloud/vector-databases-are-all-the-rage-872c888fa348


|title  |description  |
|---|---|
|pinecorn  |インフラストラクチャのメンテナンス、サービスの監視、アルゴリズムの問題の修正といった面倒な作業を行うことなく、AIソリューションを開始、管理、強化することができる  |
|chromaDB  |アプリケーションの開発を簡素化するために設計されたオープンソースの埋め込みデータベース
|Weaviate|オープンソースのベクトルデータベースで、データオブジェクトとMLモデルからのベクトル埋め込みを簡単に保存できる|
|Qdrant|ベクトルの類似性検索を簡単密接に関連する高次元ベクトルの検索を容易にするマッチング、検索、レコメンデーションのような様々なタスクのための堅牢なアプリケーションに変換することが可能|
|Faiss|Facebook AI Researchによって開発された高速で高密度なベクトル類似性検索とグループ化の課題を解決するオープンソースライブラリ。RAMの容量を超えるようなものも含め、さまざまなサイズのベクトル集合を検索する方法を提供|

## Vectara
 
今回はその中でも、Vectaraと呼ばれるベクトル型データベースシステムを活用してみたいと思います。

https://www.aiforum.asto-system.co.jp/news/vectara%E3%80%81rag%E6%8A%80%E8%A1%93%E3%81%A7%E6%96%B0%E3%81%9F%E3%81%AAllm%E3%80%8Cmockingbird%E3%80%8D%E3%82%92%E7%99%BA%E8%A1%A8/

上記の記事にもあるようにVectoaraは2022年10月頃から、RAGの元となるグラウンド検索という技術を扱うプラットフォームとして提供を開始しているそうです。(企業の知識ストアからグラウンド、または参照されること)


# 手順

FlowiseAIとVectaraを活用して簡単なRAGを活用したQnAアプリのデモを作成したいと思います。

## FlowiseAIをGihtubCodespaceで実行

https://github.co.jp/features/codespaces

Githubのcodespaceを活用します。
様々な実行方法がありますが、私自身は最近よくGithubのCodespaceを使っているので、CodeSpaceから使ってみます。CodeSpaceを活用することでipadなどから、いつでも起動してプログラミングができる点、Githubとの連携は事前準備などが何もいらないため、最近とても活用しています。

![IMG_0089.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/76200733-da5e-8029-cd7d-f7a7c7431a42.jpeg)

codespaceが実行できたら下記のようになるので、マニュアル通りに

![IMG_0104.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/6e154f14-f2e6-3d4b-3891-42b5aa75afb8.jpeg)

```ppython
npm install -g flowise
npx flowise start
```

を実行して、ブラウザを開くとFlowiseが利用できるようになります。
(右下のブラウザで開くを押下して、FlowiseのGUI画面に移動できます）

![IMG_0078.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/7fbd2527-4376-be9a-31d1-d94bdb69da66.jpeg)


## VectaraでRAGを作成する

Vectara自体は、SNSなどのアカウントを使って簡単にサインアップできます。利用料も無料プランが用意されているので、アカウントだけ用意してあげれば簡単に使うことが可能です。RAGにどのようなデータを入れれば良いのかなどについては、様々な議論があり、別途工夫をする必要がありますがここでは割愛します。

https://x.com/k_ishi_ai/status/1827888822056866035?s=46&t=JtvQjRHBlMQomyjr2rtJGg


### 1.VectaraでCorpusを作成する

![IMG_0099.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/9befdb2a-ee62-d9a7-4de6-837444f188ec.jpeg)

### 2.PDFやテキストなどのデータを入れる

![IMG_0101.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/3d0527d6-08ae-57df-dce5-6cea3715fb22.jpeg)


### 3.外部から利用できるようにAPIキーを作成しておく

![IMG_0103.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/e026789b-c366-84b3-9f99-a2faff72ba2d.jpeg)


## FlowiseAI

### 1.FLowiseAIで下記のようにアプリケーションを作成する

![IMG_0095.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/156e029f-2b0c-e00b-4d13-0396f583665e.jpeg)

### 2.FlowiseAIのVectaraにAPIキーなどの情報を入力する

![IMG_0106.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/525ec46a-d3db-fc33-5983-ad48ed815faf.jpeg)


### 3.実際に使ってみる

![IMG_0097.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/43766/fd713dc5-4e95-3570-7b89-6568e4f529d9.jpeg)


# 実行する


# RAGの精度を上げるために

RAGの精度を上げるためには、RAGを作る際に、データを加工する前処理が必要となります。前処理としては下記の手法が一般的です。データクレンジングのためには、AWSGlueDataBrew、AzureDataFactoryなど既存のサービスを利用する方法から、独自にコードを作成して行う手法など様々なものがあります。

|title  |description  |
|---|---|
|データクレンジング  |記号の削除、重複の削除  |
|正規化  |表現の揺れを統一  |
|エンベディング  |文章の文脈を保ったままベクトルに変換する  |
|アノテーション  | 文章の内容を分類してタグをつける  |