CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-dev-langchain4j--langchain4j-easy-rag

Zero-configuration RAG package that bundles document parsing, embedding, and splitting for easy Retrieval-Augmented Generation in Java applications

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

examples.mddocs/

Complete Examples

Full working applications demonstrating easy-rag usage patterns.

Example 1: Interactive RAG Chatbot

Complete chatbot that answers questions about loaded documents.

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.IngestionResult;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Scanner;

public class RagChatbot {

    interface Assistant {
        String chat(String userMessage);
    }

    public static void main(String[] args) {
        System.out.println("Loading documents...");

        // 1. Load documents
        Path documentPath = Paths.get("docs/product-manual.pdf");
        Document document = FileSystemDocumentLoader.loadDocument(documentPath);

        // 2. Create embedding store and ingest
        EmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();
        IngestionResult result = EmbeddingStoreIngestor.ingest(document, embeddingStore);

        System.out.println("Ingested document using " +
            result.tokenUsage().totalTokenCount() + " tokens");

        // 3. Setup chat model
        ChatModel chatModel = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4o-mini")
            .build();

        // 4. Create RAG-enabled assistant
        Assistant assistant = AiServices.builder(Assistant.class)
            .chatModel(chatModel)
            .contentRetriever(EmbeddingStoreContentRetriever.from(embeddingStore))
            .build();

        // 5. Interactive chat loop
        Scanner scanner = new Scanner(System.in);
        System.out.println("\nRAG Chatbot ready. Type 'exit' to quit.\n");

        while (true) {
            System.out.print("You: ");
            String userMessage = scanner.nextLine();

            if ("exit".equalsIgnoreCase(userMessage)) {
                break;
            }

            if (userMessage.trim().isEmpty()) {
                continue;
            }

            String response = assistant.chat(userMessage);
            System.out.println("Assistant: " + response + "\n");
        }

        scanner.close();
        System.out.println("Goodbye!");
    }
}

Usage:

# Set API key
export OPENAI_API_KEY=sk-...

# Run chatbot
java RagChatbot.java

# Interact
You: What are the system requirements?
Assistant: According to the manual, the system requirements are...

You: How do I configure authentication?
Assistant: The authentication is configured by...

Example 2: Batch Document Processing

Process multiple documents from a directory and persist embeddings.

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.IngestionResult;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.Instant;
import java.util.List;
import java.util.stream.Collectors;

public class BatchIngestion {

    public static void main(String[] args) throws IOException {
        System.out.println("Starting batch ingestion...");
        Instant start = Instant.now();

        // 1. Find all documents in directory tree
        Path docsDir = Paths.get("documents");
        System.out.println("Scanning directory: " + docsDir);

        List<Path> documentPaths = Files.walk(docsDir)
            .filter(Files::isRegularFile)
            .filter(p -> {
                String name = p.toString().toLowerCase();
                return name.endsWith(".pdf") ||
                       name.endsWith(".docx") ||
                       name.endsWith(".txt") ||
                       name.endsWith(".md");
            })
            .collect(Collectors.toList());

        System.out.println("Found " + documentPaths.size() + " documents");

        // 2. Load all documents
        System.out.println("Loading documents...");
        List<Document> documents = documentPaths.stream()
            .map(path -> {
                try {
                    Document doc = FileSystemDocumentLoader.loadDocument(path);

                    // Add metadata about source
                    doc.metadata().put("file_name", path.getFileName().toString());
                    doc.metadata().put("file_path", path.toString());
                    doc.metadata().put("file_size", Files.size(path));

                    return doc;
                } catch (Exception e) {
                    System.err.println("Failed to load " + path + ": " + e.getMessage());
                    return null;
                }
            })
            .filter(doc -> doc != null)
            .collect(Collectors.toList());

        System.out.println("Loaded " + documents.size() + " documents");

        // 3. Create embedding store
        InMemoryEmbeddingStore<TextSegment> embeddingStore = new InMemoryEmbeddingStore<>();

        // 4. Ingest documents
        System.out.println("Generating embeddings...");
        IngestionResult result = EmbeddingStoreIngestor.ingest(documents, embeddingStore);

        System.out.println("Ingestion complete!");
        System.out.println("  - Embeddings: " + embeddingStore.size());
        System.out.println("  - Tokens: " + result.tokenUsage().totalTokenCount());

        // 5. Persist to file
        String outputFile = "knowledge-base-embeddings.json";
        System.out.println("Saving to " + outputFile + "...");
        embeddingStore.serializeToFile(outputFile);

        Instant end = Instant.now();
        Duration duration = Duration.between(start, end);
        System.out.println("Total time: " + duration.toSeconds() + " seconds");
        System.out.println("Done!");
    }
}

Usage:

# Run batch ingestion
java BatchIngestion.java

# Output:
# Starting batch ingestion...
# Scanning directory: documents
# Found 47 documents
# Loading documents...
# Loaded 47 documents
# Generating embeddings...
# Ingestion complete!
#   - Embeddings: 342
#   - Tokens: 89234
# Saving to knowledge-base-embeddings.json...
# Total time: 45 seconds
# Done!

Example 3: Multi-Category Document System

Organize documents by category with filtered retrieval.

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.nio.file.Paths;
import java.util.Arrays;
import java.util.List;

import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;

public class MultiCategorySystem {

    interface Assistant {
        String chat(String message);
    }

    public static void main(String[] args) {
        // 1. Load and categorize documents
        EmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();

        // Technical documentation
        List<Document> techDocs = FileSystemDocumentLoader.loadDocumentsRecursively(
            Paths.get("docs/technical")
        );
        techDocs.forEach(doc -> doc.metadata().put("category", "technical"));
        EmbeddingStoreIngestor.ingest(techDocs, store);
        System.out.println("Ingested " + techDocs.size() + " technical documents");

        // User guides
        List<Document> guides = FileSystemDocumentLoader.loadDocumentsRecursively(
            Paths.get("docs/guides")
        );
        guides.forEach(doc -> doc.metadata().put("category", "guides"));
        EmbeddingStoreIngestor.ingest(guides, store);
        System.out.println("Ingested " + guides.size() + " user guides");

        // API reference
        List<Document> apiDocs = FileSystemDocumentLoader.loadDocumentsRecursively(
            Paths.get("docs/api")
        );
        apiDocs.forEach(doc -> doc.metadata().put("category", "api"));
        EmbeddingStoreIngestor.ingest(apiDocs, store);
        System.out.println("Ingested " + apiDocs.size() + " API docs");

        // 2. Create chat model
        ChatModel chatModel = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4o-mini")
            .build();

        // 3. Create category-specific assistants
        Assistant technicalAssistant = createAssistant(
            chatModel, store, "technical"
        );

        Assistant guideAssistant = createAssistant(
            chatModel, store, "guides"
        );

        Assistant apiAssistant = createAssistant(
            chatModel, store, "api"
        );

        // 4. Use assistants
        System.out.println("\n=== Technical Question ===");
        String techAnswer = technicalAssistant.chat(
            "What is the system architecture?"
        );
        System.out.println(techAnswer);

        System.out.println("\n=== User Guide Question ===");
        String guideAnswer = guideAssistant.chat(
            "How do I get started?"
        );
        System.out.println(guideAnswer);

        System.out.println("\n=== API Question ===");
        String apiAnswer = apiAssistant.chat(
            "What is the authentication endpoint?"
        );
        System.out.println(apiAnswer);
    }

    private static Assistant createAssistant(
        ChatModel chatModel,
        EmbeddingStore<TextSegment> store,
        String category
    ) {
        ContentRetriever retriever = EmbeddingStoreContentRetriever.builder()
            .embeddingStore(store)
            .filter(metadataKey("category").isEqualTo(category))
            .maxResults(5)
            .minScore(0.6)
            .displayName(category + "Retriever")
            .build();

        return AiServices.builder(Assistant.class)
            .chatModel(chatModel)
            .contentRetriever(retriever)
            .build();
    }
}

Example 4: Persistent RAG System

Load embeddings from disk on startup for fast initialization.

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Instant;
import java.util.List;

public class PersistentRagSystem {

    interface Assistant {
        String chat(String message);
    }

    private static final Path EMBEDDINGS_FILE = Paths.get("embeddings.json");
    private static final Path DOCS_DIR = Paths.get("documents");

    public static void main(String[] args) throws Exception {
        // 1. Load or create embedding store
        InMemoryEmbeddingStore<TextSegment> store = loadOrCreateEmbeddings();

        // 2. Create chat model
        ChatModel chatModel = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4o-mini")
            .build();

        // 3. Create assistant
        Assistant assistant = AiServices.builder(Assistant.class)
            .chatModel(chatModel)
            .contentRetriever(EmbeddingStoreContentRetriever.from(store))
            .build();

        // 4. Ready to use
        System.out.println("System ready!");
        String answer = assistant.chat("What information is available?");
        System.out.println(answer);
    }

    private static InMemoryEmbeddingStore<TextSegment> loadOrCreateEmbeddings()
        throws Exception {

        // Check if embeddings file exists
        if (Files.exists(EMBEDDINGS_FILE)) {
            System.out.println("Loading existing embeddings from " + EMBEDDINGS_FILE);
            Instant start = Instant.now();

            InMemoryEmbeddingStore<TextSegment> store =
                InMemoryEmbeddingStore.fromFile(EMBEDDINGS_FILE);

            long loadTime = Duration.between(start, Instant.now()).toMillis();
            System.out.println("Loaded " + store.size() +
                " embeddings in " + loadTime + "ms");

            return store;
        }

        // Create new embeddings
        System.out.println("No embeddings found, creating new...");
        System.out.println("Loading documents from " + DOCS_DIR);

        List<Document> documents = FileSystemDocumentLoader.loadDocumentsRecursively(
            DOCS_DIR
        );
        System.out.println("Loaded " + documents.size() + " documents");

        // Ingest
        InMemoryEmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();
        System.out.println("Generating embeddings...");
        EmbeddingStoreIngestor.ingest(documents, store);

        // Save
        System.out.println("Saving embeddings to " + EMBEDDINGS_FILE);
        store.serializeToFile(EMBEDDINGS_FILE);

        System.out.println("Created " + store.size() + " embeddings");
        return store;
    }
}

First run (creates embeddings):

No embeddings found, creating new...
Loading documents from documents
Loaded 25 documents
Generating embeddings...
Saving embeddings to embeddings.json
Created 187 embeddings
System ready!

Subsequent runs (loads from file):

Loading existing embeddings from embeddings.json
Loaded 187 embeddings in 234ms
System ready!

Example 5: Incremental Updates

Add new documents to existing embedding store.

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class IncrementalUpdates {

    public static void main(String[] args) {
        Path embeddingsFile = Paths.get("embeddings.json");

        // 1. Load existing embeddings
        System.out.println("Loading existing embeddings...");
        InMemoryEmbeddingStore<TextSegment> store =
            InMemoryEmbeddingStore.fromFile(embeddingsFile);

        int originalSize = store.size();
        System.out.println("Current embeddings: " + originalSize);

        // 2. Load new documents
        System.out.println("Loading new documents...");
        List<Document> newDocuments = FileSystemDocumentLoader.loadDocumentsRecursively(
            Paths.get("new-documents")
        );
        System.out.println("Found " + newDocuments.size() + " new documents");

        // 3. Ingest new documents into existing store
        System.out.println("Adding new embeddings...");
        EmbeddingStoreIngestor.ingest(newDocuments, store);

        int newSize = store.size();
        System.out.println("Added " + (newSize - originalSize) + " embeddings");
        System.out.println("Total embeddings: " + newSize);

        // 4. Save updated store
        System.out.println("Saving updated embeddings...");
        store.serializeToFile(embeddingsFile);

        System.out.println("Done!");
    }
}

Example 6: Custom Chunk Size Based on Content

Dynamically adjust chunk size based on document type.

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.document.DocumentTransformer;
import dev.langchain4j.data.document.loader.FileSystemDocumentLoader;
import dev.langchain4j.data.document.splitter.DocumentSplitter;
import dev.langchain4j.data.document.splitter.DocumentSplitters;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.EmbeddingStoreIngestor;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;

public class AdaptiveChunking {

    public static void main(String[] args) {
        // Load documents
        List<Document> documents = FileSystemDocumentLoader.loadDocumentsRecursively(
            Paths.get("documents")
        );

        // Add document type metadata
        DocumentTransformer typeDetector = document -> {
            String fileName = document.metadata().getString("file_name");
            String type;

            if (fileName.contains("api") || fileName.contains("reference")) {
                type = "technical";
            } else if (fileName.contains("guide") || fileName.contains("tutorial")) {
                type = "guide";
            } else {
                type = "general";
            }

            document.metadata().put("document_type", type);
            return document;
        };

        // Process each document type separately
        EmbeddingStore<TextSegment> store = new InMemoryEmbeddingStore<>();

        for (Document doc : documents) {
            doc = typeDetector.transform(doc);
            String type = doc.metadata().getString("document_type");

            DocumentSplitter splitter;
            switch (type) {
                case "technical":
                    // Larger chunks for technical content
                    splitter = DocumentSplitters.recursive(500, 60);
                    break;
                case "guide":
                    // Medium chunks for guides
                    splitter = DocumentSplitters.recursive(400, 50);
                    break;
                default:
                    // Standard chunks for general content
                    splitter = DocumentSplitters.recursive(300, 30);
            }

            EmbeddingStoreIngestor ingestor = EmbeddingStoreIngestor.builder()
                .documentSplitter(splitter)
                .embeddingStore(store)
                .build();

            ingestor.ingest(doc);
        }

        System.out.println("Processed " + documents.size() +
            " documents into " + store.size() + " embeddings");
    }
}

Example 7: RAG with Query Routing

Route queries to appropriate document collections.

import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.inmemory.InMemoryEmbeddingStore;

import java.util.Scanner;

public class QueryRouting {

    interface Assistant {
        String chat(String message);
    }

    public static void main(String[] args) {
        // Load separate stores for different content types
        EmbeddingStore technicalStore = loadTechnicalDocs();
        EmbeddingStore guideStore = loadGuideDocs();
        EmbeddingStore faqStore = loadFaqDocs();

        // Create retrievers
        ContentRetriever techRetriever = EmbeddingStoreContentRetriever.builder()
            .embeddingStore(technicalStore)
            .maxResults(5)
            .minScore(0.7)
            .build();

        ContentRetriever guideRetriever = EmbeddingStoreContentRetriever.builder()
            .embeddingStore(guideStore)
            .maxResults(5)
            .minScore(0.6)
            .build();

        ContentRetriever faqRetriever = EmbeddingStoreContentRetriever.builder()
            .embeddingStore(faqStore)
            .maxResults(3)
            .minScore(0.8)
            .build();

        // Chat model
        ChatModel chatModel = OpenAiChatModel.builder()
            .apiKey(System.getenv("OPENAI_API_KEY"))
            .modelName("gpt-4o-mini")
            .build();

        // Create assistants
        Assistant techAssistant = createAssistant(chatModel, techRetriever);
        Assistant guideAssistant = createAssistant(chatModel, guideRetriever);
        Assistant faqAssistant = createAssistant(chatModel, faqRetriever);

        // Interactive routing
        Scanner scanner = new Scanner(System.in);
        System.out.println("RAG System with Query Routing");
        System.out.println("Commands: /tech, /guide, /faq, /exit\n");

        Assistant currentAssistant = guideAssistant;
        String mode = "guide";

        while (true) {
            System.out.print("[" + mode + "] You: ");
            String input = scanner.nextLine();

            if (input.equals("/exit")) break;

            if (input.equals("/tech")) {
                currentAssistant = techAssistant;
                mode = "technical";
                System.out.println("Switched to technical docs");
                continue;
            }

            if (input.equals("/guide")) {
                currentAssistant = guideAssistant;
                mode = "guide";
                System.out.println("Switched to user guides");
                continue;
            }

            if (input.equals("/faq")) {
                currentAssistant = faqAssistant;
                mode = "faq";
                System.out.println("Switched to FAQ");
                continue;
            }

            String response = currentAssistant.chat(input);
            System.out.println("Assistant: " + response + "\n");
        }

        scanner.close();
    }

    private static Assistant createAssistant(
        ChatModel chatModel,
        ContentRetriever retriever
    ) {
        return AiServices.builder(Assistant.class)
            .chatModel(chatModel)
            .contentRetriever(retriever)
            .build();
    }

    // Placeholder methods - implement based on your setup
    private static EmbeddingStore loadTechnicalDocs() {
        return InMemoryEmbeddingStore.fromFile("tech-docs.json");
    }

    private static EmbeddingStore loadGuideDocs() {
        return InMemoryEmbeddingStore.fromFile("guide-docs.json");
    }

    private static EmbeddingStore loadFaqDocs() {
        return InMemoryEmbeddingStore.fromFile("faq-docs.json");
    }
}

Related Documentation

  • Quick Start - Quick start guide
  • Document Ingestion API - Document ingestion API
  • Content Retrieval API - Content retrieval API
  • Configuration - Configuration options

Install with Tessl CLI

npx tessl i tessl/maven-dev-langchain4j--langchain4j-easy-rag@1.11.0

docs

api-document-loading.md

api-ingestion.md

api-retrieval.md

api-types-chat.md

api-types-core.md

api-types-storage.md

architecture.md

configuration.md

examples.md

index.md

quickstart.md

reference.md

troubleshooting.md

tile.json