検索拡張生成(RAG)の実践的実装ガイド

RAGの核心概念と必要性

大規模言語モデル(LLM)は特定の条件下で以下のような課題を示す:

  • 事実に基づかない出力(ホールシネーション)の発生
  • トレーニングデータ以降の最新情報への対応不能
  • 専門分野や業界用語への理解不足

これらの原因として、モデルのトレーニングデータの制限、知識更新の遅延、特定分野への深さ不足が挙げられる。RAGはこれらの課題を解決するための主要な手法の一つであり、既存のLLMを改変することなく、外部知識をリアルタイムで参照可能にする仕組みである。

基本動作フロー

RAGシステムは以下の3段階で動作する:

  1. 前処理段階:ドキュメントを適切な単位で分割し、ベクトル化してデータベースに格納
  2. 検索段階:ユーザクエリをベクトル化し、類似度に基づいて関連ドキュメントを取得
  3. 生成段階:取得したドキュメントとクエリを組み合わせて、LLMに回答を生成させる

ベクトル化の実装例

テキストをベクトル空間にマッピングする処理を実装する。ここではスライディングウィンドウ方式によるテキスト分割と、埋め込みモデルの利用を示す:

import numpy as np
from typing import List, Generator

def sliding_text_chunker(text: str, chunk_size: int, stride: int) -> Generator[str, None, None]:
    """スライディングウィンドウによるテキスト分割"""
    for start in range(0, len(text), stride):
        yield text[start:start + chunk_size]

class VectorRepository:
    """ベクトルデータの永続化を管理するリポジトリ"""
    
    def __init__(self, db_path: str = "./vector_store"):
        self.vector_db = chromadb.PersistentClient(path=db_path)
        self.collection = self.vector_db.get_or_create_collection("knowledge_base")
    
    def generate_embeddings(self, texts: List[str]) -> List[List[float]]:
        """外部API経由で埋め込みベクトルを生成"""
        # 実際の実装ではAPIクライアントを初期化
        # ここではダミー実装
        return [[0.1 * i for _ in range(1536)] for i in range(len(texts))]
    
    def store_documents(self, raw_texts: List[str]):
        """ドキュメントのベクトル化と格納"""
        all_chunks = []
        for text in raw_texts:
            all_chunks.extend(sliding_text_chunker(text, 200, 50))
        
        embeddings = self.generate_embeddings(all_chunks)
        self.collection.add(
            documents=all_chunks,
            embeddings=embeddings,
            ids=[f"doc_{i}" for i in range(len(all_chunks))]
        )

ベクトル類似度の評価手法

クエリとドキュメントの関連性を評価する主な指標:

評価指標計算式特徴
コサイン類似度 $\frac{A \cdot B}{\|A\| \|B\|}$ 値が1に近いほど類似。方向性に注目
ユークリッド距離 $\sqrt{\sum (A_i - B_i)^2}$ 値が小さいほど類似。絶対距離を評価

ベクトルデータベースの選択基準

用途に応じたデータベース選定のポイント:

  • 開発検証段階:ChromaDB(軽量で組み込み可能)
  • 大規模データ処理:Milvus(分散処理対応)
  • ハイブリッド検索:Elasticsearch(キーワード+ベクトル検索)
  • クラウドネイティブ環境:Qdrant(Rust実装で高効率)

医療FAQシステムの実装例

実際のRAGシステム構築フローを医療分野のFAQシステムで説明:

class MedicalRAGSystem:
    def __init__(self):
        self.vector_repo = VectorRepository()
        self._load_medical_data()
    
    def _load_medical_data(self):
        """医療ドキュメントの読み込みと格納"""
        with open("medical_guidelines.jsonl", "r", encoding="utf-8") as f:
            guidelines = [json.loads(line)["content"] for line in f]
        self.vector_repo.store_documents(guidelines)
    
    def respond_to_query(self, user_question: str) -> str:
        """クエリへの回答生成"""
        # 類似ドキュメントの検索
        results = self.vector_repo.collection.query(
            query_texts=[user_question],
            n_results=3
        )
        
        # コンテキストの構築
        context = "\n".join(results["documents"][0])
        prompt = f"医療専門家として以下の情報を基に回答してください:\n{context}\n\n質問: {user_question}"
        
        # LLMによる回答生成
        return llm_client.generate(prompt, temperature=0.3)

この実装では、医療ガイドラインを前処理し、ユーザの質問に対して関連情報を検索して回答を生成する。温度パラメータで出力の多様性を制御し、専門分野に適した精度を維持する。

タグ: RAGシステム ベクトル検索 埋め込み技術 Qdrant LangChain

7月9日 21:40 投稿