【Streamlit】 LangChainを使ったPDFドキュメント対応RAGチャット

Streamlit
この記事は約13分で読めます。

こんにちは、JS2IIUです。

ChatGPTに代表される生成AIの活用は急速に広がっています。チャット形式でも十分に威力を発揮してくれますが、一般的な内容の回答にとどまってしまいます。社内文書などの特定のドキュメントをベースに応答を生成するシステムを構成するのがRAGです。具体的な構成方法をみていきたいと思います。今回もよろしくお願いします。

1. RAG(Retrieval-Augmented Generation)とは

RAG(Retrieval-Augmented Generation)は、大規模言語モデル(LLM)に外部データソースを統合する技術です。通常のLLMは事前学習された知識に基づいて回答を生成しますが、RAGを使用すると以下のメリットがあります:

  • 最新情報の利用:LLMが学習していない最新データを回答に活用できる。
  • 専門知識の統合:ドメイン固有の文書を利用することで、より正確で詳細な回答を提供。
  • 回答精度の向上:関連情報を検索し、よりコンテキストに即した回答を生成。

RAGの仕組み

  1. データ取得(Retrieval):外部データソースから関連情報を検索。
  2. 生成(Generation):取得した情報を元にLLMが回答を生成。

このプロジェクトでは、PDF文書を対象にRAGを構成し、チャット形式で情報を検索・回答するシステムを構築します。

2. LangChainを使ったRAGの構成方法

LangChainは、RAGシステムを簡単に構築できるPythonライブラリです。以下の主要コンポーネントを使用します:

  • Document Loader:PDFなどの文書を読み込む。
  • Text Splitter:長文を小さなチャンクに分割。
  • Embeddings:テキストをベクトル表現に変換。
  • Vector Store:ベクトル化したデータを保存し検索を効率化。
  • LLM:OpenAI APIを使った大規模言語モデル。

RAG構築の流れ

  1. PDF文書を読み込む。
  2. テキストをチャンクに分割し、埋め込みを作成。
  3. FAISSを使って埋め込みをベクトルストアに保存。
  4. OpenAIモデルと連携し、ユーザーの質問に応答。

3. サンプルプログラム

Python
import streamlit as st
import os
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA

def process_pdf(uploaded_file, api_key):
    with open("temp.pdf", "wb") as f:
        f.write(uploaded_file.getbuffer())

    loader = PyPDFLoader("temp.pdf")
    documents = loader.load()

    text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=100)
    docs = text_splitter.split_documents(documents)

    embeddings = OpenAIEmbeddings(openai_api_key=api_key)
    vectorstore = FAISS.from_documents(docs, embeddings)

    return vectorstore

def initialize_chatbot(api_key):
    st.session_state.llm = ChatOpenAI(model_name="gpt-4", openai_api_key=api_key)
    st.session_state.vectorstore = None
    st.session_state.qa_chain = None

def main():
    st.set_page_config(page_title="PDF対応 RAGチャット", layout="wide")
    st.title("\ud83d\udcc4 PDF対応 RAGチャット")

    openai_api_key = os.getenv("OPENAI_API_KEY")
    if not openai_api_key:
        st.error("OpenAI APIキーが環境変数に設定されていません。")
        return

    if "llm" not in st.session_state:
        initialize_chatbot(openai_api_key)

    uploaded_file = st.file_uploader("PDFファイルをアップロード", type="pdf")

    if uploaded_file is not None:
        with st.spinner("PDFを処理しています..."):
            st.session_state.vectorstore = process_pdf(uploaded_file, openai_api_key)
            st.session_state.qa_chain = RetrievalQA.from_chain_type(
                llm=st.session_state.llm,
                retriever=st.session_state.vectorstore.as_retriever()
            )
            st.success("PDFの処理が完了しました。チャットを開始できます!")

    if st.session_state.qa_chain:
        user_query = st.text_input("\ud83d\udcac 質問を入力してください")

        if user_query:
            with st.spinner("考えています..."):
                response = st.session_state.qa_chain.run(user_query)
                st.text_area("\ud83e\udd16 回答", value=response, height=200)

if __name__ == "__main__":
    main()

4. サンプルプログラムの詳細解説

1. 環境構築

必要なパッケージをインストールします。

Bash
pip install streamlit langchain openai faiss-cpu

環境変数にOpenAI APIキーを設定します。

Bash
export OPENAI_API_KEY="your-api-key"

2. コードの概要

  • process_pdf関数:PDFを読み込み、テキストを分割してベクトルストアを作成。
  • initialize_chatbot関数:OpenAIモデルを初期化。
  • main関数:Streamlitのインターフェースを構築。

3. コード詳細説明

このPythonコードは、StreamlitとLangChainを使用して、アップロードされたPDFファイルに基づいて質問応答を行うRAG(Retrieval Augmented Generation)チャットボットを構築するものです。以下に、コードのステップバイステップ解説を示します。

1. 必要なライブラリのインポート

Python
import streamlit as st
import os
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
  • streamlit: WebアプリケーションのUI構築に使用
  • os: 環境変数の取得に使用
  • langchain.document_loaders: PDFファイルの読み込みに使用
  • langchain.text_splitter: テキストをチャンクに分割するために使用
  • langchain.embeddings: テキストのベクトル化に使用
  • langchain.vectorstores: ベクトルデータベースの構築に使用
  • langchain.chat_models: OpenAIのチャットモデルを使用
  • langchain.chains: 質問応答チェーンの構築に使用

2. PDF処理関数 process_pdf

Python
def process_pdf(uploaded_file, api_key):
    with open("temp.pdf", "wb") as f:
        f.write(uploaded_file.getbuffer())

    loader = PyPDFLoader("temp.pdf")
    documents = loader.load()

    text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=100)
    docs = text_splitter.split_documents(documents)

    embeddings = OpenAIEmbeddings(openai_api_key=api_key)
    vectorstore = FAISS.from_documents(docs, embeddings)

    return vectorstore
  • アップロードされたPDFファイルを一時ファイルとして保存
  • PyPDFLoaderでPDFファイルを読み込み、テキストを抽出
  • CharacterTextSplitterでテキストを固定長のチャンクに分割
  • OpenAIEmbeddingsでテキストをベクトル化
  • FAISSでベクトルデータベースを構築し、返す

ちなみに、今回のサンプルで読み込ませたPDFファイルには以下の内容が書かれています。

私の昨日の夕食はカレーライスでした。チキンカレー、とても辛く旨みのあるカレーでした。
今朝はエッグベネディクトを近所のレストランで。天気のいい朝でした。

3. チャットボット初期化関数 initialize_chatbot

Python
def initialize_chatbot(api_key):
    st.session_state.llm = ChatOpenAI(model_name="gpt-4", openai_api_key=api_key)
    st.session_state.vectorstore = None
    st.session_state.qa_chain = None
  • セッションステートにOpenAIのチャットモデル、ベクトルデータベース、質問応答チェーンを初期化

4. メイン関数 main

Python
def main():
    st.set_page_config(page_title="PDF対応 RAGチャット", layout="wide")
    st.title(" PDF対応 RAGチャット")

    openai_api_key = os.getenv("OPENAI_API_KEY")
    if not openai_api_key:
        st.error("OpenAI APIキーが環境変数に設定されていません。")
        return

    if "llm" not in st.session_state:
        initialize_chatbot(openai_api_key)

    uploaded_file = st.file_uploader("PDFファイルをアップロード", type="pdf")

    if uploaded_file is not None:
        with st.spinner("PDFを処理しています..."):
            st.session_state.vectorstore = process_pdf(uploaded_file, openai_api_key)
            st.session_state.qa_chain = RetrievalQA.from_chain_type(
                llm=st.session_state.llm,
                retriever=st.session_state.vectorstore.as_retriever()
            )
            st.success("PDFの処理が完了しました。チャットを開始できます!")

    if st.session_state.qa_chain:
        user_query = st.text_input(" 質問を入力してください")

        if user_query:
            with st.spinner("考えています..."):
                response = st.session_state.qa_chain.run(user_query)
                st.text_area(" 回答", value=response, height=200)

if __name__ == "__main__":
    main()
  • Streamlitアプリの設定とタイトル表示
  • 環境変数からOpenAI APIキーを取得し、存在しない場合はエラーを表示
  • セッションステートにチャットボットが初期化されていない場合は初期化
  • PDFファイルのアップロードウィジェットを表示
  • PDFファイルがアップロードされた場合、process_pdfで処理し、質問応答チェーンを構築
  • 質問入力ウィジェットを表示し、質問が入力されたら回答を生成して表示

5. 実行

if __name__ == "__main__":main関数を実行し、Streamlitアプリを起動します。

コードのポイント

  • RAG(Retrieval Augmented Generation): アップロードされたPDFから関連する情報を検索し、その情報に基づいて回答を生成します。
  • セッションステート: Streamlitアプリの状態を保持し、ページ遷移後もデータを維持します。
  • 環境変数: OpenAI APIキーを環境変数から取得することで、コード内に直接APIキーを記述せずに済みます。

このコードを実行するには、以下の準備が必要です。

  1. 必要なライブラリのインストール: pip install streamlit langchain pypdf faiss-cpu openai
  2. OpenAI APIキーの取得: OpenAIのウェブサイトでAPIキーを取得し、環境変数OPENAI_API_KEYに設定
  3. コードの実行: streamlit run your_script_name.py

4. まとめと参考リンク

まとめ

このプログラムでは、StreamlitとLangChainを使い、PDF文書に基づくRAGチャットを構築しました。

  • RAGの概念とその利点
  • LangChainを用いた実装方法
  • サンプルコードの詳細な解説

参考リンク

最後に、書籍のPRです。

最新のOpenAIのチャットAPIの使い方もしっかりと解説されている良書です。2024年11月初版発行、「LangChainとLangGraphによるRAG・AIエージェント[実践]入門」西見、吉田、大嶋著。

最後まで読んでいただきありがとうございます。

コメント

タイトルとURLをコピーしました