WithCodeMedia-1-pc
previous arrowprevious arrow
next arrownext arrow

WithCodeMedia-1-sp
previous arrowprevious arrow
next arrownext arrow

RAG構成でChatGPT APIの自社データ参照チャットボットの作り方|Python実装コード付き

生徒

ChatGPTに「うちの会社の製品マニュアルを読んで質問に答えてほしい」と思っているんですが、汎用のChatGPTは社内データを知らないですよね。どうすればいいですか?

ペン博士

それはRAGという仕組みを使うんじゃ!RAGとは「質問が来たら関連ドキュメントを検索してから、その内容だけを参照してLLMに回答させる」構成じゃ。ファインチューニングよりずっと手軽で、社内ドキュメント・製品マニュアル・FAQなどをChatGPTに覚えさせることができるんじゃぞ。今日はPython・OpenAI API・Streamlitを使ったRAGチャットボットをコード付きで完全解説するぞい!

目次

この記事でわかること

  • RAGの仕組みとファインチューニングとの違い
  • RAGチャットボットの全体アーキテクチャ(6ステップ)
  • OpenAI API + Google Drive + Streamlit の実装コード全文(config.yml / utils.py / app.py)
  • EmbeddingとCosine類似度の直感的な理解
  • 本番運用に向けたチャンク分割・ベクトルDB・ストリーミング対応などのカスタマイズ方法

RAG(Retrieval-Augmented Generation)は、LLMによる回答生成の前に「外部データベースから関連情報を検索して取得する」ステップを挟むことで、モデルが学習していない独自情報でも正確に回答できるようにする手法です。社内FAQ・製品マニュアル・NotionのナレッジベースなどあらゆるデータをChatGPTに「参照させる」ことができます。本記事ではRAGの基本概念から実装コード全文・本番運用のポイントまで完全解説します。


RAGとは何か|ChatGPTが「知らないこと」を知らせる仕組み

RAGを一言で説明すると、「質問に答える前に関連ドキュメントを検索してから、その内容だけを参考にして回答を生成する」仕組みです。

RAGが必要な理由

【ChatGPT(汎用LLM)の限界】

❌ 学習データのカットオフ以降の情報を知らない
❌ 自社の製品情報・社内マニュアル・ナレッジベースを知らない
❌ ハルシネーション(もっともらしい嘘)を生成することがある
❌ 回答の根拠となるドキュメントを示せない

【RAGで解決できること】
✅ 自社ドキュメントを参照しながら回答できる
✅ 「どの文書を参考にしたか」をユーザーに提示できる(根拠の透明性)
✅ 情報を更新するたびにドキュメントを更新するだけで最新情報に対応できる
✅ ハルシネーションを大幅に削減できる(参照範囲を制限するため)

RAG vs ファインチューニング|どちらを選ぶべきか

観点RAGファインチューニング
実装難易度低い(APIキーとコードのみ)高い(学習データ整備・GPU必要)
コスト低い(Embeddings APIのみ)高い(学習費用 + 推論コスト)
データ更新簡単(ドキュメントを差し替えるだけ)難しい(再学習が必要)
回答の根拠提示参照ドキュメントを表示できる根拠を示しにくい
向いているケース社内ドキュメント・FAQ・マニュアル検索特定の文体・フォーマットを学習させたい場合
必要なデータ量数MB〜数GBの文書に対応数千〜数万件の学習データが必要

結論:社内データを参照させたいだけなら RAG 一択です。ファインチューニングは「ChatGPTを特定の口調・形式で回答させたい」「専門タスクへの特化」という場合に向いています。


RAGチャットボットの全体アーキテクチャ|6ステップのフロー

  1. ドキュメント取得:Google Drive API でフォルダ内のGoogle Docsを取得し、テキストを抽出する
  2. ベクトル化(Embedding):抽出したテキストをOpenAI Embeddings APIでベクトルに変換して保持する
  3. 質問の受け取り:Streamlit UIでユーザーの質問を受け取り、同じくEmbeddings APIでベクトル化する
  4. 類似度検索:質問ベクトルとドキュメントベクトルのcosine類似度を計算し、最も関連性の高いドキュメントを特定する
  5. コンテキスト付きでLLMに問い合わせ:「この文書のみを参照して回答してください」という指示と関連ドキュメントをGPT-4o-miniに渡す
  6. 回答と参照ドキュメントを表示:回答文 + 参照したドキュメントへのリンクをユーザーに提示する
【使用技術スタック】

LLM:       OpenAI API(gpt-4o-mini)
Embedding: OpenAI Embeddings API(text-embedding-3-large)
類似度計算: scikit-learn(cosine_similarity)
UI:        Streamlit
データ取得: Google Drive API + google-auth
設定管理:  PyYAML(config.yml)

環境構築と事前準備|APIキー取得からGoogle Drive設定まで

ステップ1:プロジェクトの作成と依存パッケージのインストール

# プロジェクトフォルダを作成
mkdir rag-chatbot && cd rag-chatbot

# 仮想環境を作成・有効化
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate

# 必要なパッケージをインストール
pip install openai streamlit google-api-python-client \
            google-auth-httplib2 google-auth-oauthlib \
            scikit-learn pyyaml
# ファイル構成
rag-chatbot/
├── app.py          # メイン:StreamlitのUI・チャット処理
├── utils.py        # ドキュメント取得・Embedding・類似度計算・LLM問い合わせ
├── config.yml      # APIキー・モデル名・フォルダID等の設定
└── service_account_key.json  # Google Cloud のサービスアカウントキー(Gitに含めない)

ステップ2:OpenAI APIキーの取得

  1. OpenAI Platform にアクセスしてサインイン
  2. 右上のアカウントメニュー → 「API keys」をクリック
  3. 「Create new secret key」でAPIキーを生成してコピー
  4. クレジットを追加(Billing → Add payment method)

ステップ3:Google Cloud プロジェクトとサービスアカウントの設定

  1. Google Cloud Console でプロジェクトを作成する
  2. 「APIとサービス」→「ライブラリ」から「Google Drive API」を有効化する
  3. 「IAMと管理」→「サービスアカウント」で新しいサービスアカウントを作成する
  4. 作成したサービスアカウントの「キー」タブ → 「鍵を追加」→ JSON形式でダウンロードして service_account_key.json として保存する
  5. Google Drive でRAGに使いたいフォルダを開き、サービスアカウントのメールアドレス(*@*.iam.gserviceaccount.com)を「閲覧者」として共有する
  6. そのフォルダのURLから folders/ 以降の文字列をフォルダIDとして控えておく
# .gitignore に必ず追加する
service_account_key.json
config.yml
.env
__pycache__/
venv/

実装コード全文|config.yml・utils.py・app.py の詳細解説

config.yml:設定ファイル

# config.yml
# ⚠️ このファイルはGitに含めないこと(.gitignoreに追加する)

# OpenAI設定
openai:
  api_key: 'sk-xxxxxxxxxxxxxxxxxxxx'  # OpenAI APIキー
  embedding_model: 'text-embedding-3-large'  # Embeddingモデル
  chat_model: 'gpt-4o-mini'  # チャットモデル(コスト・速度のバランス良)

# Google Cloud設定
google:
  service_account_file: 'service_account_key.json'  # サービスアカウントキーのパス

# Google Drive設定
google_drive:
  folder_id: 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'  # ドキュメントフォルダのID

utils.py:コアロジック(ドキュメント取得・Embedding・類似度計算・LLM問い合わせ)

# utils.py

import yaml
from googleapiclient.discovery import build
from google.oauth2.service_account import Credentials
from openai import OpenAI
from sklearn.metrics.pairwise import cosine_similarity


# 設定ファイルを読み込む
with open('config.yml', 'r') as file:
    config = yaml.safe_load(file)

# OpenAI クライアントの初期化
client = OpenAI(api_key=config['openai']['api_key'])

# Google Drive クライアントの初期化
credentials = Credentials.from_service_account_file(config['google']['service_account_file'])
google_client = build('drive', 'v3', credentials=credentials)


def get_docs_list(folder_id: str) -> list[dict]:
    """
    指定したGoogle DriveフォルダからGoogle Docsの一覧を取得し、
    テキスト内容・ドキュメント名・URLをリストで返す。
    """
    # フォルダ内のGoogle Docsを一覧取得
    files = google_client.files().list(
        q=f"'{folder_id}' in parents and mimeType='application/vnd.google-apps.document'",
        fields="files(id, name)"
    ).execute().get('files', [])

    docs_list = []
    for file in files:
        doc_id = file['id']
        # ドキュメントをプレーンテキストとしてエクスポート
        doc_content = google_client.files().export(
            fileId=doc_id,
            mimeType='text/plain'
        ).execute()
        docs_list.append({
            'name': file['name'],
            'url': f"https://docs.google.com/document/d/{doc_id}/edit",
            'content': doc_content.decode('utf-8')
        })

    return docs_list


def vectorize_text(text: str) -> list[float]:
    """
    テキストをOpenAI Embeddings APIでベクトル(数値の配列)に変換する。
    意味的に似たテキストは近いベクトルになる。
    """
    response = client.embeddings.create(
        input=text,
        model=config['openai']['embedding_model']
    )
    return response.data[0].embedding


def find_most_similar(
    question_vector: list[float],
    vectors: list[list[float]],
    documents: list[str],
    top_n: int = 2
) -> list[str]:
    """
    質問ベクトルとドキュメントベクトルのcosine類似度を計算し、
    最も関連性の高い上位 top_n 件のドキュメントを返す。
    """
    similarities = []

    for index, vector in enumerate(vectors):
        # cosine類似度:-1(真逆)〜 1(完全一致)の値を返す
        similarity = cosine_similarity([question_vector], [vector])[0][0]
        similarities.append([similarity, index])

    # 類似度の高い順にソートして上位 top_n 件を返す
    similarities.sort(reverse=True, key=lambda x: x[0])
    top_documents = [documents[index] for similarity, index in similarities[:top_n]]

    return top_documents


def ask_question(question: str, context: list[str]) -> str:
    """
    質問と関連ドキュメントを組み合わせてGPTに問い合わせ、回答を返す。
    context(関連ドキュメント)の範囲のみを参照するように指示する。
    """
    # コンテキスト(関連ドキュメント)を結合
    context_text = "\n\n---\n\n".join(context)

    messages = [
        {
            "role": "system",
            "content": (
                "あなたは社内ドキュメントに基づいて回答するアシスタントです。"
                "以下の情報のみを使用して回答してください。"
                "提供された情報に回答がない場合は「提供されたドキュメントには該当する情報がありませんでした」と回答してください。"
            )
        },
        {
            "role": "user",
            "content": f"質問: {question}\n\n参照ドキュメント:\n{context_text}"
        }
    ]

    response = client.chat.completions.create(
        model=config['openai']['chat_model'],
        messages=messages,
        temperature=0.1  # 低い温度で事実に基づいた回答を促す
    )

    return response.choices[0].message.content

app.py:StreamlitのUI・チャット処理

# app.py

import streamlit as st
import yaml
from utils import vectorize_text, find_most_similar, ask_question, get_docs_list

# 設定ファイルを読み込む
with open('config.yml', 'r') as file:
    config = yaml.safe_load(file)


@st.cache_data(ttl=3600)  # 1時間キャッシュ(毎回Drive APIを叩かないようにする)
def load_documents():
    """
    Google DriveからドキュメントとEmbeddingベクトルを取得する。
    ttl=3600 でキャッシュすることでAPI呼び出し回数を削減する。
    """
    docs = get_docs_list(config['google_drive']['folder_id'])
    contents = [doc['content'] for doc in docs]
    # すべてのドキュメントをベクトル化(初回のみ)
    vectors = [vectorize_text(content) for content in contents]
    return docs, contents, vectors


def main():
    st.set_page_config(
        page_title="社内ドキュメント検索チャットボット",
        page_icon="📚",
        layout="centered"
    )
    st.title('📚 社内ドキュメント検索チャットボット')
    st.caption("Google Drive のドキュメントを参照して質問に回答します")

    # ドキュメントとベクトルをキャッシュから取得
    with st.spinner("ドキュメントを読み込んでいます..."):
        docs, contents, vectors = load_documents()

    st.success(f"{len(docs)} 件のドキュメントを読み込みました")

    # チャット履歴の初期化
    if 'messages' not in st.session_state:
        st.session_state.messages = []

    # チャット履歴の表示
    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.markdown(message["content"])

    # ユーザーの入力を処理
    if user_input := st.chat_input("質問を入力してください(例:有給休暇の申請方法は?)"):
        # ユーザーのメッセージを表示・保存
        st.session_state.messages.append({"role": "user", "content": user_input})
        with st.chat_message("user"):
            st.markdown(user_input)

        # 回答を生成
        with st.chat_message("assistant"):
            with st.spinner("ドキュメントを検索して回答を生成中..."):
                # 質問をベクトル化して類似ドキュメントを検索
                question_vector = vectorize_text(user_input)
                similar_documents = find_most_similar(question_vector, vectors, contents)

                # 関連ドキュメントをコンテキストとしてGPTに渡す
                answer = ask_question(user_input, similar_documents)

                # 参照したドキュメントへのリンクを付加する
                response = f"{answer}\n\n---\n**📖 参照したドキュメント:**\n"
                for doc in docs:
                    if doc['content'] in similar_documents:
                        response += f"- [{doc['name']}]({doc['url']})\n"

            st.markdown(response)

        # アシスタントの回答を保存
        st.session_state.messages.append({"role": "assistant", "content": response})


if __name__ == "__main__":
    main()

起動方法

# Streamlit アプリを起動する
streamlit run app.py

# ブラウザが自動で開く(またはhttp://localhost:8501 にアクセス)

コアロジックの詳細解説|EmbeddingとCosine類似度の仕組み

「なぜテキストをベクトルに変換するのか」「cosine類似度とは何か」を理解することで、RAGをより深く活用・カスタマイズできるようになります。

Embeddingとは何か

【Embeddingの直感的な理解】

テキスト → 数値の配列(ベクトル)に変換する処理

例:
"有給休暇の申請方法"  → [0.21, -0.43, 0.87, ..., 0.12]  # 3072次元のベクトル
"年次有給の取り方"    → [0.19, -0.41, 0.89, ..., 0.11]  # ← 意味が近いので値も近い
"プログラムのバグ修正" → [-0.78, 0.31, -0.22, ..., 0.55] # ← 意味が遠いので値も遠い

重要なポイント:
→ 意味的に似たテキストは似たベクトルになる
→ 文字が違っても「意味が近い」なら近いベクトルになる
   「有給」と「年次有給」は文字が違うが同じトピックなので近いベクトルになる

OpenAI の text-embedding-3-large:
→ 3072次元のベクトルを生成する
→ 日本語テキストにも対応している
→ 1,000トークンあたり $0.00013(非常に安価)

Cosine類似度とは何か

【Cosine類似度の直感的な理解】

2つのベクトルの「向きの近さ」を -1〜1 の値で表す。

cosine_similarity = 1.0  → 完全に同じ方向(意味が完全に一致)
cosine_similarity = 0.9  → ほぼ同じ方向(意味が非常に近い)
cosine_similarity = 0.5  → やや似ている
cosine_similarity = 0.0  → 無関係
cosine_similarity = -1.0 → 真逆(実際のテキストではほぼ発生しない)

【コードの動作】

# 質問:「有給休暇の取り方は?」
question_vector = [0.21, -0.43, 0.87, ...]

# ドキュメントA(有給申請マニュアル)のベクトル:
doc_a_vector = [0.19, -0.41, 0.89, ...]
# → cosine_similarity = 0.97(非常に近い → 上位に選ばれる)

# ドキュメントB(給与規定)のベクトル:
doc_b_vector = [0.10, -0.20, 0.45, ...]
# → cosine_similarity = 0.72(やや近い)

# ドキュメントC(サーバー構成図)のベクトル:
doc_c_vector = [-0.78, 0.31, -0.22, ...]
# → cosine_similarity = 0.21(遠い → 選ばれない)

→ 上位 top_n = 2 件(ドキュメントA・B)がコンテキストとしてGPTに渡される

本番運用に向けたカスタマイズ5つのポイント

ポイント1:チャンク分割(長文ドキュメントの精度向上)

長いドキュメントをそのままEmbeddingすると、1つのベクトルが多くの情報を含みすぎて検索精度が下がります。ドキュメントを適切なサイズに分割(チャンク分割)してからEmbeddingすることで精度が向上します。

# utils.py に追加するチャンク分割関数

def split_into_chunks(text: str, chunk_size: int = 500, overlap: int = 50) -> list[str]:
    """
    長いテキストを chunk_size 文字ごとに分割し、
    overlap 文字のオーバーラップを持たせて文脈の途切れを防ぐ。
    """
    chunks = []
    start = 0
    while start < len(text):
        end = start + chunk_size
        chunk = text[start:end]
        chunks.append(chunk)
        start += chunk_size - overlap  # オーバーラップ分を戻す
    return chunks

# 使用例
doc_content = "...長いドキュメントのテキスト..."
chunks = split_into_chunks(doc_content, chunk_size=500, overlap=50)

# チャンクごとにEmbeddingを作成する
chunk_vectors = [vectorize_text(chunk) for chunk in chunks]

# 検索結果のチャンクを結合して回答に使用する
# → 大きなドキュメントでも関連する部分だけを正確に取得できる

ポイント2:ベクトルDBの導入(大量ドキュメントへの対応)

【ベクトルDBが必要になるタイミング】

現在の実装(in-memory):
→ 毎回すべてのドキュメントをEmbeddingする(起動が遅い)
→ ドキュメントが数十件なら問題ない
→ 数千件以上になると起動時間・メモリ使用量が問題になる

ベクトルDB(Pinecone / Chroma / Qdrant / Weaviate):
→ Embeddingベクトルを永続化して保存する
→ 高速な近似最近傍検索(ANN)を提供する
→ ドキュメントの追加・削除・更新が容易になる

# ChromaDB(ローカル・無料)の導入例
pip install chromadb

import chromadb

client = chromadb.Client()
collection = client.create_collection("company-docs")

# ドキュメントをベクトルDBに追加
collection.add(
    documents=["ドキュメント1のテキスト", "ドキュメント2のテキスト"],
    ids=["doc1", "doc2"]
)

# 類似ドキュメントを検索
results = collection.query(
    query_texts=["有給休暇の申請方法"],
    n_results=2
)

ポイント3:データソースの拡張(Google Drive以外)

【対応できるデータソースの例】

1. Notion(ノーションAPI)
   → Notion APIでページ一覧を取得してテキストを抽出する

2. ローカルのPDF・Wordファイル
   → PyMuPDF / python-docx でテキスト抽出
   pip install pymupdf python-docx

3. Webページのスクレイピング
   → BeautifulSoup4 でHTMLからテキスト抽出
   pip install beautifulsoup4 requests

4. Confluenceやその他のWikiシステム
   → Confluence REST APIで記事一覧・内容を取得

5. SlackのDM・チャンネル履歴
   → Slack APIでメッセージ履歴を取得

# PDFからテキスト抽出の例
import fitz  # PyMuPDF

def extract_text_from_pdf(pdf_path: str) -> str:
    doc = fitz.open(pdf_path)
    text = ""
    for page in doc:
        text += page.get_text()
    return text

ポイント4:ストリーミングレスポンスの実装

# utils.py - ストリーミングレスポンス版

def ask_question_stream(question: str, context: list[str]):
    """
    GPTの回答をストリーミングで返すジェネレータ関数。
    Streamlitの st.write_stream() と組み合わせて使う。
    """
    context_text = "\n\n---\n\n".join(context)
    messages = [
        {"role": "system", "content": "以下の情報のみを使用して回答してください。"},
        {"role": "user", "content": f"質問: {question}\n\n参照ドキュメント:\n{context_text}"}
    ]

    stream = client.chat.completions.create(
        model=config['openai']['chat_model'],
        messages=messages,
        stream=True  # ストリーミングを有効化
    )

    for chunk in stream:
        if chunk.choices[0].delta.content is not None:
            yield chunk.choices[0].delta.content


# app.py - ストリーミングレスポンスの表示
with st.chat_message("assistant"):
    response = st.write_stream(
        ask_question_stream(user_input, similar_documents)
    )

ポイント5:類似度スコアによるフィルタリング

# utils.py - スコアによるフィルタリング

def find_most_similar_with_threshold(
    question_vector: list[float],
    vectors: list[list[float]],
    documents: list[str],
    top_n: int = 2,
    threshold: float = 0.7  # この類似度以上のドキュメントのみを返す
) -> list[str]:
    """
    類似度が threshold 以上のドキュメントのみを返す。
    閾値を設けることで無関係なドキュメントをコンテキストに含めるのを防ぐ。
    """
    similarities = []

    for index, vector in enumerate(vectors):
        similarity = cosine_similarity([question_vector], [vector])[0][0]
        if similarity >= threshold:  # 閾値フィルタ
            similarities.append([similarity, index])

    similarities.sort(reverse=True, key=lambda x: x[0])

    if not similarities:
        return []  # 関連ドキュメントが見つからなかった場合

    top_documents = [documents[index] for similarity, index in similarities[:top_n]]
    return top_documents

RAGチャットボットの実際のユースケース5選

【ユースケース1:社内FAQ・人事情報の自動回答】
データソース:就業規則・有給申請方法・経費精算ルール・入社手続きドキュメント
効果:人事部への問い合わせ件数を削減できる
質問例:「有給休暇は入社何ヶ月後から取得できますか?」
        「交通費の申請締め切りはいつですか?」

【ユースケース2:製品マニュアル・サポートチャットボット】
データソース:製品仕様書・操作マニュアル・FAQ・よくあるトラブルと解決策
効果:サポート対応時間を削減・24時間対応を実現できる
質問例:「エラーコード E-404 が表示されたときの対処法は?」
        「〇〇機能の設定方法を教えてください」

【ユースケース3:過去案件・提案書の検索支援】
データソース:過去の提案書・案件報告書・顧客別資料・議事録
効果:類似案件の素早い検索・提案書作成の効率化
質問例:「小売業のDX支援で過去に提案した内容はありますか?」
        「顧客の〇〇社との商談履歴を教えてください」

【ユースケース4:技術ドキュメント・API仕様の検索】
データソース:社内のAPI仕様書・アーキテクチャ設計書・コーディング規約
効果:新入エンジニアのオンボーディング効率化
質問例:「ユーザー認証APIのエンドポイントとパラメータは?」
        「データベース接続の設定方法は?」

【ユースケース5:法的ドキュメント・契約書の参照】
データソース:各種契約書テンプレート・法的チェックリスト・コンプライアンスガイドライン
効果:法務確認の初期スクリーニングを自動化
質問例:「業務委託契約書の秘密保持条項のサンプルは?」
        「個人情報取り扱いに必要な同意文書の要件は?」

トラブルシューティング|よくあるエラーと対処法

【エラー1: google.auth.exceptions.DefaultCredentialsError】
原因:サービスアカウントキーのファイルパスが間違っている
対処:config.yml の service_account_file のパスを確認する
     service_account_key.json が存在するディレクトリから app.py を起動する

【エラー2: HttpError 403 when requesting Google Drive API】
原因:サービスアカウントにフォルダの閲覧権限がない
対処:Google Drive でフォルダを右クリック → 「共有」から
     サービスアカウントのメールアドレスを「閲覧者」として追加する

【エラー3: openai.AuthenticationError: Incorrect API key provided】
原因:OpenAI APIキーが間違っているか、有効期限切れ
対処:platform.openai.com でAPIキーを再確認・再生成する
     config.yml の api_key を更新する

【エラー4: openai.RateLimitError: Rate limit reached】
原因:短時間にAPI呼び出しが集中している
対処:time.sleep(1) で呼び出しの間隔を開ける
     または Embeddings の計算結果をキャッシュする(@st.cache_data 活用)

【エラー5: 回答が「情報がありません」と表示される】
原因:類似度スコアの閾値が高すぎる / ドキュメントの内容が質問と一致しない
対処:threshold を 0.7 → 0.5 に下げて試す
     ドキュメントのテキスト内容を確認し、質問と関連する情報が含まれているか確認する

よくある質問

RAGの構築にプログラミング経験がどれくらい必要ですか?

Pythonの基礎(関数・辞書・リスト操作)がわかれば実装できます。本記事のコードはコピー&ペーストで動作するように設計されています。難しい数学的背景(線形代数・cosine類似度の計算式)は理解していなくても使えます。まずはコードをそのまま動かして動作を確認してから、自分のデータソースに置き換えるアプローチをお勧めします。

APIの利用料金はどれくらいかかりますか?

非常に安価です。Embeddingは1,000トークンあたり約$0.00013(0.02円程度)で、チャットには gpt-4o-mini(入力1Mトークンあたり$0.15)を使うため、月100回程度の質問なら数十円で運用できます。ただし毎回すべてのドキュメントをEmbeddingすると起動コストが嵩むため、@st.cache_data によるキャッシュを必ず活用してください。

Google Drive以外のデータソース(Notion・PDF・社内Wiki)にも対応できますか?

対応できます。get_docs_list() 関数でテキストを取得する部分を置き換えるだけで、どんなデータソースにも対応できます。PDFなら PyMuPDF(pip install pymupdf)、NotionならNotion API、社内WikiならそれぞれのAPIを使ってテキストを取得する関数を書くだけで、Embedding・類似度検索・LLM問い合わせの部分はそのまま使えます。

機密情報を含む社内ドキュメントをOpenAIに送っても大丈夫ですか?

OpenAI API経由で送ったデータは、デフォルトではモデルの学習に使用されません(API利用時のデータ利用ポリシーより)。ただし、高度な機密情報(個人情報・営業秘密・未公開財務情報)を扱う場合は、Azure OpenAI Serviceの使用を推奨します。Azure OpenAIではデータがユーザーのAzureリソース内に留まり、Microsoftにデータが送られないことが明示されています。

RAGとLangChainの関係は何ですか?

LangChainはRAGを含むLLMアプリケーション構築のためのフレームワークです。本記事の実装は「RAGの仕組みを理解するためにスクラッチで書いた最小構成」です。本番運用ではLangChainやLlamaIndexを使うことで、チャンク分割・ベクトルDB連携・会話履歴管理・エラーハンドリングを簡単に実装できます。まずは本記事の最小実装でRAGの動作原理を理解してから、LangChainに移行するアプローチを推奨します。


生徒

RAGって意外とシンプルな仕組みなんですね!「検索してからLLMに渡す」だけなんですね。実際に動かしてみたいです!

ペン博士

そうじゃ!RAGの本質は「検索(Retrieval)+生成(Generation)」のシンプルな組み合わせじゃ。今日の実装を動かしてみれば「あ、これだけで社内チャットボットが作れるのか」と驚くはずじゃぞ。WithCodeでPythonやJavaScriptの基礎を学んだ後、このRAGの実装に挑戦してみてくれ。AIを「使う」だけでなく「作る」側になれると、エンジニアとしての価値が一段上がるんじゃ!

生徒

まずは自分のGoogle Driveのドキュメントを使ってコードを動かしてみます!将来的には会社に提案できるようなチャットボットを作ってみたいです!


まとめ

  • RAGとは:「質問に答える前に関連ドキュメントを検索して、その内容だけをLLMに参照させる」仕組み。ファインチューニングより手軽でデータ更新が簡単
  • Embeddingの役割:テキストを数値ベクトルに変換することで「意味の近さ」を計算できるようになる。OpenAIの text-embedding-3-large が日本語にも対応
  • Cosine類似度:質問ベクトルとドキュメントベクトルの「向きの近さ」を計算し、最も関連性の高いドキュメントを特定する
  • 実装の流れ:ドキュメント取得 → Embedding → 質問Embedding → 類似度計算 → GPTへのコンテキスト注入 → 回答表示
  • 本番化のポイント:チャンク分割・ベクトルDB(Chroma/Pinecone)・類似度スコアフィルタ・ストリーミングレスポンス・データソースの拡張で実用レベルに高める
  • ユースケース:社内FAQ・製品サポート・過去案件検索・技術ドキュメント・法的ドキュメント参照など幅広く活用できる

RAGは「AIを使いこなすエンジニア」と「AIを使うだけのユーザー」を分ける技術のひとつです。APIキーとPython環境があれば今日から実装できるので、まず手を動かしてみましょう。


関連記事


参考リンク(公式・一次情報)


WithCodeを体験できる初級コース公開中!

WithCodeを体験できる初級コース公開中!

初級コース(¥49,800)が完全無料に!

  • 期間:1週間
  • 学習内容:
    ロードマップ/基礎知識/環境構築/HTML/CSS/LP・ポートフォリオ作成
    正しい学習方法で「確かな成長」を実感できるカリキュラム。

副業・フリーランスが主流になっている今こそ、自らのスキルで稼げる人材を目指してみませんか?

未経験でも心配することはありません。初級コースを受講される方の大多数はプログラミング未経験です。まずは無料カウンセリングで、悩みや不安をお聞かせください!

この記事を書いた人

WithCode(ウィズコード)は「目指すなら稼げる人材」をビジョンに、累計400名以上のフリーランスを輩出してきた超実践型プログラミングスクールです。150社以上の実案件支援を特徴にWeb制作・Webデザインなどの役立つ情報を現場のノウハウに基づいて発信していきます。

– service –WithGroupの運営サービス

  • WithCode
    - ウィズコード -

    スクール

    「未経験」から
    現場で通用する
    スキルを身に付けよう!

    詳細はこちら
  • WithFree
    - ウィズフリ -

    実案件サポート

    制作会社のサポート下で
    実務経験を積んでいこう!

    詳細はこちら

公式サイト より
今すぐ
無料カウンセリング
予約!

目次