はじめに
日本語の文章を扱うRAG(Retrieval-Augmented Generation)システムを、Windows 11環境で構築したときの備忘録です。
主な構成は以下の通り:
- LLM: Ollama(TokyoTech Swallow 8B)
- Embeddingモデル: mxbai-embed-large
- ベクトルデータベース: PostgreSQL + PGVector
- ドキュメント: 日本語のpdf(検証用の創作物語)
1. 環境構築(Ollama, PostgreSQL + PGVector)
Ollama や PostgreSQL + PGVector の導入手順は以下の外部記事を参考にしました。
Ollamaのインストール方法
PostgreSQL + PGVector のインストール方法
使用モデル:
-
LLM:
hf.co/mmnga/tokyotech-llm-Llama-3.1-Swallow-8B-Instruct-v0.3-gguf:latest
-
埋め込みモデル:
mxbai-embed-large
データベース設定(例):
データベース名:postgres
ユーザー名:postgres
パスワード:pass
2. 必要モジュールのインストール
以下のコマンドで必要なパッケージをインストールします:
pip install langchain langchain-community langchain-postgres langchain-ollama
pip install "psycopg[binary]" unstructured[all-docs]
3. コード①:PDFの読み込みとベクトルデータベースへの登録
日本語PDFを読み込み、テキストを分割し、Ollamaでベクトル化した上で、PGVectorに保存します。
from langchain.document_loaders import UnstructuredFileLoader
from langchain_text_splitters import CharacterTextSplitter
from langchain_postgres import PGVector
from langchain_ollama import OllamaEmbeddings, ChatOllama
# LLMの初期化(Ollamaに接続)
llm = ChatOllama(
model="hf.co/mmnga/tokyotech-llm-Llama-3.1-Swallow-8B-Instruct-v0.3-gguf:latest",
base_url="http://localhost:11434",
temperature=0,
seed=0,
)
# PDFファイルの読み込み(日本語対応・ページ単位)
loader = UnstructuredFileLoader("./test.pdf", mode='paged', languages=['ja'])
documents = loader.load()
# テキストをチャンク(分割)に分ける
text_splitter = CharacterTextSplitter(
chunk_size=600, # 1チャンクあたりの文字数
chunk_overlap=20, # チャンク同士の重複(文脈保持のため)
separator="\n" # 改行で分割
)
docs = text_splitter.split_documents(documents)
# Embeddingモデルの設定
embeddings = OllamaEmbeddings(model='mxbai-embed-large')
# PostgreSQL + PGVector に接続
connection = "postgresql+psycopg://postgres:pass@localhost:5432/postgres"
collection_name = "test"
# インデックスを新規作成
vectorstore = PGVector(
embeddings=embeddings,
collection_name=collection_name,
connection=connection,
use_jsonb=True,
pre_delete_collection=True,
)
# ドキュメントをベクトルDBに追加
vectorstore.add_documents(docs)
📌補足:
PyPDFLoader ではpdfファイルによっては文字化けが発生したため、UnstructuredFileLoader を使用しました。
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader(file_path='test.pdf')
ちなみにpdfの中身は架空の人物"Kタロウ"の物語を適当に制作したもの。
📄 使用したPDFの中身(ばs)
知恵者Kタロウの物語。
昔々、京の都の西にある小さな山村に、「Kタロウ」という名の若者が住んでおった。
年は二十歳を少し超えたばかり。村の者たちは皆、...
🗄 データベースの中身
テーブル:langchain_pg_embedding に保存されます。
4. コード②:質問応答チェーンの構築と実行
保存したベクトル情報を使い、LLMを通じて質問に答える処理を実装します。
from langchain_postgres import PGVector
from langchain.chains.retrieval import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain_ollama import OllamaEmbeddings, ChatOllama
from langchain import hub
# LLMの設定(前と同様)
llm = ChatOllama(
model="hf.co/mmnga/tokyotech-llm-Llama-3.1-Swallow-8B-Instruct-v0.3-gguf:latest",
base_url="http://localhost:11434",
temperature=0,
seed=0,
)
# Embeddingの再設定
embeddings = OllamaEmbeddings(model='mxbai-embed-large')
# PGVectorに接続
connection = "postgresql+psycopg://postgres:pass@localhost:5432/postgres"
collection_name = "test"
vectorstore = PGVector.from_existing_index(
embedding=embeddings,
collection_name=collection_name,
connection=connection,
use_jsonb=True
)
# LangChain Hubから質問応答用のプロンプトテンプレートを取得
retrieval_qa_chat_prompt = hub.pull("langchain-ai/retrieval-qa-chat")
# ドキュメントを統合するチェーンを作成
combine_docs_chain = create_stuff_documents_chain(llm, retrieval_qa_chat_prompt)
# RAGの質問応答チェーンを構築
retrieval_chain = create_retrieval_chain(vectorstore.as_retriever(), combine_docs_chain)
# 質問を投げる
res = retrieval_chain.invoke({"input": "Kタロウに関して教えてください。"})
# 回答を出力
print(res["answer"])
得られた回答は以下の通り。pdfの内容を参照して回答してくれていることを確認しました。
✅ 出力結果
Kタロウについて、以下の情報が分かっています。
* **知恵者:** 村人から「知恵者K」と呼ばれるほど、頭が良く、問題解決能力に優れている。
* **謎の事件を解決:** 村の田畑に現れた謎の黒い影(実は夜猿)の正体を見破り、その原因と対策を提示した。
* **村のために尽力:** 村の発展のために、様々な難問を解き続け、「東山の賢者」として広く知られるようになった。...
6. おわりに
RAGの構築に際して、Ollama + LangChain + PGVectorの構成は思ったよりシンプルでした。ローカルで完結する点も安心材料です。GUIも試してみたいと思います。