Monday, 22 December 2025

Enhancing AI Responses with LangChain4j’s ContentInjector

In any Retrieval-Augmented Generation (RAG) setup, once you’ve retrieved the most relevant content using tools like ContentRetriever and merged them using a ContentAggregator, the final step before generating a response is injecting that content into the actual user query. LangChain4j handles this using a component called the ContentInjector.

In this post, we’ll look specifically at the DefaultContentInjector, the standard implementation of ContentInjector and understand how it works with templates, metadata, and more.

 

What is a ContentInjector?

The ContentInjector is responsible for inserting the aggregated content into the user’s original message. This helps the AI give a better answer by using information from documents or other sources.

 

In LangChain4j, you can plug in your own implementation or use the built-in DefaultContentInjector.

 

How DefaultContentInjector Works?

The DefaultContentInjector appends the retrieved content to the user’s query using a predefined prompt template. Here’s the default prompt structure used:

public static final PromptTemplate DEFAULT_PROMPT_TEMPLATE = PromptTemplate.from(
    """
    {{userMessage}}

    Answer using the following information:
    {{contents}}"""
);

This means:

·      The user's original question ({{userMessage}}) appears at the top.

·      The AI is explicitly instructed to answer using the provided {{contents}}.

 

The injected content comes from the ContentAggregator, which combines results from one or more retrievers.

 


Including Metadata for More Transparency

One powerful feature of DefaultContentInjector is metadata injection. Suppose your content pieces have metadata (like source, author, or documentId); you can choose to include them in the final prompt.

 

Here’s how you can configure it:

DefaultContentInjector.builder()
    .metadataKeysToInclude(List.of("author"))
    .build();

 

With this setup, the author metadata (e.g., document name or URL) will be included in the injected content, making the AI’s responses more auditable and transparent.

 

ContentInjectorDemo.java

package com.sample.app.contentinjector;

import dev.langchain4j.data.document.Metadata;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.rag.content.Content;
import dev.langchain4j.rag.content.injector.DefaultContentInjector;
import java.util.List;
import java.util.Map;

public class ContentInjectorDemo {

  public static void main(String[] args) {
    // Step 1: Create a sample UserMessage
    UserMessage userMessage = UserMessage.from("What are the HR policies?");

    // Step 2: Create sample contents with text segments and metadata
    Metadata metadata = Metadata.from(Map.of("source", "Company portal"));
    Content content1 =
        Content.from(
            TextSegment.from(
                "Our HR policies include flexible work hours, medical leave, and onboarding support.",
                metadata));

    Content content2 =
        Content.from(
            TextSegment.from("You can check your leave balance and benefits on the HR portal."));

    List<Content> contents = List.of(content1, content2);

    // Step 3: Create a DefaultContentInjector with metadata keys to include
    DefaultContentInjector contentInjector =
        DefaultContentInjector.builder().metadataKeysToInclude(List.of("source")).build();

    // Step 4: Inject the contents into the user message
    ChatMessage finalMessage = contentInjector.inject(contents, userMessage);

    // Step 5: Print the final message content (injected prompt)
    System.out.println(((UserMessage) finalMessage).singleText());
  }
}

Output

What are the HR policies?

Answer using the following information:
content: Our HR policies include flexible work hours, medical leave, and onboarding support.
source: Company portal

You can check your leave balance and benefits on the HR portal.

We can configure the contentAggregator using RetrievalAugmentor and then use it together with AiServices to build the assistant.

// Create a retrieval augmentor with a dynamic content retriever routed via a router
RetrievalAugmentor retrievalAugmentor =
    DefaultRetrievalAugmentor.builder()
        .queryTransformer(expandingQueryTransformer)
        .queryRouter(router)
        .contentAggregator(new DefaultContentAggregator())
        .contentInjector(new DefaultContentInjector())
        .build();

// Build the assistant using the configured retrieval augmentor
ChatAssistant chatAssistant =
    AiServices.builder(ChatAssistant.class)
        .chatModel(chatModel)
        .retrievalAugmentor(retrievalAugmentor)
        .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
        .build();

 

Find the below working Application.

 

ContentInjectorDemoFullApp.java

 

package com.sample.app.contentinjector;

import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.bgesmallenv15q.BgeSmallEnV15QuantizedEmbeddingModel;
import dev.langchain4j.model.ollama.OllamaChatModel;
import dev.langchain4j.rag.DefaultRetrievalAugmentor;
import dev.langchain4j.rag.RetrievalAugmentor;
import dev.langchain4j.rag.content.aggregator.DefaultContentAggregator;
import dev.langchain4j.rag.content.injector.DefaultContentInjector;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.rag.query.router.LanguageModelQueryRouter;
import dev.langchain4j.rag.query.transformer.ExpandingQueryTransformer;
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.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ContentInjectorDemoFullApp {
  interface ChatAssistant {
    String chat(String userMessage);
  }

  public static void main(String[] args) {

    // Initialize LLM model for routing and chatting
    OllamaChatModel chatModel =
        OllamaChatModel.builder().baseUrl("http://localhost:11434").modelName("llama3.2").build();

    // Shared embedding model
    EmbeddingModel embeddingModel = new BgeSmallEnV15QuantizedEmbeddingModel();

    // Create embedding stores
    EmbeddingStore<TextSegment> hrStore = new InMemoryEmbeddingStore<>();
    EmbeddingStore<TextSegment> itStore = new InMemoryEmbeddingStore<>();
    EmbeddingStore<TextSegment> financeStore = new InMemoryEmbeddingStore<>();

    // Sample data for ingestion

    List<String> hrDocs =
        List.of(
            "Our HR policies include flexible work hours, medical leave, and onboarding support.",
            "You can check your leave balance and benefits on the HR portal.",
            "Employee handbooks and code of conduct documents are available on the intranet.",
            "Annual performance reviews are conducted in Q1 of each year.",
            "We provide mental wellness programs and employee assistance plans (EAP).",
            "New employees must complete their joining formalities within the first week.",
            "HR holds town halls every quarter to discuss policy changes and updates.",
            "Exit interviews are mandatory and help improve employee retention practices.");

    List<String> itDocs =
        List.of(
            "If you're facing issues with VPN, restart your system and try again.",
            "Password resets can be done via the IT Helpdesk portal.",
            "For software installation requests, raise a ticket through the ServiceNow portal.",
            "Two-factor authentication is mandatory for accessing company email remotely.",
            "Laptop issues should be reported to the IT asset management team.",
            "New joiners will receive their device credentials within 24 hours of onboarding.",
            "We recommend using the Chrome browser for all internal web tools.",
            "The weekly IT newsletter includes patch updates and known issues.");

    List<String> financeDocs =
        List.of(
            "Payslips are generated on the 5th of every month and available on the finance dashboard.",
            "You can file business travel reimbursements through the expense portal.",
            "Employees must submit receipts within 15 days for expense claims.",
            "Annual tax declarations must be uploaded to the HRMS by January 31.",
            "Salary revisions are processed in March and reflected in April pay.",
            "All invoice-related queries should be addressed to finance@company.com.",
            "The company reimburses professional certification exam fees up to ₹10,000.",
            "You can track investment proofs under the ‘My Tax’ section on the intranet.");

    // Ingest documents into respective stores
    EmbeddingStoreIngestor.ingest(convertToDocuments(hrDocs), hrStore);
    EmbeddingStoreIngestor.ingest(convertToDocuments(itDocs), itStore);
    EmbeddingStoreIngestor.ingest(convertToDocuments(financeDocs), financeStore);

    // Create content retrievers
    ContentRetriever hrRetriever =
        EmbeddingStoreContentRetriever.builder()
            .embeddingStore(hrStore)
            .embeddingModel(embeddingModel)
            .maxResults(3)
            .minScore(0.7)
            .displayName("hrRetriever")
            .build();

    ContentRetriever itRetriever =
        EmbeddingStoreContentRetriever.builder()
            .embeddingStore(itStore)
            .embeddingModel(embeddingModel)
            .maxResults(3)
            .minScore(0.7)
            .displayName("itRetriever")
            .build();

    ContentRetriever financeRetriever =
        EmbeddingStoreContentRetriever.builder()
            .embeddingStore(financeStore)
            .embeddingModel(embeddingModel)
            .maxResults(3)
            .minScore(0.7)
            .displayName("financeRetriever")
            .build();

    Map<ContentRetriever, String> retrieverToDescription = new HashMap<>();
    retrieverToDescription.put(
        hrRetriever,
        "Provides information on leave policies, benefits, onboarding, and other HR-related topics.");
    retrieverToDescription.put(
        itRetriever,
        "Handles technical queries such as VPN access, email issues, password resets, and software installations.");
    retrieverToDescription.put(
        financeRetriever,
        "Answers questions related to reimbursements, payslips, taxation, invoices, and financial approvals.");

    // Create LLM-based router
    LanguageModelQueryRouter router =
        LanguageModelQueryRouter.builder()
            .chatModel(chatModel)
            .retrieverToDescription(retrieverToDescription)
            .build();

    // Expand queries (optional step for better retrieval)
    ExpandingQueryTransformer expandingQueryTransformer =
        new ExpandingQueryTransformer(chatModel, 3);

    // Create retrieval augmentor with dynamic contentRetriever via router
    RetrievalAugmentor retrievalAugmentor =
        DefaultRetrievalAugmentor.builder()
            .queryTransformer(expandingQueryTransformer)
            .queryRouter(router)
            .contentAggregator(new DefaultContentAggregator())
            .contentInjector(new DefaultContentInjector())
            .build();

    // Build the assistant
    ChatAssistant chatAssistant =
        AiServices.builder(ChatAssistant.class)
            .chatModel(chatModel)
            .retrievalAugmentor(retrievalAugmentor)
            .chatMemory(MessageWindowChatMemory.withMaxMessages(10))
            .build();

    // Run a conversation
    List<String> userQueries =
        List.of(
            "How do I reset my email password?",
            "Where can I see my payslip for last month?",
            "What is the company policy on medical leave?",
            "How to claim business travel reimbursement?",
            "I'm facing issues connecting to the VPN.");

    for (String userQuery : userQueries) {
      System.out.println("User: " + userQuery);
      String answer = chatAssistant.chat(userQuery);
      System.out.println("Assistant: " + answer);
      System.out.println("-----------------------------------------------");
    }
  }

  private static List<Document> convertToDocuments(List<String> texts) {
    List<Document> documents = new ArrayList<>();
    for (String text : texts) {
      documents.add(Document.from(text));
    }
    return documents;
  }
}

 

Output

SLF4J(W): No SLF4J providers were found.
SLF4J(W): Defaulting to no-operation (NOP) logger implementation
SLF4J(W): See https://www.slf4j.org/codes.html#noProviders for further details.
User: How do I reset my email password?
Assistant: To reset your email password, follow these steps:

1. Log in to the IT Helpdesk portal.
2. Follow the prompts to initiate a password reset request.
3. Complete any additional verification steps as required.

Note: You may need to use two-factor authentication to access company email remotely. If you're having trouble accessing the IT Helpdesk portal, try restarting your system and then attempting again.
-----------------------------------------------
User: Where can I see my payslip for last month?
Assistant: To view your payslip for last month, follow these steps:

1. Log in to the finance dashboard.
2. Look for the section related to payroll and benefits.
3. You should be able to see your payslip for the previous month.

Note: The finance dashboard is available on the intranet, so make sure you have access to it.
-----------------------------------------------
User: What is the company policy on medical leave?
Assistant: Unfortunately, the provided information does not specifically state the company's policy on medical leave. However, it does mention that medical leave is included in the HR policies, which suggests that there are guidelines and procedures in place for managing medical leaves.

To find more specific information about the company's policy on medical leave, you may need to check other resources, such as the employee handbook or speak with HR directly.
-----------------------------------------------
User: How to claim business travel reimbursement?
Assistant: To claim business travel reimbursement:

1. Log in to the expense portal.
2. Submit your receipts for eligible business expenses within 15 days of the trip.

Note: The specific steps and deadlines may vary depending on the company's policies, so it's best to check the employee handbook or consult with HR for more detailed information.
-----------------------------------------------
User: I'm facing issues connecting to the VPN.
Assistant: To resolve your issue connecting to the VPN:

1. Restart your system.
2. Try reconnecting to the VPN again.

If you're still experiencing issues, you may want to consider reaching out to the IT Helpdesk for further assistance or support.
-----------------------------------------------

 

 

Previous                                                    Next                                                    Home

No comments:

Post a Comment