1. Overview
A menudo necesitamos interacciones similares a las humanas cuando tenemos una conversación con una aplicación de IA. Por lo tanto, hay una necesidad de mantener la conversación con el modelo LLM, mientras Spring AI lo aborda a través de su funcionalidad de memoria de chat.
En este tutorial, exploraremos las distintas opciones de memoria de chat proporcionadas en Spring AI y daremos ejemplos de cómo integrar la memoria de chat con el cliente de chat.
2. Chat Memory
Los modelos de lenguaje grande (LLM) son sin estado y no memoriza nada. Cada prompt al LLM se considera una consulta aislada, lo que significa que el modelo no recuerda ningún mensaje previo.
En las aplicaciones de IA, mantener conversaciones previas es crucial para que el LLM produzca respuestas significativas. Aquí es donde la memoria de chat entra para llenar el vacío, proporcionando:
- Comprensión contextual – Esto permite que el LLM genere respuestas basadas en la conversación completa.
- Personalización – Facilita la provisión de respuestas personalizadas basadas en la memoria de chat.
- Persistencia – Según la implementación, la memoria de chat puede persistir a través de múltiples sesiones.
3. Chat Memory Repositories
Spring AI proporciona una interfaz ChatMemory y algunas implementaciones listas para usar que nos ayudan a integrar fácilmente la memoria de chat en nuestra aplicación.
Primero, agreguemos la dependencia Maven spring-ai-starter-model-openai para habilitar la integración con OpenAI. Esta dependencia importará transitivamente las bibliotecas centrales de Spring AI también:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
<version>1.0.0</version>
</dependency>
Cuando creamos la memoria de chat, debemos proporcionar una implementación de ChatMemoryRepository, que es responsable de persistir los mensajes de chat en el almacén:
ChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
Spring AI ofrece diferentes tipos de repositorios de memoria de chat que podemos elegir según la pila tecnológica de nuestro proyecto. Aquí discutiremos dos de ellos.
3.1. In-Memory Repository
Si no definimos explícitamente la memoria de chat, Spring AI usa un almacén en memoria por defecto. Almacena el mensaje de chat internamente en un ConcurrentHashMap donde el ID de conversación es la clave, y el valor es una lista de mensajes en esa conversación:
public final class InMemoryChatMemoryRepository implements ChatMemoryRepository {
Map<String, List<Message>> chatMemoryStore = new ConcurrentHashMap();
// other methods
}
El repositorio en memoria es muy sencillo y funciona bien cuando no necesitamos persistencia a largo plazo. Necesitamos elegir algo más si se requiere persistencia a largo plazo.
3.2. JDBC Repository
El repositorio JDBC sirve para persistir mensajes de chat en una base de datos relacional. Spring AI ofrece soporte incorporado para varias bases de datos relacionales, incluidas MySQL, PostgreSQL, SQL Server y HSQLDB.
Si deseamos almacenar la memoria de chat en una base de datos relacional, necesitaremos incluir la dependencia Maven dependency para soportarla:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-model-chat-memory-repository-jdbc</artifactId>
<version>1.0.0</version>
</dependency>
Cada una de las bases de datos soportadas tiene su propia implementación de dialecto que proporciona las sentencias SQL para las operaciones CRUD en la tabla de memoria de chat. Debemos proporcionar el dialecto cuando inicializamos el JdbcChatMemoryRepository:
JdbcChatMemoryRepositoryDialect dialect = ...; // El dialecto del repositorio elegido
ChatMemoryRepository repository = JdbcChatMemoryRepository.builder()
.jdbcTemplate(jdbcTemplate)
.dialect(dialect)
.build();
Para bases de datos que no tienen soporte incorporado, debemos implementar la interfaz JdbcChatMemoryRepositoryDialect y proporcionar las sentencias SQL para cada operación CRUD:
public interface JdbcChatMemoryRepositoryDialect {
String getSelectMessagesSql();
String getInsertMessageSql();
String getSelectConversationIdsSql();
String getDeleteMessagesSql();
}
Para el dialecto implementado en Spring AI, las operaciones CRUD siguen SQL estándar y no dependen de un proveedor específico. Por lo tanto, podríamos simplemente usar una implementación proporcionada como MysqlChatMemoryRepositoryDialect sin implementar nuestro propio dialecto.
Necesitamos inicializar el esquema antes de usarlo. Para los dialectos soportados, Spring AI también proporciona el script de creación del esquema. Podemos encontrar esos scripts en classpath:org/springframework/ai/chat/memory/repository/jdbc.
4. Apply Chat Memory to Chat Client
Spring AI ofrece una auto-configuración para la memoria de chat en ChatMemoryAutoConfiguration. Si elegimos un repositorio en memoria, no necesitamos definir nada explícitamente, ya que es el valor por defecto.
Sin embargo, si deseamos usar el repositorio JDBC en su lugar, debemos proporcionar nuestro método bean de ChatMemoryRepository para sobrescribir el repositorio en memoria por defecto:
@Configuration
public class ChatConfig {
@Bean
public ChatMemoryRepository getChatMemoryRepository(JdbcTemplate jdbcTemplate) {
return JdbcChatMemoryRepository.builder()
.jdbcTemplate(jdbcTemplate)
.dialect(new HsqldbChatMemoryRepositoryDialect())
.build();
}
}
Nota que no necesitamos definir explícitamente el método bean de ChatMemory, ya que ya está definido en ChatMemoryAutoConfiguration.
Creemos un ChatService en Spring Boot:
@Component
@SessionScope
public class ChatService {
private final ChatClient chatClient;
private final String conversationId;
public ChatService(ChatModel chatModel, ChatMemory chatMemory) {
this.chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
this.conversationId = UUID.randomUUID().toString();
}
public String chat(String prompt) {
return chatClient.prompt()
.user(userMessage -> userMessage.text(prompt))
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.call()
.content();
}
}
En el constructor, Spring Boot inyectará automáticamente la implementación de ChatMemory. De este modo, inicializamos el ChatClient con ella a través de MemoryChatMemoryAdvisor.
Definimos el método chat para aceptar el prompt y enviar el mensaje al modelo de chat. Además, agregamos un ID de conversación como parámetro del asesor de chat para identificar de manera única la conversación basada en la sesión actual.
Es importante señalar que debemos anotar el servicio con @SessionScope para que su instancia pueda persistir entre múltiples solicitudes.
5. Integration with OpenAI
En nuestra demostración, integraremos la memoria de chat con OpenAI y examinaremos cómo Spring AI llama a la API de OpenAI y adoptaremos una base de datos HSQL en memoria como almacén de persistencia.
Agreguemos propiedades a application.yml para añadir la clave API de OpenAI, configurar la conexión a la base de datos y inicializar el esquema durante el inicio de la aplicación:
spring:
ai:
openai:
api-key: "<YOUR-API-KEY>"
datasource:
url: jdbc:hsqldb:mem:chatdb
driver-class-name: org.hsqldb.jdbc.JDBCDriver
username: sa
password:
sql:
init:
mode: always
schema-locations: classpath:org/springframework/ai/chat/memory/repository/jdbc/schema-hsqldb.sql
Ahora, la configuración está lista. Creamos un punto final REST para poder llamar al ChatService que definimos anteriormente:
@RestController
public class ChatController {
private final ChatService chatService;
public ChatController(ChatService chatService) {
this.chatService = chatService;
}
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestBody @Valid ChatRequest request) {
String response = chatService.chat(request.getPrompt());
return ResponseEntity.ok(response);
}
}
ChatRequest es un DTO simple que contiene el prompt como una cadena de texto:
public class ChatRequest {
@NotNull
private String prompt;
// getter and setter
}
6. Test Run
Ahora estamos listos para enviar solicitudes al punto final REST. Usaremos Postman para enviar solicitudes al punto final REST e interceptaremos las solicitudes HTTP entre nuestra aplicación Spring Boot y OpenAI con la herramienta HTTP para ver cómo funcionan juntos.
6.1. First Request
Realicemos una llamada para pedir un chiste en Postman y ver la respuesta:

Cuando observamos la solicitud interceptada en la herramienta HTTP, veremos la solicitud HTTP a OpenAI:
{
"messages": [
{
"content": "Tell me a joke",
"role": "user"
}
],
"model": "gpt-4o-mini",
"stream": false,
"temperature": 0.7
}
Esta es una solicitud bastante trivial que envía nuestro payload de prompt usando el rol de usuario.
6.2. Second Request
Ahora, realicemos otra solicitud para comparar la diferencia:

Cuando leemos la solicitud HTTP interceptada a OpenAI esta vez, vemos que Spring AI no solo envía nuestro payload de prompt a OpenAI, sino que también envía el prompt y la respuesta anteriores:
{
"messages": [
{
"content": "Tell me a joke",
"role": "user"
},
{
"content": "Why did the scarecrow win an award? \n\nBecause he was outstanding in his field!",
"role": "assistant"
},
{
"content": "Tell me another one",
"role": "user"
}
],
"model": "gpt-4o-mini",
"stream": false,
"temperature": 0.7
}
En este ejemplo, observamos que Spring AI envía todo el historial de chat al modelo de chat. Este enfoque ayuda al modelo de chat a mantener el contexto de la conversación completa y hacer que la interacción se sienta más natural.
7. Conclusion
En este artículo, hemos aprendido cómo Spring AI mejora la experiencia conversacional manteniendo el historial de chat a través de múltiples solicitudes de chat mediante la memoria de chat.
Exploramos diferentes repositorios de memoria e ilustramos cómo integrar la memoria de chat con Spring AI y OpenAI. También examinamos cómo funciona la memoria de chat de Spring AI con OpenAI detrás de escena.
Newsletter Semanal de Java
Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.
Sin spam. Cancela cuando quieras.