Vai al contenuto principale

Tutorial sulla memoria con strumenti LangChain4j: crea un agente Java con Oracle AI Database

Scopri come creare un semplice agente LangChain4j che può ricordare note importanti tra conversazioni.
Aggiornato 29 set 2026  · 8 min leggi

Scopri con l'IA

ChatGPTClaudePerplexity

In questo tutorial, vedremo un piccolo modulo Java che espone strumenti personalizzati, salva note come JSON in Oracle AI Database e le recupera con la ricerca full-text.

Se stai creando agenti AI, la cronologia chat a breve termine di solito non basta. Gli assistenti reali spesso hanno bisogno di una piccola quantità di memoria durevole per archiviare fatti importanti e recuperarli in seguito.

Questo tutorial mostra come creare questo pattern con un modulo Java minimale. Userai gli strumenti LangChain4j, una semplice interfaccia dell'agente e Oracle AI Database come archivio di supporto. Invece di aggiungere embedding o una pipeline completa di retrieval, questo esempio mantiene il design volutamente essenziale: le note vengono salvate come documenti JSON e cercate con Oracle Text.

Alla fine, capirai come il modulo collega:

  • Un'interfaccia agente LangChain4j
  • Due metodi personalizzati con @Tool
  • Un repository JDBC per l'archiviazione durevole delle note
  • Un'applicazione da console che collega l'intero flusso

Cos'è la memoria basata su strumenti di LangChain4j?

LangChain4j ti permette di esporre normali metodi Java come strumenti che un modello può chiamare quando ha bisogno di capacità esterne. In questo modulo, tali capacità sono orientate alla memoria:

  • searchMemories(String question) trova note salvate rilevanti
  •  storeMemory(String note) scrive una nuova nota durevole

Questo è diverso dalla classica cronologia chat, che memorizza i messaggi in una finestra di contesto in memoria.

La memoria persistente basata su strumenti decide quando deve cercare o salvare note, e i dati sottostanti vivono in un database.

Questo pattern è utile quando vuoi che un agente ricordi fatti come note sugli incidenti, promemoria di runbook o azioni di follow-up, senza costruire troppo presto un sistema di retrieval più grande. 

La memoria persistente supportata da database può anche essere accessibile in parallelo da uno o più agenti senza contesa sui dati.

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 basata su strumenti consente all'assistente di recuperare note esistenti o di salvarne di nuove in un database durevole.

Caratteristiche chiave del modulo LangChain4j e Oracle AI DB

Questo esempio è piccolo, ma dimostra diverse idee importanti di design degli agenti.

Strumenti LangChain4j personalizzati

La classe MemoryTools espone due metodi annotati con @Tool. Ciò significa che LangChain4j può renderli disponibili all'agente senza codice wrapper aggiuntivo.

// 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();
 }

Questo metodo fa due cose utili. Delega il recupero al repository e formatta i risultati in un modo adatto al modello, ad esempio [M12] testo della nota. 

Questo rende l'output dello strumento facile da citare nella risposta finale dell'agente.

Memoria durevole in Oracle AI Database

Le note sono archiviate in una tabella agent_memories come documenti JSON nativi. Ogni riga contiene:

  • Una colonna identitaria id
  • Una colonna JSON chiamata memory_doc
  • Un timestamp created_at

Il modulo inizializza automaticamente lo schema:

-- 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
 )

Questo mantiene il modello di archiviazione semplice, offrendo comunque dati strutturati e durabilità gestita dal database.

Ricerca per parole chiave con Oracle Text

Il repository non genera embedding. Invece, estrae parole chiave dalla domanda dell'utente, rimuove le stop word, costruisce un'espressione Oracle Text con ACCUM e cerca il contenuto del documento JSON con json_textcontains.

Questo è particolarmente utile per identificatori e termini operativi come CHG2145, dove la corrispondenza esatta o quasi esatta dei token conta spesso più della somiglianza semantica.

Un flusso minimale da console

La classe Application crea il repository, inizializza lo schema, inserisce alcune note di esempio, costruisce il modello di chat e avvia un loop da terminale. Questo rende l'esempio facile da eseguire e da adattare in un altro servizio in seguito.

Primi passi con il tuo progetto di memoria LangChain4j

Prima di eseguire il modulo, assicurati che il tuo ambiente locale rispetti i requisiti del progetto.

Prerequisiti

Ti servono:

  • Java 21 o successivo
  • Maven 3.9 o successivo
  • Oracle AI Database Free in esecuzione in locale o in remoto
  • Una variabile d'ambiente OPENAI_API_KEY

Il progetto Maven dipende da:

  • langchain4j
  • langchain4j-open-ai
  • ojdbc11
  • JUnit e Testcontainers per i test di integrazione

Esegui l'esempio

Imposta prima la tua chiave API OpenAI:

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

Poi esegui l'app dalla directory del progetto:

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

L'applicazione si aspetta esattamente tre argomenti:

1. JDBC URL

2. Username del database

3. Password del database

Se mancano, Application.main() genera una IllegalArgumentException. Se OPENAI_API_KEY manca, genera una IllegalStateException.

Cosa succede all'avvio

Quando l'app parte, esegue:

1. Crea un MemoryRepository

2. Inizializza lo schema del database

3. Popola tre note di esempio se la tabella è vuota

4. Costruisce un OpenAiChatModel

5. Collega modello e strumenti in AiServices

6. Avvia un loop CLI che legge l'input finché non digiti exit

Questo significa che puoi provare subito il recupero della memoria senza inserire manualmente righe in anticipo.

Come creare un agente Java con memoria con LangChain4j

Ora percorriamo il modulo nello stesso ordine usato dall'applicazione.

Definisci l'interfaccia dell'agente

LangChain4j usa un'interfaccia Java per descrivere il contratto dell'agente. In questo progetto, quell'interfaccia è 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);
 }

La parte importante è il messaggio di sistema. Dice al modello quando usare ogni strumento:

  • Cerca quando note precedenti possono aiutare
  • Salva quando l'utente chiede esplicitamente all'assistente di ricordare qualcosa

Questo è un buon esempio di policy leggera sugli strumenti. Il prompt è breve, ma abbastanza specifico da guidare la selezione degli strumenti.

Esponi le operazioni di memoria come strumenti

L'agente non parla direttamente con il repository. Invece, chiama il layer MemoryTools.

Ecco il metodo di archiviazione:

// 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 + ".";
 }

Questo è volutamente compatto. Un utente potrebbe dire:

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

Se il modello segue correttamente il prompt di sistema, chiama storeMemory(), il repository persiste la nota e lo strumento restituisce qualcosa come:

Stored memory M4.

In seguito, l'utente può chiedere:

What should the next shift verify?

A quel punto, l'agente può chiamare searchMemories() e usare la nota restituita per rispondere.

Crea il layer repository

Il repository è dove vive la maggior parte della logica di memoria durevole. È comunque semplice JDBC, il che rende il design facile da ispezionare.

A livello alto, MemoryRepository è responsabile di:

  • Creazione dello schema
  • Seeding opzionale
  • Inserimento note
  • Estrazione di parole chiave
  • Oracle Text search
  • Recupero di fallback delle note recenti quando non ci sono parole chiave utili

Il percorso di inserimento normalizza e valida il testo della nota prima di archiviarlo:

// 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);
 	}
 }

Il documento JSON in sé appare così:

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

Questa struttura è semplice, ma sufficiente a supportare sia la persistenza sia la ricerca testuale.

Comprendi la strategia di ricerca

La parte più interessante del repository è il metodo 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);
 }

Questo metodo segue un flusso pratico:

1. Tokenizza la domanda dell'utente

2. Converte in minuscolo i token

3. Rimuove le stop word come the, what e remember

4. Mantiene token brevi ma significativi quando includono cifre

5. Costruisce un'espressione Oracle Text con ACCUM

6. Cerca i dati JSON con json_textcontains

Quel dettaglio “sono ammesse le cifre” è importante. Aiuta termini come CHG2145 a sopravvivere all'estrazione delle parole chiave, esattamente il tipo di lookup ricco di identificatori di cui molti agenti operativi hanno bisogno.

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

Il workflow di ricerca converte una domanda in un'espressione di parole chiave e restituisce note di memoria classificate.

La query SQL di ricerca è:

-- 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

Se la domanda non produce parole chiave utili, il repository ripiega sulle note recenti. Questo dà all'agente un default ragionevole invece di restituire troppo presto una ricerca vuota.

Collega tutto nell'applicazione

L'applicazione collega i pezzi 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();

Questo è l'intero pattern in un unico punto:

  • Costruisci un modello di chat
  • Espone i metodi come strumenti
  • Collega quegli strumenti a un agente guidato da interfaccia

Dopo di che, il loop da console passa semplicemente l'input utente a assistant.chat(input).

Prova i prompt demo

L'applicazione stampa alcuni prompt iniziali:

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?

Questi prompt sono utili perché esercitano l'intero ciclo di vita della memoria:

  • Recupera una nota seed esistente
  • Salva una nuova nota
  • Recupera la nuova nota salvata in seguito

Suggerimenti e risoluzione dei problemi

Questo modulo è volutamente ristretto, il che è un punto di forza quando impari il pattern. Ci sono comunque alcuni dettagli pratici da tenere a mente.

Mantieni esplicite le istruzioni per gli strumenti

Il prompt di sistema dice al modello di salvare la memoria solo quando l'utente lo chiede esplicitamente. Questo aiuta a ridurre le scritture accidentali. Se ampli il prompt senza attenzione, il modello potrebbe iniziare a persistere note di scarso valore.

Consideralo memoria di note durevoli, non un retrieval di conoscenza completo

Questo repository è ideale per brevi note operative, promemoria e identificatori. Non è ancora un sistema completo di retrieval-augmented generation con embedding, chunking, ricerca ibrida o ranking.

Usa Oracle Text quando gli identificatori contano

Per stringhe come ID di incidenti, numeri di ticket e riferimenti a richieste di modifica, un retrieval orientato alle parole chiave è spesso un punto di partenza migliore rispetto alla ricerca semantica. Questo esempio riflette chiaramente questo trade-off.

Convalida separatamente il tuo database e l'installazione Docker

I test del progetto sono test di integrazione alimentati da Testcontainers. Se Docker non è disponibile, mvn test fallirà prima che venga eseguita la logica del repository. Si tratta di un problema di ambiente, non necessariamente di un bug dell'applicazione.

Esegui i test con:

# Run the integration tests.
 mvn test

I test coprono due comportamenti importanti:

  • Le note possono essere salvate e recuperate tramite il repository
  • L'output degli strumenti include ID di memoria e testo di note rilevanti

Parti semplice prima di aggiungere altre funzionalità di memoria

Un errore comune nei progetti di agenti è aggiungere archiviazione vettoriale, cronologia conversazioni, sintesi e policy di retrieval tutte insieme. Questo esempio mostra una progressione migliore:

1. Definisci un caso d'uso concreto per la memoria

2. Espone strumenti espliciti

3. Supporta quegli strumenti con storage durevole

4. Verifica il comportamento di retrieval

5. Solo dopo valuta pattern di memoria più avanzati

Continua a imparare

Se vuoi continuare a sviluppare competenze sugli agenti dopo questo tutorial, queste risorse DataCamp sono un buon passo successivo:

Conclusione

Hai visto come creare un agente piccolo ma utile, abilitato alla memoria, con LangChain4j, Java e Oracle AI Database. L'idea centrale è semplice: fornisci al modello strumenti di memoria espliciti, salva le note in modo durevole come JSON e recuperale con una strategia di ricerca lineare.

Questo design è una base solida perché è facile da capire e da estendere. Una volta che questo pattern funziona, puoi aggiungere filtri migliori, schemi di note più ricchi, scoping per utente o persino la ricerca vettoriale se il caso d'uso lo richiede.

Se vuoi continuare a imparare il design degli agenti, il prossimo buon passo è confrontare questo pattern di memoria basato su strumenti con framework di agenti più ampi e workflow di orchestrazione. In particolare, vale la pena esplorare come strumenti espliciti, pattern di retrieval e agenti multi-step si integrano in sistemi AI più grandi.

FAQs

Che tipo di memoria implementa questo tutorial?

Questo tutorial implementa una memoria durevole basata su strumenti. Le note vengono archiviate in una tabella del database e recuperate tramite chiamate esplicite agli strumenti invece di essere aggiunte automaticamente alla finestra del prompt.

Questo progetto usa embedding vettoriali?

No. Questo esempio evita deliberatamente embedding, ricerca vettoriale e reranking. Usa Oracle Text con json_textcontains per cercare nel contenuto delle note.

Perché usare strumenti per la memoria invece di tenere tutto nella cronologia della chat?

La memoria basata su strumenti ti dà più controllo su cosa viene salvato, quando viene salvato e come viene recuperato. Funziona anche meglio per note durevoli che devono sopravvivere oltre una singola conversazione.

Cosa mi serve per eseguire la demo live?

Ti servono Java 21 o successivo, Maven 3.9 o successivo, accesso a Oracle AI Database e una OPENAI_API_KEY nella tua shell.

I test richiedono Docker?

Sì. I test di integrazione usano Testcontainers per avviare Oracle AI Database Free, quindi Docker deve essere disponibile nel tuo ambiente.


Anders Swanson's photo
Author
Anders Swanson
LinkedIn

Come Developer Evangelist in Oracle, aiuto gli sviluppatori a creare app moderne, tra cui microservizi, sistemi event-driven, architetture cloud-native e altro ancora. Se lavori con strumenti come Kubernetes, Spring Boot o Kafka e ti chiedi come Oracle si inserisca in quel mondo, è lì che entro in gioco.Il mio obiettivo è condividere indicazioni pratiche e operative che ti aiutino a sviluppare più velocemente, distribuire in modo più intelligente e sfruttare al massimo la tecnologia database di Oracle. Che tu stia usando JSON, event streaming, ricerca vettoriale o stia solo cercando di capire come iniziare, sono qui per rendere le cose più chiare e a misura di sviluppatore.

Argomenti
AI Agents
Java

Corsi DataCamp in evidenza

{count, plural, one {Corso} other {Corsi}}

Introduzione a Oracle SQL

{hours} ore
18.5K
Migliora le tue abilità con Oracle SQL, imparando le basi, come aggregare, combinare e personalizzare i dati.
Vedi i dettagliRight Arrow
Inizia Il Corso
Mostra altroRight Arrow
Correlato

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

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

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

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

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

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

Mostra AltroMostra Altro