Zero-configuration RAG package that bundles document parsing, embedding, and splitting for easy Retrieval-Augmented Generation in Java applications
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Full working applications demonstrating easy-rag usage patterns.
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...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!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();
}
}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!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!");
}
}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");
}
}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");
}
}Install with Tessl CLI
npx tessl i tessl/maven-dev-langchain4j--langchain4j-easy-rag