Ir al contenido principal

Tutorial de memoria con herramientas en LangChain4j: crea un agente Java con Oracle AI Database

Aprende a crear un agente sencillo de LangChain4j que recuerde notas importantes entre conversaciones.
Actualizado 19 sept 2026  · 8 min leer

Explora con IA

ChatGPTClaudePerplexity

En este tutorial, recorreremos un pequeño módulo de Java que expone herramientas personalizadas, guarda notas como JSON en Oracle AI Database y las recupera con búsqueda de texto completo.

Si estás creando agentes de IA, el historial de chat de corto plazo suele no ser suficiente. Los asistentes reales a menudo necesitan una pequeña cantidad de memoria duradera para guardar hechos importantes y recuperarlos más tarde.

Este tutorial muestra cómo implantar ese patrón con un módulo mínimo en Java. Usarás LangChain4j tools, una interfaz de agente sencilla y Oracle AI Database como almacén subyacente. En lugar de añadir embeddings o una canalización completa de retrieval, este ejemplo mantiene el diseño deliberadamente pequeño: las notas se guardan como documentos JSON y se buscan con Oracle Text.

Al final, entenderás cómo el módulo conecta:

  • Una interfaz de agente LangChain4j
  • Dos métodos personalizados con @Tool
  • Un repositorio JDBC para almacenamiento duradero de notas
  • Una aplicación de consola que enlaza todo el flujo

¿Qué es la memoria basada en herramientas en LangChain4j?

LangChain4j te permite exponer métodos Java ordinarios como herramientas que un modelo puede llamar cuando necesita capacidades externas. En este módulo, esas capacidades están orientadas a la memoria:

  • searchMemories(String question) encuentra notas guardadas relevantes
  •  storeMemory(String note) escribe una nueva nota duradera

Esto es distinto del historial estándar de chat que almacena mensajes en una ventana de contexto en memoria.

La memoria persistente basada en herramientas decide cuándo necesita buscar o guardar notas, y los datos subyacentes viven en una base de datos.

Este patrón es útil cuando quieres que un agente recuerde hechos como notas de incidencias, recordatorios de runbooks o acciones de seguimiento sin construir demasiado pronto un sistema de retrieval más grande. 

La memoria persistente respaldada por base de datos también puede ser accedida en paralelo por uno o más agentes sin contención de datos.

Flow showing a user request reaching a LangChain4j assistant, which searches or stores durable JSON notes in Oracle AI Database before returning a concise response.

La memoria basada en herramientas permite que el asistente recupere notas existentes o guarde nuevas notas en una base de datos duradera.

Funciones clave del módulo LangChain4j y Oracle AI DB

Este ejemplo es pequeño, pero demuestra varias ideas importantes de diseño de agentes.

Herramientas personalizadas de LangChain4j

La clase MemoryTools expone dos métodos anotados con @Tool. Eso significa que LangChain4j puede ponerlos a disposición del agente sin código envoltorio adicional.

// Retrieve relevant notes and format them for the assistant.
 @Tool(
     	"Search saved memory notes when earlier context might help answer the user."
 )
 public String searchMemories(String question) {
 	List<MemoryRepository.Note> matches = repository.search(question, 3);
 	if (matches.isEmpty()) {
     	return "No saved memory matched that question.";
 	}

 	StringBuilder builder = new StringBuilder();
 	for (int i = 0; i < matches.size(); i++) {
     	MemoryRepository.Note note = matches.get(i);
     	builder.append(i + 1)
             	.append(". [M")
             	.append(note.id())
             	.append("] ")
             	.append(note.text());
     	if (i + 1 < matches.size()) {
         	builder.append('\n');
     	}
 	}
 	return builder.toString();
 }

Este método hace dos cosas útiles. Delegar la recuperación al repositorio y dar formato a los resultados de forma que el modelo los entienda, por ejemplo [M12] some note text. 

Así, la salida de la herramienta es fácil de citar por el agente en su respuesta final.

Memoria duradera en Oracle AI Database

Las notas se almacenan en una tabla agent_memories como documentos JSON nativos. Cada fila contiene:

  • Una columna de identidad id
  • Una columna JSON llamada memory_doc
  • Una marca de tiempo created_at

El módulo inicializa el esquema automáticamente:

-- Create a JSON-backed table for durable agent notes.
 CREATE TABLE IF NOT EXISTS agent_memories (
 	id NUMBER GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
 	memory_doc JSON NOT NULL,
 	created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
 )

Esto mantiene el modelo de almacenamiento simple y, aun así, te da datos estructurados y durabilidad gestionada por la base de datos.

Búsqueda por palabras clave con Oracle Text

El repositorio no genera embeddings. En su lugar, extrae palabras clave de la pregunta del usuario, elimina stop words, construye una expresión de Oracle Text con ACCUM y busca el contenido del documento JSON con json_textcontains.

Esto resulta especialmente útil para identificadores y términos operativos como CHG2145, donde el ajuste exacto o casi exacto de tokens suele importar más que la similitud semántica.

Un flujo mínimo por consola

La clase Application crea el repositorio, inicializa el esquema, precarga algunas notas, construye el modelo de chat y arranca un bucle en terminal. Así el ejemplo es fácil de ejecutar y de adaptar a otro servicio más adelante.

Primeros pasos con tu proyecto de memoria en LangChain4j

Antes de ejecutar el módulo, asegúrate de que tu entorno local cumple los requisitos del proyecto.

Requisitos previos

Necesitas:

  • Java 21 o superior
  • Maven 3.9 o superior
  • Oracle AI Database Free en local o remoto
  • Una variable de entorno OPENAI_API_KEY

El proyecto Maven depende de:

  • langchain4j
  • langchain4j-open-ai
  • ojdbc11
  • JUnit y Testcontainers para pruebas de integración

Ejecuta el ejemplo

Primero, define tu clave de API de OpenAI:

# Set the API key used by the chat model.
 export OPENAI_API_KEY=<your key>

Después, ejecuta la app desde el directorio del proyecto:

# Compile and run the sample with the database connection details.
 mvn compile exec:java \
     -Dexec.args="jdbc:oracle:thin:@localhost:1521/freepdb1 testuser testpwd"

La aplicación espera exactamente tres argumentos:

1. URL JDBC

2. Nombre de usuario de la base de datos

3. Contraseña de la base de datos

Si faltan, Application.main() lanza un IllegalArgumentException. Si falta OPENAI_API_KEY, lanza un IllegalStateException.

Qué ocurre al iniciar

Cuando la app arranca:

1. Crea un MemoryRepository

2. Inicializa el esquema de base de datos

3. Precaega tres notas de ejemplo si la tabla está vacía

4. Construye un OpenAiChatModel

5. Conecta el modelo y las herramientas en AiServices

6. Inicia un bucle CLI que lee entradas hasta que escribes exit

Esto te permite probar la recuperación de memoria de inmediato sin insertar filas a mano primero.

Cómo crear un agente Java con memoria en LangChain4j

Ahora recorramos el módulo en el mismo orden en que lo usa la aplicación.

Define la interfaz del agente

LangChain4j usa una interfaz de Java para describir el contrato del agente. En este proyecto, esa interfaz es MemoryAssistant.

// Define the interface that receives each user message.
 public interface MemoryAssistant {

 	@SystemMessage("""
         	You are a simple assistant with access to durable memory notes.
         	Use searchMemories when earlier notes might help answer the user.
         	If the user explicitly asks you to remember something for later, call storeMemory.
         	Keep answers concise.
         	""")
 	@UserMessage("{{message}}")
 	String chat(@V("message") String message);
 }

Lo importante es el mensaje del sistema. Indica al modelo cuándo usar cada herramienta:

  • Buscar cuando notas anteriores puedan ayudar
  • Guardar cuando el usuario pida explícitamente que se recuerde algo

Es un buen ejemplo de política de herramientas ligera. El prompt es breve, pero lo bastante específico para guiar la selección de herramientas.

Expón las operaciones de memoria como herramientas

El agente no habla directamente con el repositorio. En su lugar, llama a la capa MemoryTools.

Este es el método de almacenamiento:

// Save a note and return its generated memory identifier.
 @Tool(
     	"Store a new memory note when the user asks you to remember something "
             	+ "important for later."
 )
 public String storeMemory(String note) {
 	long id = repository.store(note);
 	return "Stored memory M" + id + ".";
 }

Esto es intencionadamente compacto. Un usuario podría decir:

Remember that the next shift should verify the checkout retry queue stays below 50.

Si el modelo sigue correctamente el prompt del sistema, llamará a storeMemory(), el repositorio persistirá la nota y la herramienta devolverá algo como:

Stored memory M4.

Más tarde, el usuario puede preguntar:

What should the next shift verify?

En ese momento, el agente puede llamar a searchMemories() y usar la nota devuelta para responder.

Crea la capa de repositorio

El repositorio es donde vive la mayor parte de la lógica de memoria duradera. Sigue siendo JDBC puro, lo que facilita inspeccionar el diseño.

A alto nivel, MemoryRepository se encarga de:

  • Creación del esquema
  • Precarga opcional
  • Inserción de notas
  • Extracción de palabras clave
  • Búsqueda con Oracle Text
  • Recuperación alternativa de notas recientes cuando no hay palabras clave útiles

La ruta de inserción normaliza y valida el texto de la nota antes de almacenarlo:

// Normalize the note, store its JSON document, and return the generated ID.
 public long store(String note) {
 	String normalized = normalizeNote(note);
 	Instant createdAt = Instant.now();
 	try (
         	Connection connection = dataSource.getConnection();
         	PreparedStatement statement = connection.prepareStatement(
                 	INSERT_SQL,
                 	new String[]{"id"})
 	) {
     	statement.setObject(
             	1,
                 toJsonDocument(normalized, createdAt),
             	OracleTypes.JSON
     	);
     	statement.setTimestamp(2, Timestamp.from(createdAt));
     	statement.executeUpdate();
     	try (ResultSet keys = statement.getGeneratedKeys()) {
         	if (keys.next()) {
             	return keys.getLong(1);
         	}
     	}
     	throw new IllegalStateException(
             	"No generated id returned for stored memory."
     	);
 	} catch (SQLException e) {
     	throw new RuntimeException("Failed to store memory", e);
 	}
 }

El propio documento JSON tiene este aspecto:

{
 	"noteText": "Runbook note: use section 3 to validate checkout recovery after a rollback.",
 	"createdAt": "2026-04-27T17:00:00Z"
 }

La estructura es simple, pero suficiente para soportar persistencia y búsqueda de texto.

Entiende la estrategia de búsqueda

La parte más interesante del repositorio es el método search():

// Search by extracted keywords or fall back to recent notes.
 public List<Note> search(String question, int maxResults) {
 	List<String> keywords = extractKeywords(question);
 	if (keywords.isEmpty()) {
     	return findRecent(maxResults);
 	}
 	return textSearch(buildTextExpression(keywords), maxResults);
 }

Este método sigue un flujo práctico:

1. Tokeniza la pregunta del usuario

2. Pasa los tokens a minúsculas

3. Elimina stop words como the, what y remember

4. Conserva tokens cortos significativos cuando incluyan dígitos

5. Construye una expresión de Oracle Text con ACCUM

6. Busca en los datos JSON con json_textcontains

Ese detalle de “se permiten dígitos” es importante. Ayuda a que términos como CHG2145 sobrevivan a la extracción de palabras clave, justo el tipo de búsquedas con identificadores que muchos agentes operativos necesitan.

Six-step flow from a user question through keyword extraction, stop-word removal, an ACCUM expression, and json_textcontains to ranked memory notes.

El flujo de búsqueda convierte una pregunta en una expresión de palabras clave y devuelve notas ordenadas por relevancia.

La consulta SQL de búsqueda es:

-- Rank matching notes by Oracle Text relevance.
 SELECT id,
    	JSON_VALUE(
        	memory_doc,
        	'$.noteText' RETURNING VARCHAR2(2000 CHAR)
    	) AS note_text,
    	created_at,
    	SCORE(1) AS text_score
 FROM agent_memories
 WHERE JSON_TEXTCONTAINS(memory_doc, '$', ?, 1)
 ORDER BY SCORE(1) DESC, id DESC
 FETCH FIRST ? ROWS ONLY

Si la pregunta no produce palabras clave útiles, el repositorio recurre a notas recientes. Eso da al agente un valor por defecto razonable en lugar de devolver demasiado pronto una búsqueda vacía.

Conecta todo en la aplicación

La aplicación une las piezas con AiServices.builder():

// Build the chat model, then attach the memory tools to the assistant.
 ChatModel chatModel = OpenAiChatModel.builder()
     	.apiKey(apiKey)
         .modelName("gpt-5-nano")
     	.build();

 MemoryAssistant assistant = AiServices.builder(MemoryAssistant.class)
     	.chatModel(chatModel)
     	.tools(new MemoryTools(repository))
     	.build();

Este es el patrón completo en un solo lugar:

  • Construir un modelo de chat
  • Exponer métodos como herramientas
  • Vincular esas herramientas a un agente guiado por interfaz

Después de eso, el bucle de consola simplemente pasa la entrada del usuario a assistant.chat(input).

Prueba los prompts de demo

La aplicación imprime algunos prompts de inicio:

What do we know about CHG2145?
 Remember that the next shift should verify the checkout retry queue stays below 50.
 What should the next shift verify?

Estos prompts son útiles porque ejercitan todo el ciclo de memoria:

  • Recuperar una nota precargada existente
  • Guardar una nota nueva
  • Recuperar más tarde la nota recién guardada

Consejos y resolución de problemas

Este módulo es deliberadamente acotado, lo que es una ventaja cuando aprendes el patrón. Aun así, conviene tener presentes algunos detalles prácticos.

Mantén las instrucciones de la herramienta explícitas

El prompt del sistema indica al modelo que solo guarde memoria cuando el usuario lo pida explícitamente. Eso ayuda a reducir escrituras accidentales. Si amplías el prompt sin cuidado, el modelo puede empezar a persistir notas de poco valor.

Trátalo como memoria de notas duraderas, no como retrieval de conocimiento completo

Este repositorio es ideal para notas operativas cortas, recordatorios e identificadores. Aún no es un sistema completo de generación aumentada con recuperación con embeddings, particionado, búsqueda híbrida o reordenamiento.

Usa Oracle Text cuando los identificadores importen

Para cadenas como IDs de incidencias, números de tickets y referencias de solicitudes de cambio, la recuperación orientada a palabras clave suele ser un mejor punto de partida que la búsqueda semántica. Este ejemplo refleja claramente ese compromiso.

Valida por separado tu base de datos y la configuración de Docker

Las pruebas del proyecto son de integración y se ejecutan con Testcontainers. Si Docker no está disponible, mvn test fallará antes de que se ejecute la lógica del repositorio. Eso es un problema de entorno, no necesariamente un bug de la aplicación.

Ejecuta las pruebas con:

# Run the integration tests.
 mvn test

Las pruebas cubren dos comportamientos importantes:

  • Las notas se pueden guardar y recuperar a través del repositorio
  • La salida de las herramientas incluye IDs de memoria y texto relevante de la nota

Empieza simple antes de añadir más funciones de memoria

Un error común en proyectos de agentes es añadir almacenamiento vectorial, historial de conversación, resumen y políticas de retrieval todo a la vez. Este ejemplo propone una mejor secuencia:

1. Define un caso de uso concreto de memoria

2. Expón herramientas explícitas

3. Respalda esas herramientas con almacenamiento duradero

4. Verifica el comportamiento de recuperación

5. Solo entonces valora patrones de memoria más avanzados

Sigue aprendiendo

Si quieres seguir desarrollando habilidades con agentes después de este tutorial, estos recursos de DataCamp son un buen siguiente paso:

Conclusión

Ya has visto cómo crear un agente pequeño pero útil con memoria usando LangChain4j, Java y Oracle AI Database. La idea principal es sencilla: dale al modelo herramientas de memoria explícitas, guarda las notas de forma duradera como JSON y recupéralas con una estrategia de búsqueda directa.

Este diseño es una base sólida porque es fácil de entender y de ampliar. Una vez que este patrón funcione, podrás añadir mejor filtrado, esquemas de notas más ricos, ámbito por usuario o incluso búsqueda vectorial si el caso de uso lo requiere.

Si quieres seguir aprendiendo sobre diseño de agentes, un buen siguiente paso es comparar este patrón de memoria basada en herramientas con frameworks de agentes más amplios y flujos de orquestación. En particular, merece la pena explorar cómo encajan las herramientas explícitas, los patrones de retrieval y los agentes multi‑paso en sistemas de IA más grandes.

FAQs

¿Qué tipo de memoria implementa este tutorial?

Este tutorial implementa memoria duradera basada en herramientas. Las notas se guardan en una tabla de base de datos y se recuperan mediante llamadas explícitas a herramientas, en lugar de adjuntarse automáticamente a la ventana de prompt.

¿Este proyecto usa embeddings vectoriales?

No. Este ejemplo evita a propósito embeddings, búsqueda vectorial y reranking. Usa Oracle Text con json_textcontains para buscar en el contenido de las notas.

¿Por qué usar herramientas para la memoria en lugar de mantener todo en el historial del chat?

La memoria basada en herramientas te da más control sobre qué se guarda, cuándo se guarda y cómo se recupera. Además, funciona mejor para notas duraderas que deben sobrevivir más allá de una sola conversación.

¿Qué necesito para ejecutar la demo en vivo?

Necesitas Java 21 o superior, Maven 3.9 o superior, acceso a Oracle AI Database y una OPENAI_API_KEY en tu shell.

¿Las pruebas necesitan Docker?

Sí. Las pruebas de integración usan Testcontainers para arrancar Oracle AI Database Free, por lo que Docker debe estar disponible en tu entorno.


Anders Swanson's photo
Author
Anders Swanson
LinkedIn

Como Developer Evangelist en Oracle, ayudo a desarrolladores a crear aplicaciones modernas: microservicios, sistemas orientados a eventos, arquitecturas cloud‑native y mucho más. Si trabajas con herramientas como Kubernetes, Spring Boot o Kafka y te preguntas cómo encaja Oracle en ese mundo, ahí entro yo.Mi objetivo es compartir guías prácticas y accionables que te permitan desarrollar más rápido, publicar con más seguridad y exprimir al máximo la tecnología de bases de datos de Oracle. Si trabajas con JSON, event streaming, vector search o simplemente estás empezando, estoy aquí para aclarar el camino y hacerlo todo más accesible para desarrolladores.

Temas
Agentes de IA
Java

Cursos destacados de DataCamp

{count, plural, one {Curso} other {Cursos}}

Introducción a Oracle SQL

{hours} h
18.5K
Perfecciona tus habilidades en Oracle SQL, incluidos los conceptos básicos de SQL, la agregación, la combinación y la personalización de datos.
Ver detallesRight Arrow
Empezar Curso
Ver másRight Arrow
Relacionado
An AI juggles tasks

{count, plural, one {blog} other {blogs}}

Cinco proyectos que puedes crear con modelos de IA generativa (con ejemplos)

Aprende a utilizar modelos de IA generativa para crear un editor de imágenes, un chatbot similar a ChatGPT con pocos recursos y una aplicación clasificadora de aprobación de préstamos y a automatizar interacciones PDF y un asistente de voz con GPT.
Abid Ali Awan's photo

Abid Ali Awan

10 min

{count, plural, one {Tutorial} other {Tutoriales}}

Construir agentes LangChain para automatizar tareas en Python

Un tutorial completo sobre la construcción de agentes LangChain multiherramienta para automatizar tareas en Python utilizando LLMs y modelos de chat utilizando OpenAI.

{count, plural, one {Tutorial} other {Tutoriales}}

RAG Con Llama 3.1 8B, Ollama y Langchain: Tutorial

Aprende a crear una aplicación RAG con Llama 3.1 8B utilizando Ollama y Langchain, configurando el entorno, procesando documentos, creando incrustaciones e integrando un recuperador.
Ryan Ong's photo

Ryan Ong

12 min

{count, plural, one {Tutorial} other {Tutoriales}}

Tutorial sobre cómo crear aplicaciones LLM con LangChain

Explore el potencial sin explotar de los grandes modelos lingüísticos con LangChain, un marco Python de código abierto para crear aplicaciones avanzadas de IA.
Moez Ali's photo

Moez Ali

12 min

{count, plural, one {Tutorial} other {Tutoriales}}

Guía para principiantes de LlaMA-Factory WebUI: Ajuste de los LLM

Aprende a afinar los LLM en conjuntos de datos personalizados, evaluar el rendimiento y exportar y servir modelos sin problemas utilizando el marco de trabajo de bajo/ningún código de LLaMA-Factory.
Abid Ali Awan's photo

Abid Ali Awan

12 min

{count, plural, one {Tutorial} other {Tutoriales}}

Tutorial FLAN-T5: Guía y puesta a punto

Una guía completa para afinar un modelo FLAN-T5 para una tarea de respuesta a preguntas utilizando la biblioteca de transformadores, y ejecutando la inferencia optmizada en un escenario del mundo real.
Zoumana Keita 's photo

Zoumana Keita

15 min

Ver MásVer Más