plugins/developer-kit-java/skills/langchain4j-rag-implementation-patterns/SKILL.md
Provides Retrieval-Augmented Generation (RAG) implementation patterns with LangChain4j for Java. Generates document ingestion pipelines, embedding stores, vector search, and semantic search capabilities. Use when building chat-with-documents systems, document Q&A over PDFs or text files, AI assistants with knowledge bases, semantic search over document repositories, or knowledge-enhanced AI applications with source attribution.
npx skillsauth add giuseppe-trisciuoglio/developer-kit langchain4j-rag-implementation-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Implements RAG systems with LangChain4j: document ingestion pipelines, embedding stores, and vector search for chat-with-documents and knowledge-enhanced AI applications.
Create a new Spring Boot project with required dependencies:
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>
Configure document loading and processing with validation:
Validation Checkpoint: After ingestion, verify embedding count matches segment count and test retrieval with a sample query.
@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<>();
}
}
Create document ingestion service:
@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();
}
}
Setup content retrieval with filtering:
Validation Checkpoint: After configuration, test retrieval with a known query to verify embeddings are searchable.
@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();
}
}
Define AI service with context retrieval:
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);
}
@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);
}
}
@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);
}
}
Embedding Count Mismatch: Thrown when segments != embeddings. Check splitter configuration and model availability.
Empty Retrieval Results: Call validateIngestion(testQuery) to verify embeddings are searchable. Check if document was ingested successfully.
Low Retrieval Scores: Verify minScore threshold (default 0.7) is not too high for your use case. Test with known queries.
Poor Retrieval Results
Slow Performance
High Memory Usage
development
Provides final code cleanup after task review approval. Removes debug logs, temporary comments, dead code, optimizes imports, and improves readability. Use when asked to clean up code, polish, finalize, tidy up, remove technical debt, or prepare code for completion after review. Not for refactoring logic or fixing bugs—focused solely on cosmetic and hygiene cleanup.
tools
Ralph Wiggum-inspired automation loop for specification-driven development. Orchestrates task implementation, review, cleanup, and synchronization using a Python script. Use when: user runs /loop command, user asks to automate task implementation, user wants to iterate through spec tasks step-by-step, or user wants to run development workflow automation with context window management. One step per invocation. State machine: init → choose_task → implementation → review → fix → cleanup → sync → update_done. Supports --from-task and --to-task for task range filtering. State persisted in fix_plan.json.
testing
Creates, updates, validates, and displays the architectural DNA of a project through two shared documents: docs/specs/architecture.md (technology stack, architectural rules, security constraints, AI guardrails) and docs/specs/ontology.md (domain glossary / Ubiquitous Language). Use BEFORE brainstorm as a project setup step, or at any point in the SDD lifecycle to validate specs/tasks against architecture principles. Triggers on 'create constitution', 'update constitution', 'constitution check', 'validate against constitution', 'project principles', 'architectural guardrails', 'setup project architecture', 'define ontology'.
tools
Provides Qwen Coder CLI delegation workflows for coding tasks using Qwen2.5-Coder and QwQ models, including English prompt formulation, execution flags, and safe result handling. Use when the user explicitly asks to use Qwen for tasks such as code generation, refactoring, debugging, or architectural analysis. Triggers on "use qwen", "use qwen coder", "delegate to qwen", "ask qwen", "second opinion from qwen", "qwen opinion", "continue with qwen", "qwen session".