【RAG構築】FAISS×S3×Pythonで社内PDF検索AIをゼロから作る方法

目次

はじめに

「RAGってよく聞くけど、実際どうやって作るの?」「ベクトル検索とか難しそうで手が出せない…」
そんな状態からでも、ローカル環境だけで“動くRAG”を作れる構成をこの記事では紹介します。

本記事では、トヨタやソニーの決算PDFを使いながら、
PDFを読み込んで → AIが検索できる形に変換し → チャット形式で質問できる
という一連の流れを、FAISS × S3 × Python × Streamlitで構築していきます。やっていることはシンプルです。

「文章を細かく分けて → 数値化して → 似ているものを探して → AIに答えさせる」
これを一つずつ積み上げていくだけです。難しい理論は後回しでOKです。
まずは「動くもの」を作りながら、RAGの全体像をつかんでいきましょう。

全体構成

まずは全体の構成を「アーキテクチャ構成図」と「フォルダ構成」で示します。
手順を実施するうえで、何をしているのかを把握しながら進めることが大事だと思いますので、この図を振り返りながら進めていただくと理解が深まると思います。

アーキテクチャ構成図

フォルダ構成

project-root/
├── app/
│   └── app.py                  #  Streamlitアプリ本体(質問→検索→回答)
│
├── scripts/
│   ├── extract_text.py         #  PDF→テキスト抽出
│   ├── chunk.py                #  テキスト分割(チャンク化)
│   ├── embed.py                #  ベクトル化&FAISSインデックス生成
│   └── __pycache__/            # (自動生成)Pythonキャッシュ
│       └── chunk.cpython-39.pyc
│
├── data/
│   ├── texts.txt               #  抽出された生テキスト
│   ├── texts.npy               #  チャンク化済みテキスト
│   ├── meta.npy                #  メタ情報(会社・ファイル紐付け)
│   ├── index.faiss             #  ベクトル検索インデックス(コア)
│   │
│   ├── toyota/                 #  トヨタ決算PDF
│   │   ├── 2026_1q_summary_jp.pdf
│   │   ├── 2026_2q_summary_jp.pdf
│   │   └── 2026_3q_summary_jp.pdf
│   │
│   └── sony/                   #  ソニー決算PDF
│       ├── 25q1_sony.pdf
│       ├── 25q2_sony.pdf
│       └── 25q3_sony.pdf
│
├── metadata/
│   └── files.csv               #  PDF管理データ(company, yearなど)
│
└── requirements.txt            #  依存ライブラリ一覧

前提(環境)

・AlmaLinux9
(本記事ではAlmaLinux9で実施してるが、自身の環境に合わせて読み替えて実施可能です。コマンドはAlmaLinuxベースで記載しているので、AlmaLinuxをインストールするほうが構築はしやすいかと思います。)
・AWS アカウント
・OPEN AIのAPIキー

事前準備

開発ツールインストール

AlmaLinux9を起動後以下のコマンドを実行し、必要なツールをインストールする。

sudo dnf update -y
sudo dnf install epel-release -y
sudo dnf install gcc openssl-devel bzip2-devel libffi-devel zlib-devel wget make -y
sudo dnf install vi -y

Python3.12をインストール

以下のコマンドを実行し、Python3.12をインストールする。
※AlmaLinux9にはpyhon3.9がインストールされるが、今回実施する内容では3.10以上のバージョンが推奨であるため、ここでPython3.12をインストールしています。なお、ビルドしてインストールしているので少し時間がかかります。

wget https://www.python.org/ftp/python/3.12.0/Python-3.12.0.tar.xz
tar -xf Python-3.12.0.tar.xz
cd Python-3.12.0
./configure --enable-optimizations
make -j $(nproc)
sudo make altinstall

AWS CLIインストール

以下のコマンドを実行し、AWS CLIをインストールする。
(このインストールが完了するとAWSのコマンドを実行できるようになります。)

pip install awscli

AWS Configure設定

以下のコマンドを実行し、認証情報を入力する。

aws configure

・AWS Access Key ID:{自身のAWSアカウントのアクセスキー}
・AWS Secret Access Key:自身のAWSアカウントのシークレットキー
・region: ap-northeast-1
・output: json

ローカル環境準備

以下のコマンドを実行し、作成した仮想環境を有効化する。
有効化すると、ターミナルの先頭に (venv)と表示され、 以降の python や pip install はこの環境専用になる。

cd ~
python -m venv venv
source venv/bin/activate

ディレクトリ作成

以下のコマンドを実行し、RAG構築・利用に必要なディレクトリを作成する。

mkdir -p ~/project-root/app
mkdir -p ~/project-root/data/toyota
mkdir -p ~/project-root/data/sony
mkdir -p ~/project-root/scripts
mkdir -p ~/project-root/faiss_index
cd project-root

PDF配置

トヨタやソニーなど、自身が利用したいいpdf情報を取得し、~/project-root/dataに配置する。
(本記事ではソニーとトヨタの決算資料をサンプルとして配置している。)

~/project-root/data/sony/25q1_sony.pdf
~/project-root/data/sony/25q2_sony.pdf
~/project-root/data/sony/25q3_sony.pdf
~/project-root/data/toyota/2026_1q_summary_jp.pdf
~/project-root/data/toyota/2026_2q_summary_jp.pdf
~/project-root/data/toyota/2026_3q_summary_jp.pdf

S3バケット作成&PDFアップロード

S3バケットを作成してPDFをアップロードする。

aws s3 mb s3://rag-financial-data-12345
aws s3 cp ~/project-root/data/ s3://rag-financial-data-12345/data/ --recursive

メタデータCSV作成

metadata用ディレクトリを作成し、CSVファイルを作成する。

mkdir -p ~/project-root/metadata
vi ~/project-root/metadata/files.csv

ファイル内容↓

company,year,quarter,s3_path
toyota,2023,Q1,s3://rag-financial-data-12345/data/toyota/2026_1q_summary_jp.pdf
toyota,2023,Q2,s3://rag-financial-data-12345/data/toyota/2026_2q_summary_jp.pdf
toyota,2023,Q3,s3://rag-financial-data-12345/data/toyota/2026_3q_summary_jp.pdf
sony,2023,Q1,s3://rag-financial-data-12345/data/sony/25q1_sony.pdf
sony,2023,Q2,s3://rag-financial-data-12345/data/sony/25q2_sony.pdf
sony,2023,Q3,s3://rag-financial-data-12345/data/sony/25q3_sony.pdf

以下のコマンドを実行し、S3アップロードする。

aws s3 cp ~/project-root/metadata/files.csv s3://rag-financial-data-12345/metadata/files.csv

requirements作成

依存ライブラリを保持するファイルである「requirements.txt」を作成する。

vi ~/project-root/requirements.txt

ファイル内容↓

boto3
pandas
PyPDF2
faiss-cpu
openai
Tqdm
pdfplumber

以下のコマンドを実行し、インストールを実施する。

pip install -r ~/project-root/requirements.txt

python作成

以下のpythonファイルを作成する。

~/project-root/scripts/extract_text.py
import os
import pdfplumber

DATA_DIR = "data"
OUTPUT_FILE = "data/texts.txt"

def clean_text(text: str) -> str:
    if not text:
        return ""
    return (
        text.replace("\n", " ")
            .replace("\r", " ")
            .replace("\t", " ")
            .strip()
    )

def run():
    with open(OUTPUT_FILE, "w", encoding="utf-8") as out:
        for company in os.listdir(DATA_DIR):
            cpath = os.path.join(DATA_DIR, company)

            # ディレクトリのみ処理(超重要)
            if not os.path.isdir(cpath):
                continue

            for file in os.listdir(cpath):
                if not file.endswith(".pdf"):
                    continue

                path = os.path.join(cpath, file)
                print(f"Processing: {path}")

                try:
                    with pdfplumber.open(path) as pdf:
                        for page in pdf.pages:
                            text = page.extract_text()

                            if text:
                                text = clean_text(text)
                                if text:
                                    out.write(f"{company}|{file}|{text}\n")

                except Exception as e:
                    print(f"Error processing {path}: {e}")

    print("✅ テキスト抽出完了")

if __name__ == "__main__":
    run()
~/project-root/scripts/chunk.py
CHUNK_SIZE = 500

def chunk_text(text):
    words = text.split()
    for i in range(0, len(words), CHUNK_SIZE):
        yield " ".join(words[i:i+CHUNK_SIZE])
~/project-root/scripts/embed.py
import faiss
import numpy as np
from openai import OpenAI
from chunk import chunk_text

client = OpenAI()

texts = []
metas = []

with open("data/texts.txt", encoding="utf-8") as f:
    for line in f:
        company, file, text = line.strip().split("|", 2)
        for chunk in chunk_text(text):
            texts.append(chunk)
            metas.append((company, file))

embeddings = []

for t in texts:
    res = client.embeddings.create(
        model="text-embedding-3-small",
        input=t
    )
    embeddings.append(res.data[0].embedding)

embeddings = np.array(embeddings).astype("float32")

index = faiss.IndexFlatL2(len(embeddings[0]))
index.add(embeddings)

faiss.write_index(index, "data/index.faiss")

np.save("data/meta.npy", metas)
np.save("data/texts.npy", texts)
~/project-root/app/app.py
    for m in history:
        messages.append(m)

    messages.append({
        "role": "user",
        "content": f"""
質問:
{q}

参考資料:
{context}
"""
    })

    res = client.chat.completions.create(
        model="gpt-4.1-mini",
        messages=messages
    )

    return res.choices[0].message.content, context_chunks


# ===== 過去メッセージ表示 =====
for msg in st.session_state.messages:
    with st.chat_message(msg["role"]):
        st.write(msg["content"])

# ===== 入力欄 =====
if prompt := st.chat_input("質問してください(例:トヨタの2023年Q1の営業利益は?)"):

    # ユーザー入力表示
    st.session_state.messages.append({"role": "user", "content": prompt})
    with st.chat_message("user"):
        st.write(prompt)

    # AI応答
    with st.chat_message("assistant"):
        with st.spinner("分析中..."):
            answer, context_chunks = search(prompt)

        st.write(answer)

        # 参考資料(折りたたみ)
        with st.expander("📄 参考にしたテキスト"):
            for i, chunk in enumerate(context_chunks):
                st.markdown(f"**Chunk {i+1}**")
                st.write(chunk)

    # 履歴に追加
    st.session_state.messages.append({"role": "assistant", "content": answer})

OPEN AIのAPIキー設定

今回のRAG構成では、OPEN AIのAPI機能を利用するため、その認証としてAPIキーが必要となる。
以下の手順でOPEN AIのAPIキーを設定する。

nano ~/.bashrc
export OPENAI_API_KEY="sk-xxxxxxx"

ctl + O ,ctl + Xで保存して終了

source ~/.bashrc
echo $OPENAI_API_KEY

※OPEN AIのAPIキー取得方法はインターネット検索で出てくると思いますのでお手数ですがそちらを参照ください。

実行

作成したpythonコードを実行する。

python ~/project-root/scripts/extract_text.py
python ~/project-root/scripts/embed.py

今回の構成ではstreamlitを用いてChatUIを構築するために「streamlit」をインストールしてapp.pyを起動する。

pip install streamlit
streamlit run ~/project-root/app/app.py

app.pyを起動すると以下のような画面が現れるが、無視してEnterを押下する。

アクセス&利用確認

http://localhost:8501へアクセスすると以下のような画面が表示される。
得たい質問を投げかけると、AIが回答してくれる。

以上でRAG構築完了となります。

おわりに

今回は、FAISSとS3を組み合わせたシンプルなRAG構成を、ローカル環境から構築しました。
ポイントは3つです。

・ローカルで完結できるシンプルな構成
・実データ(決算PDF)を使ったリアルなユースケース
・UIまで含めて「実際に使える形」まで持っていくこと

この構成の良いところは、無理なく段階的に拡張できることです。
たとえば、Athenaでメタデータ検索を強化したり、S3中心の構成に寄せていけば、そのまま実務レベルにもスケールできます。RAGは難しく見えますが、分解するとやっていることはシンプルです。一度手を動かしてしまえば、理解のスピードは一気に上がります。ぜひこの構成をベースに、「自分のデータで検索できるAI」を作ってみてください。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

コメント

コメントする

CAPTCHA


目次