mcpField
L
langchain4j-rag-implementation-patterns

langchain4j-rag-implementation-patterns

github.com/giuseppe-trisciuoglio開発3653.7k 回インストール7 週間前に更新
リポジトリ

langchain4j-rag-implementation-patterns について

LangChain4j で RAG アプリケーションを構築する開発者向けのスキルです。ドキュメント取り込み、ベクトル検索、質問応答に加え、ハイブリッド検索や検証のパターンを扱います。

主な機能

  1. 01ドキュメントの読み込み・分割・埋め込み
  2. 02メタデータで絞り込むベクトル検索
  3. 03ハイブリッド検索と再ランキングの指針
  4. 04ドキュメント対話のパターン
  5. 05取り込みと検索の検証

活用シーン

  1. 01PDF、テキスト、ウェブページの質疑応答構築
  2. 02アシスタントへの社内・外部情報の提供
  3. 03ドキュメントリポジトリでのセマンティック検索
  4. 04出典付きの分野別ナレッジ整理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/giuseppe-trisciuoglio/developer-kit --skill langchain4j-rag-implementation-patterns

インストール後、Claude Code で/langchain4j-rag-implementation-patternsを入力して読み込まれたか確認します。

同梱ファイルreferences/examples.mdreferences/references.md

詳細

LangChain4j RAG 実装パターン

概要

LangChain4j を使用して、ドキュメントの取り込みパイプライン、埋め込みストア、ベクトル検索を備えた RAG システムを実装し、ドキュメントとのチャットやナレッジを活用した AI アプリケーションを構築します。

このスキルを使用する場面

  • PDF、テキストファイル、ウェブページを対象とした、ドキュメントとのチャットシステムやドキュメント Q&A を構築する場合
  • 企業のナレッジベースや外部ソースにアクセスできる AI アシスタントを作成する場合
  • ドキュメントリポジトリを対象としたセマンティック検索やハイブリッド検索を実装する場合
  • 厳選したナレッジと情報源の明示を備えた、ドメイン固有の AI を構築する場合

手順

RAG プロジェクトの初期化

必要な依存関係を含む新しい Spring Boot プロジェクトを作成します。

pom.xml:

<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-spring-boot-starter</artifactId>
    <version>1.8.0</version>
</dependency>
<dependency>
    <groupId>dev.langchain4j</groupId>
    <artifactId>langchain4j-open-ai</artifactId>
    <version>1.8.0</version>
</dependency>
ドキュメント取り込みの設定

検証機能を備えたドキュメントの読み込みと処理を設定します。

検証チェックポイント:取り込み後、埋め込み数がセグメント数と一致することを確認し、サンプルクエリで検索をテストします。

@Configuration
public class RAGConfiguration {

    @Bean
    public EmbeddingModel embeddingModel() {
        return OpenAiEmbeddingModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("text-embedding-3-small")
            .build();
    }

    @Bean
    public EmbeddingStore<TextSegment> embeddingStore() {
        return new InMemoryEmbeddingStore<>();
    }
}

ドキュメント取り込みサービスを作成します。

@Service
@RequiredArgsConstructor
public class DocumentIngestionService {

    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore<TextSegment> embeddingStore;

    public void ingestDocument(String filePath, Map<String, Object> metadata) {
        Document document = FileSystemDocumentLoader.loadDocument(filePath);
        document.metadata().putAll(metadata);

        DocumentSplitter splitter = DocumentSplitters.recursive(
            500, 50, new OpenAiTokenCountEstimator("text-embedding-3-small")
        );

        List<TextSegment> segments = splitter.split(document);
        List<Embedding> embeddings = embeddingModel.embedAll(segments).content();
        embeddingStore.addAll(embeddings, segments);

        // Validation: verify embedding count matches segments
        if (embeddings.size() != segments.size()) {
            throw new IllegalStateException("Embedding count mismatch: expected " + segments.size() + ", got " + embeddings.size());
        }
    }

    public boolean validateIngestion(String testQuery) {
        // Validation: test retrieval with sample query
        Embedding queryEmbedding = embeddingModel.embed(testQuery).content();
        List<EmbeddingMatch<TextSegment>> results = embeddingStore.search(
            EmbeddingSearchRequest.builder()
                .queryEmbedding(queryEmbedding)
                .maxResults(1)
                .build()
        ).matches();
        return !results.isEmpty();
    }
}
コンテンツ検索の設定

フィルタリングを使用したコンテンツ検索を設定します。

検証チェックポイント:設定後、既知のクエリで検索をテストし、埋め込みを検索できることを確認します。

@Configuration
public class ContentRetrieverConfiguration {

    @Bean
    public ContentRetriever contentRetriever(
            EmbeddingStore<TextSegment> embeddingStore,
            EmbeddingModel embeddingModel) {

        return EmbeddingStoreContentRetriever.builder()
            .embeddingStore(embeddingStore)
            .embeddingModel(embeddingModel)
            .maxResults(5)
            .minScore(0.7)
            .build();
    }
}
RAG 対応 AI サービスの作成

コンテキスト検索を備えた AI サービスを定義します。

interface KnowledgeAssistant {
    @SystemMessage("""
        You are a knowledgeable assistant with access to a comprehensive knowledge base.

        When answering questions:
        1. Use the provided context from the knowledge base
        2. If information is not in the context, clearly state this
        3. Provide accurate, helpful responses
        4. When possible, reference specific sources
        5. If the context is insufficient, ask for clarification
        """)
    String answerQuestion(String question);
}

@Service
@RequiredArgsConstructor
public class KnowledgeService {

    private final KnowledgeAssistant assistant;

    public KnowledgeService(ChatModel chatModel, ContentRetriever contentRetriever) {
        this.assistant = AiServices.builder(KnowledgeAssistant.class)
            .chatModel(chatModel)
            .contentRetriever(contentRetriever)
            .build();
    }

    public String answerQuestion(String question) {
        return assistant.answerQuestion(question);
    }
}

例

基本的なドキュメント処理
public class BasicRAGExample {
    public static void main(String[] args) {
        var embeddingStore = new InMemoryEmbeddingStore<TextSegment>();

        var embeddingModel = OpenAiEmbeddingModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("text-embedding-3-small")
            .build();

        var ingestor = EmbeddingStoreIngestor.builder()
            .embeddingModel(embeddingModel)
            .embeddingStore(embeddingStore)
            .build();

        ingestor.ingest(Document.from("Spring Boot is a framework for building Java applications with minimal configuration."));

        var retriever = EmbeddingStoreContentRetriever.builder()
            .embeddingStore(embeddingStore)
            .embeddingModel(embeddingModel)
            .build();
    }
}
複数ドメイン対応のアシスタント
interface MultiDomainAssistant {
    @SystemMessage("""
        You are an expert assistant with access to multiple knowledge domains:
        - Technical documentation
        - Company policies
        - Product information
        - Customer support guides

        Tailor your response based on the type of question and available context.
        Always indicate which domain the information comes from.
        """)
    String answerQuestion(@MemoryId String userId, String question);
}
階層型 RAG
@Service
@RequiredArgsConstructor
public class HierarchicalRAGService {

    private final EmbeddingStore<TextSegment> chunkStore;
    private final EmbeddingStore<TextSegment> summaryStore;
    private final EmbeddingModel embeddingModel;

    public String performHierarchicalRetrieval(String query) {
        List<EmbeddingMatch<TextSegment>> summaryMatches = searchSummaries(query);
        List<TextSegment> relevantChunks = new ArrayList<>();

        for (EmbeddingMatch<TextSegment> summaryMatch : summaryMatches) {
            String documentId = summaryMatch.embedded().metadata().getString("documentId");
            List<EmbeddingMatch<TextSegment>> chunkMatches = searchChunksInDocument(query, documentId);
            chunkMatches.stream()
                .map(EmbeddingMatch::embedded)
                .forEach(relevantChunks::add);
        }

        return generateResponseWithChunks(query, relevantChunks);
    }
}

ベストプラクティス

ドキュメントの分割
  • ほとんどのアプリケーションでは、500~1000 トークンのチャンクに再帰的に分割します
  • コンテキストを保持するため、チャンク間で 20~50 トークンのオーバーラップを維持します
  • 分割する際は、ドキュメントの構造(見出し、段落)を考慮します
  • 最適な埋め込み生成のため、トークンを考慮したスプリッターを使用します
メタデータ戦略
  • フィルタリングと情報源の明示のため、豊富なメタデータを含めます。
    • マルチテナント対応のユーザー ID とテナント ID
    • ドキュメントの種類とカテゴリの分類
    • 作成日時と更新日時
    • バージョンと作成者の情報
    • 機密性とアクセスレベルのタグ
クエリ処理
  • クエリの前処理とクリーニングを実装します
  • 再現率を高めるため、クエリ拡張を検討します
  • ユーザーのコンテキストに基づいて動的なフィルタリングを適用します
  • 結果の品質を向上させるため、再ランキングを使用します
パフォーマンスの最適化
  • 繰り返し実行されるクエリの埋め込みをキャッシュします
  • 大量の処理には、埋め込みのバッチ生成を使用します
  • 大規模な結果セットにはページネーションを実装します
  • 時間のかかる処理には、非同期処理を検討します

一般的なパターン

シンプルな RAG パイプライン
@RequiredArgsConstructor
@Service
public class SimpleRAGPipeline {

    private final EmbeddingModel embeddingModel;
    private final EmbeddingStore<TextSegment> embeddingStore;
    private final ChatModel chatModel;

    public String answerQuestion(String question) {
        Embedding queryEmbedding = embeddingModel.embed(question).content();
        EmbeddingSearchRequest request = EmbeddingSearchRequest.builder()
            .queryEmbedding(queryEmbedding)
            .maxResults(3)
            .build();

        List<TextSegment> segments = embeddingStore.search(request).matches().stream()
            .map(EmbeddingMatch::embedded)
            .collect(Collectors.toList());

        String context = segments.stream()
            .map(TextSegment::text)
            .collect(Collectors.joining("\n\n"));

        return chatModel.generate(context + "\n\nQuestion: " + question + "\nAnswer:");
    }
}
ハイブリッド検索(ベクトル+キーワード)
@Service
@RequiredArgsConstructor
public class HybridSearchService {

    private final EmbeddingStore<TextSegment> vectorStore;
    private final FullTextSearchEngine keywordEngine;
    private final EmbeddingModel embeddingModel;

    public List<Content> hybridSearch(String query, int maxResults) {
        // Vector search
        List<Content> vectorResults = performVectorSearch(query, maxResults);

        // Keyword search
        List<Content> keywordResults = performKeywordSearch(query, maxResults);

        // Combine and re-rank using RRF algorithm
        return combineResults(vectorResults, keywordResults, maxResults);
    }
}

トラブルシューティング

検証の失敗

埋め込み数の不一致:セグメント数と埋め込み数が一致しない場合に発生します。スプリッターの設定とモデルが利用可能かを確認してください。

検索結果が空:validateIngestion(testQuery) を呼び出し、埋め込みを検索できることを確認します。ドキュメントが正常に取り込まれたか確認してください。

検索スコアが低い:minScore のしきい値(デフォルトは 0.7)がユースケースに対して高すぎないか確認してください。既知のクエリでテストします。

よくある問題

検索結果の品質が低い

  • ドキュメントのチャンクサイズとオーバーラップの設定を確認します
  • 埋め込みモデルとの互換性を確認します
  • メタデータフィルターが厳しすぎないことを確認します
  • 再ランキングのステップを追加することを検討します
  • 検証を実行して、埋め込みが存在することを確認します

パフォーマンスが遅い

  • 頻繁に実行されるクエリには、キャッシュ済みの埋め込みを使用します
  • ベクトルストアのデータベースインデックスを最適化します
  • 大規模なデータセットにはページネーションを実装します
  • 大量の処理には非同期処理を検討します

メモリ使用量が多い

  • 大規模なデータセットには、ディスクベースの埋め込みストアを使用します
  • 適切なページネーションとフィルタリングを実装します
  • 使用されていない埋め込みを定期的にクリーンアップします
  • チャンクサイズを監視して最適化します

制約と警告

  • 埋め込みモデルのコスト:大規模なドキュメントコレクションの埋め込み生成には高いコストがかかる場合があります。キャッシュとバッチ処理を実装してください。
  • ベクトルストアのスケーラビリティ:インメモリストアは開発用途にのみ適しています。本番環境では、永続ストア(Pinecone、Qdrant、Redis)を使用してください。
  • チャンクサイズのトレードオフ:チャンクを小さくすると精度が向上しますが、コンテキストが失われます。チャンクを大きくするとコンテキストを保持できますが、ノイズが生じる可能性があります。
  • 古いデータ:ソースドキュメントが変更されると、キャッシュされた埋め込みは古くなります。更新戦略を実装してください。
  • トークン制限:RAG のコンテキストウィンドウには制限があります。通常、標準的なモデルの制限内に収まるのは、取得したチャンク 3~5 個です。
  • ハルシネーションのリスク:RAG によってハルシネーションは減少しますが、なくなるわけではありません。重要な回答は必ずソースと照合して検証してください。
  • レイテンシー:ベクトル検索と埋め込み生成によりレイテンシーが増加します。リアルタイムアプリケーションでは、非同期処理を検討してください。
  • メタデータフィルタリング:フィルターを厳しくしすぎると、結果が返されない場合があります。フォールバック戦略を実装してください。
  • マルチテナント:テナント間でデータが漏えいしないよう、メタデータが適切に分離されていることを確認してください。

参考資料

コメント