はじめに
LangChainは、言語モデルと外部リソースを組み合わせて使用するための柔軟なフレームワークです。ここでは、LangChainを使用したRAG(Retrieval-Augmented Generation)の実装について以下の内容を説明します。
- 指定したドキュメントの読み込みと分割
- 分割したドキュメントのベクトルストアへの登録
- クエリ実行による類似文書の検索
主にLangChainライブラリを使用し、ベクトルストアにはChromaDBというオープンソースソフトウェア(OSS)を利用し、埋め込み生成にはAWSのBedrockサービスを用います。
こちらも参考にして下さい
筆者のレベル
- RAGについて基本的な理解を持っている
- RAGを実装することで、さらに詳しく学びたい
- プログラムを書く経験はあまりないが、RAGを実装してみたい
全体の構成
以下の流れでプログラムを構成しています。
- 必要なライブラリのインポート
- ドキュメントの読み込みと分割
- 埋め込みの作成
- ベクトルストアの作成
- ドキュメントの追加
- クエリの実行
1. 必要なライブラリのインポート
最初に、必要なライブラリをインポートします。これには、Boto3、ChromaDB、LangChainの各モジュールが含まれます。
import boto3
import chromadb
import pprint
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_aws import BedrockEmbeddings
from langchain_chroma import Chroma
2. ドキュメントの読み込みと分割
次に、ドキュメントを読み込み、指定されたサイズで分割する関数を定義します。LangChainでドキュメントをスプリット(分割)する際には、TextSplitterを使うことが一般的です。これは、長いドキュメントをチャンク(小さなセクション)に分割し、効率的に処理するための手法です。LangChainには、さまざまなTextSplitterクラスがあり、以下の目的に応じて使用されます。
- CharacterTextSplitter: 特定の文字数で分割する
- RecursiveCharacterTextSplitter: 文や段落単位で分割するが、文字数の制約も考慮する
NLP-based splitters: 自然言語処理に基づいて文を分割する
ここでは、CharacterTextSplitterを使用した例を示します。
text_splitter = CharacterTextSplitter(chunk_size=chunk_size, chunk_overlap=chunk_overlap)
source_text = TextLoader(file_path).load()
documents = text_splitter.split_documents(source_text)
print('\nソースデータの分割結果: \n')
pprint.pprint(documents)
3. 埋め込みの作成
次のステップでは、Bedrockを使用して埋め込みを作成する関数を定義します。この関数では、Boto3クライアントを利用してAWSのBedrockサービスにアクセスします。
embeddings = BedrockEmbeddings(
client=boto3.client("bedrock-runtime", region_name=client_region),
model_id=model_id,
region_name=client_region
)
4. ベクトルストアの作成
Chromaを使用してベクトルストアを作成する関数も定義します。この関数では、指定されたコレクション名と埋め込み関数を使用してベクトルストアを作成します。
clientは、ChromaDBへの接続を管理するクライアントオブジェクトを指定します。ここでは、PersistentClientを使用しており、データが指定されたパス(この場合は.tmp/)に永続的に保存されます。これにより、プログラムを再実行した場合でも、以前に保存したデータを引き続き利用可能です。
vectorstore = Chroma(
collection_name=collection_name,
embedding_function=embeddings,
client=chromadb.PersistentClient(path='.tmp/')
)
5. ドキュメントの追加
ドキュメントをベクトルストアに追加する関数を定義します。
vectorstore.add_documents(documents=documents, embedding=embeddings)
6. クエリの実行
指定したクエリを実行し、その結果を返す関数を定義することにより、類似の文書を検索します。
query:
ここで指定するのは、検索を行うためのテキストクエリです。このクエリに基づいて、ベクトルストア内の文書が比較されます。
k:
取得したい類似文書の数を指定します。例えば、k=3と設定した場合、最も類似している上位3つの文書が返されます。
result:
この変数には、検索結果が格納されます。通常、結果は類似文書のリストと、それぞれの類似度スコアのペアで構成されます。
result = vectorstore.similarity_search_with_score(query=query, k=k)
print('\n結果: \n')
pprint.pprint(result)
7. まとめ
LangChainを利用してRAGの実装を行うための基本的な流れを説明しました。具体的には、ドキュメントの読み込みと分割、埋め込みの生成、ベクトルストアへの登録、そしてクエリを通じた類似文書の検索について詳しく見てきました。LangChainとChromaDB、AWSのBedrockを組み合わせて実装することで、RAGシステム構築の理解が深まれば幸いです。