Sitemap
Oracle Developers

Aggregation of articles from Oracle engineers, Oracle ACEs, and Java Champions on all things Oracle technology. The views expressed are those of the authors and not necessarily of Oracle.

The LangChain4J AI Services API — Creating RAG services with Oracle AI Vector Search, OracleEmbeddingStore, and the Oracle Database 23ai

10 min readAug 27, 2025

--

LangChain4J with the Oracle Database 23ai

by Juarez Junior

Introduction

LangChain4J has introduced a new AI Services high-level API that provides a high-level abstraction for Java developers working on GenAI applications.

AI Services eliminate the need to manually orchestrate multiple low-level components such as ChatModel, ChatMessage, and ChatMemory2. While these classic implementations provide flexibility, they often require extensive boilerplate and intricate coordination across prompt templates, memory management, output parsing, and Retrieval-Augmented Generation (RAG).

Its level of abstraction has been inspired by frameworks like Spring Data JPA, allowing Java developers to use declarative interfaces so that LangChain4J automatically generates proxy implementations that handle the underlying complexity, including chat memory for maintaining context across interactions, tool integration for function calling, and RAG for enriching responses with augmented context.

In this post, I’ll walk you through how I built a simple yet powerful RAG agent using the new AiServices class. This agent answers questions based on embedded knowledge from Oracle Database 23ai, maintains conversation history with chat memory, and incorporates moderation to ensure safe interactions.

So, without further ado, let’s get started!

Prerequisites

Step 1: Configure your Java application

Configure your pom.xml with the following dependencies, which include the Oracle JDBC driver, UCP (Universal Connection Pool), and the LangChain4J modules for Oracle Embedding Store, in-process embedding model, and other dependencies. Ensure you use the latest version of langchain4j-oracle.

Using Maven

    <dependencies>
<!-- Oracle Database 23ai JDBC / UCP -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc17</artifactId>
<version>23.9.0.25.07</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ucp17</artifactId>
<version>23.9.0.25.07</version>
</dependency>

<!-- LangChain4j -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>1.3.0</version>
</dependency>

<!-- LangChain4j Oracle -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-oracle</artifactId>
<version>1.3.0-beta9</version>
</dependency>

<!-- LangChain4j OpenAI -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>1.3.0</version>
</dependency>

<!-- In-process embedding models-->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-embeddings-all-minilm-l6-v2-q</artifactId>
<version>1.3.0-beta9</version>
</dependency>

<!-- SLF4J -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>2.0.9</version>
</dependency>
</dependencies>

Step 2: Define the Java interface with AI Services annotations

The first component of our RAG agent is a simple Java interface, OracleRag.java, with the AI Services annotations. This is where we specify the system prompt template we want to use, moderation details, system input, and other configurations.

package com.oracle.dev.jdbc.langchain4j;

import dev.langchain4j.service.Moderate;
import dev.langchain4j.service.SystemMessage;
import dev.langchain4j.service.UserMessage;

public interface OracleRag {
@SystemMessage("You are an agent who answers questions based on the provided context. Use the retrieved information to give accurate and concise responses.")
@Moderate
String answer(@UserMessage String question);
}

The @SystemMessage annotation sets the agent’s persona and instructions, ensuring responses are based on the retrieved context, based on our RAG integration.

The @UserMessage annotation marks the input parameter, that is, the question provided by the user, while the @Moderate annotation enables automatic content moderation. This declarative style is the magic of AI Services!

Step 2: Define the Java class with the concrete implementation of our AI Services

Here’s the complete implementation of our Java service class, that is, the OracleRagService.java concrete implementation.

package com.oracle.dev.jdbc.langchain4j;

import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;
import static dev.langchain4j.model.openai.OpenAiModerationModelName.TEXT_MODERATION_LATEST;

import java.sql.SQLException;

import javax.sql.DataSource;

import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.allminilml6v2q.AllMiniLmL6V2QuantizedEmbeddingModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.openai.OpenAiModerationModel;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.service.ModerationException;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.oracle.CreateOption;
import dev.langchain4j.store.embedding.oracle.OracleEmbeddingStore;

public class OracleRagService {

public static void main(String[] args) throws SQLException {

// Get Oracle JDBC Pooled DataSource
DataSource dataSource = OracleDbUtils.getPooledDataSource();

// Initialize Oracle Embedding Store
EmbeddingStore<TextSegment> embeddingStore = OracleEmbeddingStore.builder().dataSource(dataSource)
.embeddingTable("rag_ai_service_embeddings", CreateOption.CREATE_OR_REPLACE).build();

// Initialize embedding model
EmbeddingModel embeddingModel = new AllMiniLmL6V2QuantizedEmbeddingModel();

OpenAiModerationModel moderationModel = OpenAiModerationModel.builder().apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(TEXT_MODERATION_LATEST).build();

// Initialize content retriever for RAG
ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder().embeddingStore(embeddingStore)
.embeddingModel(embeddingModel).maxResults(10).minScore(0.5).build();

// Initialize Chat Model (OpenAI in this case)
ChatModel chatModel = OpenAiChatModel.builder().apiKey(System.getenv("OPENAI_API_KEY")).modelName(GPT_4_O_MINI)
.build();

// Initialize chat memory for a single user
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder().maxMessages(50).build();

// Add sample data to the embedding store
TextSegment segment1 = TextSegment
.from("Oracle Database 23ai introduces AI Vector Search for efficient similarity searches.");
Embedding embedding1 = embeddingModel.embed(segment1).content();
embeddingStore.add(embedding1, segment1);

TextSegment segment2 = TextSegment
.from("LangChain4j integrates with Oracle Database 23ai to store and retrieve vector embeddings.");
Embedding embedding2 = embeddingModel.embed(segment2).content();
embeddingStore.add(embedding2, segment2);

// Create RAG agent with memory using AiServices
OracleRag agent = AiServices.builder(OracleRag.class).chatModel(chatModel).moderationModel(moderationModel)
.contentRetriever(contentRetriever).chatMemory(chatMemory).build();

// Use the RAG agent to answer question 1
String query1 = "What are the features of Oracle Database 23ai?";
System.out.println("Query: " + query1);
String response1 = agent.answer(query1);
System.out.println("Response: " + response1 + "\n");

// Memory confirmation check 1
String memory1 = "What did I ask you in my previous question?";
System.out.println("Query: " + memory1);
String answerRelatedToPreviousDb23aiQuestion = agent.answer(memory1);
System.out.println("Response: " + answerRelatedToPreviousDb23aiQuestion + "\n"); // Oracle Database 23ai features

// Use the RAG agent to answer question 1
String query2 = "Which database LangChain4J integrates with to work with vector embeddings?";
System.out.println("Query: " + query2);
String response2 = agent.answer(query2);
System.out.println("Response: " + response2 + "\n");

// Memory confirmation check 2
String memory2 = "What did I ask you in my previous question?";
System.out.println("Query: " + memory2);
String answerRelatedToPreviousLangChain4JQuestion = agent.answer(memory2);
System.out.println("Response: " + answerRelatedToPreviousLangChain4JQuestion + "\n");

// Auto moderation check
try {
String innapropriateMessage = "I WILL KILL YOU!!!";
System.out.println("Message: " + innapropriateMessage + "\n");

} catch (ModerationException e) {
// Text "I WILL KILL YOU!!!" violates content policy
System.out.println(e.getMessage());
}

}
}

Let me walk you through each specific code block of this implementation.

First, we’ll configure a JDBC data source, the OracleEmbeddingStore instance, and the embedding and moderation models.

We start by obtaining a pooled JDBC DataSource, and then initialize the OracleEmbeddingStore, which integrates with the Oracle Database 23ai for storing and querying vector embeddings.

DataSource dataSource = OracleDbUtils.getPooledDataSource();

// Initialize Oracle Embedding Store
EmbeddingStore<TextSegment> embeddingStore = OracleEmbeddingStore.builder().dataSource(dataSource)
.embeddingTable("rag_ai_service_embeddings", CreateOption.CREATE_OR_REPLACE).build();

// Initialize embedding model
EmbeddingModel embeddingModel = new AllMiniLmL6V2QuantizedEmbeddingModel();

OpenAiModerationModel moderationModel = OpenAiModerationModel.builder().apiKey(System.getenv("OPENAI_API_KEY"))
.modelName(TEXT_MODERATION_LATEST).build();

We use the quantized All-MiniLM-L6-v2 model for efficient embedding generation. The moderation model from OpenAI ensures compliance with content policies, and it will be linked to the @Moderate annotation marking our Java interface.

Second, we’ll build a content retriever instance, a chat model instance, and a MessageWindowChatMemory instance.

// Initialize content retriever for RAG
ContentRetriever contentRetriever = EmbeddingStoreContentRetriever.builder().embeddingStore(embeddingStore)
.embeddingModel(embeddingModel).maxResults(10).minScore(0.5).build();

// Initialize Chat Model (OpenAI in this case)
ChatModel chatModel = OpenAiChatModel.builder().apiKey(System.getenv("OPENAI_API_KEY")).modelName(GPT_4_O_MINI)
.build();

// Initialize chat memory for a single user
MessageWindowChatMemory chatMemory = MessageWindowChatMemory.builder().maxMessages(50).build();

Content retrievers help in fetching the relevant segments from the embedding store based on query similarity. In our implementation, the EmbeddingStoreContentRetriever uses the embedding model and store to retrieve up to 10 relevant results with a minimum similarity score of 0.5.

We pair this with the GPT-4o-mini model from OpenAI as the chat model for generating responses. Also, chat memory instance MessageWindowChatMemory keeps track of the last 50 messages, enabling context-aware conversations.

Then, the next code block will add sample data to the vector embedding store, which is ultimately the Oracle Database 23ai. So, let’s populate the store with sample segments about Oracle Database 23ai and LangChain4j.

// Add sample data to the embedding store
TextSegment segment1 = TextSegment
.from("Oracle Database 23ai introduces AI Vector Search for efficient similarity searches.");
Embedding embedding1 = embeddingModel.embed(segment1).content();
embeddingStore.add(embedding1, segment1);

TextSegment segment2 = TextSegment
.from("LangChain4j integrates with Oracle Database 23ai to store and retrieve vector embeddings.");
Embedding embedding2 = embeddingModel.embed(segment2).content();
embeddingStore.add(embedding2, segment2);

The code above will create the vector embeddings, then store them. With that, we’ll be able to execute similarity searches later as expected.

Now, the main code block of our scenario. We’ll create our RAG service by using the AiServices.builder() method to instantiate a service instance.

// Create RAG agent with memory using AiServices
OracleRag agent = AiServices.builder(OracleRag.class).chatModel(chatModel).moderationModel(moderationModel)
.contentRetriever(contentRetriever).chatMemory(chatMemory).build();

Besides, note that this builder wires everything together in a fluent interface style, as we also have a method call to configure the chat model for generation, moderation for safety, content retriever for RAG, and memory for persistence. AI Services handles the orchestration, invoking retrieval, moderation, and response generation transparently and automatically.

Finally, we can now use the RAG service to perform the similarity searches that comprise RAG, memory, and moderation, all together in action.

// Use the RAG agent to answer question 1
String query1 = "What are the features of Oracle Database 23ai?";
System.out.println("Query: " + query1);
String response1 = agent.answer(query1);
System.out.println("Response: " + response1 + "\n");

// Memory confirmation check 1
String memory1 = "What did I ask you in my previous question?";
System.out.println("Query: " + memory1);
String answerRelatedToPreviousDb23aiQuestion = agent.answer(memory1);
System.out.println("Response: " + answerRelatedToPreviousDb23aiQuestion + "\n"); // Oracle Database 23ai features

// Use the RAG agent to answer question 1
String query2 = "Which database LangChain4J integrates with to work with vector embeddings?";
System.out.println("Query: " + query2);
String response2 = agent.answer(query2);
System.out.println("Response: " + response2 + "\n");

// Memory confirmation check 2
String memory2 = "What did I ask you in my previous question?";
System.out.println("Query: " + memory2);
String answerRelatedToPreviousLangChain4JQuestion = agent.answer(memory2);
System.out.println("Response: " + answerRelatedToPreviousLangChain4JQuestion + "\n");

// Auto moderation check
try {
String innapropriateMessage = "I WILL KILL YOU!!!";
System.out.println("Message: " + innapropriateMessage + "\n");
agent.answer(innapropriateMessage);
} catch (ModerationException e) {
// Text "I WILL KILL YOU!!!" violates content policy
System.out.println(e.getMessage());
e.printStackTrace();
}

Step 3: Run the sample application

Now you can use your preferred IDE to compile and run the code.

We’ll see the results of our first similarity search:

Press enter or click to view image in full size
First similarity search

Next, we’ll have a confirmation that memory is working as expected:

First memory verification

Then, our second similarity search:

Press enter or click to view image in full size
Second similarity search

And again, another confirmation that memory is implemented as desired:

Press enter or click to view image in full size
Second memory verification

And finally, we’ll see the message that results from a moderation exception:

Moderation check

To summarize it, as you can see, using the new AI Services high-level API in LangChain4J, we’ve created a RAG service that:

  • Stores embeddings in Oracle Database
  • Retrieves contextually relevant data
  • Remembers conversation history
  • Applies content moderation automatically

Last but not least, here’s the OracleDBUtils.java implementation. Note that I have externalized the JDBC connection details as environment variables, so I do not have to disclose my connection details.

package com.oracle.dev.jdbc.langchain4j;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

import javax.sql.DataSource;

import oracle.ucp.jdbc.PoolDataSource;
import oracle.ucp.jdbc.PoolDataSourceFactory;

public class OracleDbUtils {

// JDBC connection details
private final static String URL = "jdbc:oracle:thin:@localhost:1521/FREEPDB1";
private final static String USERNAME = System.getenv("DB_23AI_USERNAME");
private final static String PASSWORD = System.getenv("DB_23AI_PASSWORD");

public static Connection getConnectionFromPooledDataSource() throws SQLException {
// Create pool-enabled data source instance
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
// set connection properties on the data source
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL(URL);
pds.setUser(USERNAME);
pds.setPassword(PASSWORD);
// Configure pool properties with a Properties instance
Properties prop = new Properties();
prop.setProperty("oracle.jdbc.vectorDefaultGetObjectType", "String");
pds.setConnectionProperties(prop);
// Override any pool properties directly
pds.setInitialPoolSize(10);
// Get a database connection from the pool-enabled data source
Connection conn = pds.getConnection();
return conn;
}

public static DataSource getPooledDataSource() throws SQLException {
// Create pool-enabled data source instance
PoolDataSource pds = PoolDataSourceFactory.getPoolDataSource();
// set connection properties on the data source
pds.setConnectionFactoryClassName("oracle.jdbc.pool.OracleDataSource");
pds.setURL(URL);
pds.setUser(USERNAME);
pds.setPassword(PASSWORD);
// Configure pool properties with a Properties instance
Properties prop = new Properties();
prop.setProperty("oracle.jdbc.vectorDefaultGetObjectType", "String");
pds.setConnectionProperties(prop);
// Override any pool properties directly
pds.setInitialPoolSize(10);
return pds;
}

}

If you need to configure a running instance of Oracle Database 23ai, my previous blog posts listed below provide all details about it.

You can also get a Maven project with a complete code sample on GitHub.

Wrapping it up

That’s it! Our simple service implementation illustrated how easy it is to use the new AI Services API in LangChain4J and its encapsulated capabilities to address several requirements, such as implementing RAG (Retrieval-Augmented Generation) to retrieve context for more accurate and meaningful answers, remember prior questions via chat memory, and throw a ModerationException to flag inappropriate content — all powered by the new high-level AI Services API!

Just to mention, LangChain4j has also introduced a new module langchain4j-agentic tailored specifically for agentic development.

It’s an agentic suite with abstractions and utilities purpose-built in LangChain4j 1.3.0, enabling more sophisticated multi-agent workflows where agents can delegate tasks, share context, and collaborate.

My next blog post about LangChain4J will present the new langchain4j-agentic module.

As one last tip, I invite you to check out the new Database Navigator tool published at JetBrains Marketplace. In a nutshell, it features a robust SQL and PL/SQL editor with advanced capabilities for managing database connections, executing scripts, editing database objects, and modifying data and code with IntelliJ!

I will soon post more examples of scenarios involving LangChain4J, the OracleEmbeddingStore, GraalVM, Micronaut, Quarkus, and other GenAI / Agentic AI-related topics!

We hope you liked this blog post. Stay tuned!

References

LangChain4J

LangChain4J — 1.3.0 — Javadoc

LangChain4J — AI Services

LangChain4J — Oracle Embedding Store

Maven — Oracle Embedding Store (langchain4j-oracle)

Oracle Database 23ai

Oracle AI Vector Search User’s Guide

Oracle JDBC Driver 23ai — Maven Central

Oracle AI Vector Search Technical Architecture

Oracle Database Free Release 23ai — Container Image

Oracle® Database JDBC Java API Reference, Release 23ai

Database Navigator — JetBrains Marketplace

Quickstart: Connect to Oracle Database 23ai using IntelliJ IDEA

Developers Guide For Oracle JDBC on Maven Central

Develop Java applications with Oracle Database

Oracle Developers and Oracle OCI Free Tier

Join our Oracle Developers channel on Slack to discuss Java, GenAI, Agentic AI, JDK, JDBC, GraalVM, Micronaut, Spring Boot, Helidon, Quarkus, Reactive Streams, Cloud, DevOps, SRE, IaC, and other topics!

Build, test, and deploy your applications on Oracle Cloud — for free! Get access to OCI Cloud Free Tier!

--

--

Juarez Junior
Juarez Junior

Written by Juarez Junior

Senior Principal Developer Evangelist @ Oracle ☕️🐍🥑 https://linktr.ee/juarezjunior

Oracle Developers
Oracle Developers

Published in Oracle Developers

Aggregation of articles from Oracle engineers, Oracle ACEs, and Java Champions on all things Oracle technology. The views expressed are those of the authors and not necessarily of Oracle.