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 Agentic AI API — Creating RAG Agents with Oracle AI Vector Search, OracleEmbeddingStore, and the Oracle AI Database 26ai

9 min readAug 29, 2025

--

Press enter or click to view image in full size
LangChain4J with the Oracle AI Database 26ai

by Juarez Junior

Introduction

In this post, we’ll explore how to build an Agentic RAG (Retrieval-Augmented Generation) application with LangChain4j, which uses the Oracle AI Database 26ai as a vector store and Oracle AI Vector Search.

Our LangChain4J RAG agent will act as a technical support agent that provides descriptions for Oracle Database Error Messages [ORA Index] by searching a simple knowledge base stored in the Oracle AI Database 26ai as vector embeddings.

In a nutshell, our technical support agentic scenario will present:

  • How to use the dev.langchain4j.agentic.AgenticServices class to create agents with LangChain4J and its Agents and Agentic AI API.
  • How to use the @Tool annotation (dev.langchain4j.agent.tool.Tool) to expose database search functionality as a callable agent function.
  • Use the OracleEmbeddingStore and the Oracle AI Database 26ai to store vector embeddings, and then retrieve them using a content retriever, as I already demonstrated in a previous blog post, but using a slightly different strategy, as I split the ingestion and retrieval operations between the OracleRagAgent.java class (the RAG agent class) and the OracleDatabaseTool.java class (the tool class). However, behind the scenes, everything is still implemented using Oracle AI Vector Search.
  • Also note that in comparison with the sample code I created in a previous blog post, another difference is that now I’m using the AgenticServices class instead of the AgenticServices one, so once again, note that I’m using the Agents and Agentic AI API.

Given that, primarily the components used in this blog post belong to the langchain4j-agentic module available in LangChain4J, so they relate to its Agents and Agentic AI API — some examples are the AgenticService class (dev.langchain4j.agentic.AgenticServices) and the @Tool annotation (dev.langchain4j.agent.tool.Tool).

Interesting to mention that I did not find a public/published Javadoc for the langchain4j-agentic module used in this blog post, so many references were left in italic to highlight that. Otherwise, I’d have included the links to allow you to easily check the documentation as you read my blog post, as I always do in all my blog posts.

Besides, breaking changes may happen if so (remember this is about open-source software). I did my best to create a simple scenario that will allow you to kick the tyres and explore the Agents and Agentic AI API and the langchain4j-agentic module, combined with our Oracle embedding store module (langchain4j-oracle / OracleEmbeddingStore) to create a simple Agentic RAG scenario.

Anyway, I may update this blog post with the Javadoc link and address any other breaking changes in the near future.

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, LangChain4J Agentic Framework, in-process embedding model, and other dependencies. Ensure you use the latest version of langchain4j-oracle.

Using Maven

    <properties>
<java.version>17</java.version>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jdbc.version>23.9.0.25.07</jdbc.version>
</properties>

<dependencies>
<!-- Oracle AI Database JDBC / UCP -->
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ojdbc17</artifactId>
<version>${jdbc.version}</version>
</dependency>
<dependency>
<groupId>com.oracle.database.jdbc</groupId>
<artifactId>ucp17</artifactId>
<version>${jdbc.version}</version>
</dependency>

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

<!-- LangChain4j Agentic AI -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-agentic</artifactId>
<version>1.3.0-beta9</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 model -->
<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 an agent contract interface

The agent interface describes the contract of our service and its role, that is, how the agent should act. I use the @Agent and @UserMessage annotations from LangChain4j to define its contract and behavior.

This declarative approach allows the LangChain4j framework to generate a runtime implementation that uses the instructions provided by the annotations, which is an interesting highlight here.

package com.oracle.dev.jdbc.langchain4j;

import dev.langchain4j.agentic.Agent;
import dev.langchain4j.service.UserMessage;
import dev.langchain4j.service.V;

public interface OracleRag {

@UserMessage("""
You are a technical support agent working with Oracle AI Database 26ai.
Generate error code descriptions for the provided Oracle AI Database error codes (the error code pattern is ORA-*****)
with a single sentence containing the description of the given error code.
The error code is {{errorCode}}.
""")
@Agent("Generates an error code description based on the provided Oracle Database error cocde")
String getErrorCodeDescription(@V("errorCode") String errorCode);

}

In a nutshell, here’s what this interface defines:

  • The @UserMessage annotation defines the user message instruction and template.
  • @Agent annotation marks the method as an agentic function.
  • The @V annotation binds method parameters into the prompt template.

Step 3: Create a tool for Oracle Database Integration

Now, we implement a retrieval tool that performs searches against the knowledge base, which is supported by vector embeddings created from ingesting a simple text file oracle-error-codes-descriptions.txt located under /resources containing the Oracle Database error codes and their descriptions.

Similar to my previous blog post, it’s backed by the OracleEmbeddingStore and a content retriever instance. Note that the Java method searchKnowledgeBase() uses the @Tool annotation to make it callable during agent execution. The RAG agent will call this tool automatically as designed.

package com.oracle.dev.jdbc.langchain4j;

import java.util.List;

import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.rag.content.Content;
import dev.langchain4j.rag.content.retriever.ContentRetriever;
import dev.langchain4j.rag.content.retriever.EmbeddingStoreContentRetriever;
import dev.langchain4j.rag.query.Query;
import dev.langchain4j.store.embedding.EmbeddingStore;

public class OracleDatabaseTool {

private final ContentRetriever retriever;

public OracleDatabaseTool(EmbeddingStore<TextSegment> embeddingStore, EmbeddingModel embeddingModel) {
this.retriever = EmbeddingStoreContentRetriever.builder().embeddingStore(embeddingStore)
.embeddingModel(embeddingModel).maxResults(10).minScore(0.5).build();
}

@Tool("Searches the Oracle Database knowledge base for the respective Oracle error code description to answer a tech support agent's question")
public String searchKnowledgeBase(String query) {
List<Content> contents = retriever.retrieve(Query.from(query));
if (contents == null || contents.isEmpty()) {
return "";
}
StringBuilder sb = new StringBuilder();
for (Content content : contents) {
TextSegment segment = content.textSegment();
if (segment != null) {
sb.append(segment.text()).append('\n');
}
}
return sb.toString();
}
}

Again, our tool supports this core functionality:

  • The @Tool annotation makes the Java method searchKnowledgeBase() callable by the agent.
  • An EmbeddingStoreContentRetrieverinstance is used to connect the embedding model and the Oracle Database vector store.

Step 4: Create an Agentic AI application class

This step wires all the main components together in a runnable Agentic AI application. Here, we combine the chat model, the embedding model, and the OracleEmbeddingStore. It also helps in ingesting the sample Oracle Database error codes and descriptions into the vector store, creates the tool, instantiates the agent (dev.langchain4j.agentic.AgenticServices), and then runs the sample questions.

package com.oracle.dev.jdbc.langchain4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import javax.sql.DataSource;

import dev.langchain4j.agentic.AgenticServices;
import dev.langchain4j.data.document.Document;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.chat.ChatModel;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.oracle.CreateOption;
import dev.langchain4j.store.embedding.oracle.OracleEmbeddingStore;
import static dev.langchain4j.model.openai.OpenAiChatModelName.GPT_4_O_MINI;

public class OracleRagAgent {

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

DataSource dataSource = OracleDbUtils.getPooledDataSource();

// Chat model
ChatModel chatModel = OpenAiChatModel.builder().apiKey(System.getenv("OPENAI_API_KEY")).modelName(GPT_4_O_MINI)
.build();

// Embedding store (Oracle DB as vector store)
EmbeddingModel embeddingModel = new dev.langchain4j.model.embedding.onnx.allminilml6v2q.AllMiniLmL6V2QuantizedEmbeddingModel();
EmbeddingStore<TextSegment> embeddingStore = OracleEmbeddingStore.builder().dataSource(dataSource)
.embeddingTable("rag_agent_embeddings", CreateOption.CREATE_IF_NOT_EXISTS).build();

// Index a few sample documents (simple demo ingestion)
addErrorCodes(embeddingModel, embeddingStore);

// Create a knowledge-base tool over the Oracle embedding store
OracleDatabaseTool kbTool = new OracleDatabaseTool(embeddingStore, embeddingModel);

// Build an Agentic service that uses the Oracle Database tool
OracleRag techSupportAgent = AgenticServices.agentBuilder(OracleRag.class).chatModel(chatModel)
.outputName("errorCodeDescription").tools(kbTool).build();

// Ask a question. The tech support agent will use the Oracle Database tool to
// retrieve context
String errorCode = "ORA-00060";
String answer = techSupportAgent.getErrorCodeDescription(errorCode);

System.out.println("Query: What is the error code description for ORA-00060?");
System.out.println("Response: " + answer + "\n");

errorCode = "ORA-28018";
answer = techSupportAgent.getErrorCodeDescription(errorCode);

System.out.println("Query: What is the error code description for ORA-28018?");
System.out.println("Response: " + answer + "\n");

}

public static void addErrorCodes(EmbeddingModel embeddingModel, EmbeddingStore<TextSegment> embeddingStore) {
List<Document> documents = new ArrayList<>();

try (var inputStream = OracleRagAgent.class.getClassLoader()
.getResourceAsStream("oracle-error-codes-descriptions.txt")) {
if (inputStream == null) {
throw new IllegalStateException("Resource oracle-error-codes-descriptions.txt not found on classpath. "
+ "Make sure it is located under src/main/resources/");
}

try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
documents = reader.lines().filter(line -> !line.trim().isEmpty()).map(Document::from)
.collect(Collectors.toList());
}

} catch (IOException e) {
throw new RuntimeException("Failed to load oracle-error-codes-descriptions.txt from resources", e);
}

if (embeddingModel != null && embeddingStore != null) {
for (Document doc : documents) {
Embedding embedding = embeddingModel.embed(doc.text()).content();
embeddingStore.add(embedding, TextSegment.from(doc.text()));
}
}
System.out.println("Loaded " + documents.size() + " Oracle error codes into the embedding store.\n");
}

}

Essentially, the Agentic AI application comprises the following functionality:

  • Agentic service creation with AgenticServices.agentBuilder() method.
  • Automatic tool usage when answering technical support questions.
  • A Java method to ingest the Oracle Database error code and descriptions and store them using the OracleEmbeddingStore.

Step 5: Run the sample application

When you run this, the agent loads error codes into the vector store, builds a retrieval tool, and then answers technical support queries.

Press enter or click to view image in full size
Tech support queries about Oracle DB error codes and the respective responses with the error code descriptions

Note, the core focus of this blog post is to introduce how you can use the dev.langchain4j.agentic.AgenticServices class to create Agentic AI applications in Java with LangChain4J. Needless to say, the sample data set I ingested and used to store the vector embeddings from the Oracle Database error codes and descriptions (again, a text file named oracle-error-codes-descriptions.txt under /resources) does not comprise an exhaustive list of all Oracle Database error codes and descriptions, as it’s for illustrative purposes only.

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 javax.sql.DataSource;
import java.util.Properties;

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_USERNAME");
private final static String PASSWORD = System.getenv("DB_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 AI Database 26ai, 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! In this post, we built a simple Agentic AI application using LangChain4j, the OracleEmbeddingStore, Oracle AI Vector Search, and the Oracle AI Database 26ai. The key takeaway is that Agentic AI is not just about simple interactions where LLM models answer questions, but creating intelligent agents that can reason, retrieve data with RAG and tools, and perform actions within your existing Java applications.

This blog post only introduced the basic building blocks of creating Agentic AI applications in Java with LangChain4J and the OracleEmbeddingStore, so my focus was on presenting the basic components, their integration, and the dev.langchain4j.agentic.AgenticServices API. In my next blog post about this topic, I’ll show you how to integrate two different agents that can collaborate to execute a more elaborate scenario.

However, before that, we’ll explore how to use MCP (Model Context Protocol) with LangChain4J and the Oracle AI Database 26ai!

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 — Agents and Agentic AI

LangChain4J — Oracle Embedding Store

Maven — Oracle Embedding Store (langchain4j-oracle)

Oracle AI Database 26ai

Oracle AI Vector Search User’s Guide

Oracle JDBC Driver

Oracle AI Vector Search Technical Architecture

Oracle AI Database Free Release — Container Image

Oracle AI Database JDBC Java API Reference

Database Navigator — JetBrains Marketplace

Quickstart: Connect to Oracle AI Database 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.