Chat Memory
Chat Memory
LLM은 상태가 없어서(stateless) 이전 상호작용에 대한 정보를 유지하지 못해요. 여러 차례의 상호작용에 걸쳐 컨텍스트나 상태를 유지하려면 이게 제약이 될 수 있습니다. 이 문제를 풀기 위해 Spring AI는 여러 상호작용에 걸쳐 정보를 저장하고 검색하게 해 주는 채팅 메모리 기능을 제공합니다.
출처: 공식문서
ChatMemory 추상화로 다양한 사용 사례를 지원하는 여러 메모리 타입을 구현할 수 있어요. 메시지의 기반 저장소는 ChatMemoryRepository가 처리하는데, 그 책임은 메시지를 저장하고 검색하는 것뿐입니다. 어떤 메시지를 유지하고 언제 제거할지는 ChatMemory 구현이 결정해요. 마지막 N개 메시지 유지, 특정 기간 메시지 유지, 특정 토큰 한도까지 유지 같은 전략이 예시가 될 수 있습니다.
메모리 타입을 고르기 전에 채팅 메모리와 채팅 히스토리의 차이를 이해하는 게 중요합니다.
- Chat Memory. 대화 전반에 걸쳐 컨텍스트 인식을 유지하기 위해 LLM이 보유하고 사용하는 정보.
- Chat History. 사용자와 모델 사이에 오간 모든 메시지를 포함한 전체 대화 기록.
ChatMemory 추상화는 _채팅 메모리_를 관리하도록 설계됐습니다. 현재 대화 컨텍스트에 관련된 메시지를 저장하고 검색하게 해 줘요. 하지만 _채팅 히스토리_를 저장하기에는 적합하지 않습니다. 오간 모든 메시지의 완전한 기록을 유지해야 한다면, 완전한 채팅 히스토리를 효율적으로 저장·검색하는 Spring Data 같은 다른 접근을 고려하세요.
Quick Start
Spring AI는 애플리케이션에서 바로 쓸 수 있는 ChatMemory 빈을 자동 구성합니다. 기본적으로 메시지를 저장하는 데 인메모리 저장소(InMemoryChatMemoryRepository)를, 대화 기록을 관리하는 데 MessageWindowChatMemory 구현을 사용해요. 다른 저장소(예: Cassandra, JDBC, Neo4j)가 이미 구성되어 있다면 Spring AI는 그걸 대신 사용합니다.
@Autowired
ChatMemory chatMemory;
이어지는 절에서 Spring AI에서 사용 가능한 서로 다른 메모리 타입과 저장소를 더 자세히 설명할게요.
Conversation ID 다루기
모든 채팅 메모리 연산은 conversation ID를 키로 삼습니다. ChatMemory와 ChatMemoryRepository 추상화는 이 식별자로 어떤 대화를 읽고, 추가하고, 삭제할지 결정하며, 항상 명시적으로 제공돼야 해요.
conversation ID는 대화를 스코프하는 유일한 요소입니다. 메시지는 정확히 제공된 conversation ID에 대해 저장·검색돼요. 그래서 그 ID들을 어떻게 할당하느냐가 상호작용이 함께 묶일지, 분리될지를 결정합니다.
멀티유저 애플리케이션이라면 conversation ID를 사용자마다(사용자가 여러 대화를 가질 수 있다면 대화마다) 고유하게 만들어, 각 사용자의 메시지가 자기 대화에 머물게 하세요. 흔한 방법은 고정되거나 공유된 값을 쓰는 대신 서버에서 현재 사용자나 세션으로 ID를 유도하는 것입니다.
// Build the conversation ID from the current user/session so each
// user's messages are kept in their own conversation.
String conversationId = currentUser.getId() + ":" + httpSession.getId();
chatClient.prompt()
.user(userInput)
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.call()
.content();
애플리케이션이 커질 때 대화를 정리하는 데 도움이 되는 몇 가지 관행이에요.
- 사용자마다, 그리고 한 사용자가 여러 대화를 가질 수 있다면 대화마다 고유한 conversation ID를 할당하세요.
- 사용자 간에 고정 값을 재사용하는 대신, 서버에서 사용자나 세션으로 conversation ID를 유도하세요.
- 대화를 나열하거나 삭제할 때는 모든 conversation ID에 걸쳐가 아니라 현재 사용자에게 속한 conversation ID를 대상으로 동작하세요.
메모리 타입
ChatMemory 추상화로 다양한 사용 사례에 맞는 여러 메모리 타입을 구현할 수 있습니다. 메모리 타입의 선택은 애플리케이션의 성능과 동작에 큰 영향을 줄 수 있어요. 이 절은 Spring AI가 제공하는 내장 메모리 타입과 그 특성을 설명합니다.
Message Window Chat Memory
MessageWindowChatMemory는 지정된 최대 크기까지 메시지의 슬라이딩 윈도우를 유지합니다. 메시지 수가 최대치를 넘으면 오래된 메시지를 제거하면서 항상 SystemMessage 인스턴스는 보존해요. 기본 윈도우 크기는 20개 메시지입니다.
MessageWindowChatMemory memory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build();
이것이 Spring AI가 ChatMemory 빈을 자동 구성할 때 사용하는 기본 메시지 타입입니다.
Turn 경계 제거(Turn-boundary eviction)
제거가 필요할 때 MessageWindowChatMemory는 턴 중간을 자르지 않고 항상 완전한 턴을 제거합니다. _턴_은 UserMessage에서 시작해 그 뒤의 모든 어시스턴트 답변, 도구 호출, 도구 응답을 다음 UserMessage까지 포함해요. 원시 제거 지점이 비사용자 메시지(예: 도구 호출 교환 중간의 어시스턴트 답변)에 닿으면 절단 지점을 다음 UserMessage로 앞당겨서, 유지되는 윈도우가 항상 완전한 턴에서 시작하게 합니다.
즉 maxMessages는 저장되는 메시지 수의 _상한_입니다. 다음 턴 경계를 찾기 위해 스냅이 필요하면 실제 개수는 다소 낮을 수 있어요.
NOTE: maxMessages가 완전한 턴 하나보다 작으면(예: 윈도우 크기를 초과하는 다단계 도구 호출 교환), 새 UserMessage가 추가될 때까지 모든 비시스템 메시지가 제거될 수 있습니다. maxMessages를 사용 사례의 대표 턴 하나를 담을 수 있을 만큼 넉넉하게 잡으세요.
메모리 저장소
Spring AI는 채팅 메모리를 저장하기 위한 ChatMemoryRepository 추상화를 제공합니다. 이 절은 Spring AI가 제공하는 내장 저장소와 사용법을 설명하며, 필요하면 직접 저장소를 구현할 수도 있어요.
인메모리 저장소 (In-Memory Repository)
InMemoryChatMemoryRepository는 ConcurrentHashMap을 사용해 메시지를 메모리에 저장합니다.
기본적으로 다른 저장소가 구성되어 있지 않으면 Spring AI는 InMemoryChatMemoryRepository 타입의 ChatMemoryRepository 빈을 자동 구성해서 애플리케이션에서 바로 쓸 수 있게 합니다.
@Autowired
ChatMemoryRepository chatMemoryRepository;
InMemoryChatMemoryRepository를 직접 만들고 싶다면 이렇게 할 수 있어요.
ChatMemoryRepository repository = new InMemoryChatMemoryRepository();
JdbcChatMemoryRepository
JdbcChatMemoryRepository는 JDBC를 사용해 관계형 데이터베이스에 메시지를 저장하는 내장 구현입니다. 다양한 데이터베이스를 기본 지원하며, 채팅 메모리의 영속 저장이 필요한 애플리케이션에 적합합니다.
메시지는 오름차순(오래된 것부터 새로운 것)으로 검색되는데, 이는 LLM 대화 기록의 예상 형식과 같습니다. 순서는 대화 내 각 메시지의 위치를 기록하는 sequence_id 컬럼으로 유지됩니다. 각 메시지는 생성 timestamp도 저장하며, 이는 JdbcChatMemoryRepository.CONVERSATION_TS 키 아래 메시지 메타데이터에 java.time.Instant로 노출되어 애플리케이션이 메시지 생성 시각을 표시할 수 있어요.
WARNING: JdbcChatMemoryRepository는 도구 호출 메시지를 지원하지 않습니다. 도구 호출을 포함한 AssistantMessage 인스턴스와 ToolResponseMessage 인스턴스는 저장 시 조용히 걸러져 검색된 대화 기록에 나타나지 않아요. 도구 호출을 쓴다면, 모든 메시지 타입을 제대로 영속하는 Spring AI Session 프로젝트와 JDBC session store를 고려하세요.
먼저 프로젝트에 아래 의존성을 추가합니다:
Maven:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-jdbc'
}
Spring AI는 JdbcChatMemoryRepository에 대한 자동 구성을 제공하므로 애플리케이션에서 바로 쓸 수 있어요.
@Autowired
JdbcChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
JdbcChatMemoryRepository를 직접 만들고 싶다면 JdbcTemplate 인스턴스와 JdbcChatMemoryRepositoryDialect를 제공하면 됩니다.
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
.jdbcTemplate(jdbcTemplate)
.dialect(new PostgresChatMemoryRepositoryDialect())
.build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
메시지 타임스탬프 읽기
각 저장된 메시지는 생성 시각을 기록합니다. 타임스탬프는 JdbcChatMemoryRepository.CONVERSATION_TS 키 아래 메시지 메타데이터에 java.time.Instant로 제공되며, UI에서 메시지 시각을 표시하는 데 유용합니다.
List<Message> messages = chatMemory.get(conversationId);
for (Message message : messages) {
Instant createdAt = (Instant) message.getMetadata().get(JdbcChatMemoryRepository.CONVERSATION_TS);
}
타임스탬프는 대화가 다시 저장돼도 보존되므로, 메시지는 대화가 존재하는 동안 원래 생성 시각을 유지합니다.
지원 데이터베이스와 Dialect 추상화
Spring AI는 dialect 추상화로 여러 관계형 데이터베이스를 지원합니다. 기본 지원되는 데이터베이스는 다음과 같습니다.
- PostgreSQL
- MySQL / MariaDB
- SQL Server
- HSQLDB
- Oracle Database
JdbcChatMemoryRepositoryDialect.from(DataSource)를 쓸 때 JDBC URL에서 올바른 dialect를 자동 감지할 수 있습니다. JdbcChatMemoryRepositoryDialect 인터페이스를 구현하면 다른 데이터베이스 지원을 확장할 수 있어요.
구성 속성
| Property | Description | Default Value |
|---|---|---|
spring.ai.chat.memory.repository.jdbc.initialize-schema |
스키마 초기화 시점을 제어. 값: embedded(기본값), always, never. |
embedded |
spring.ai.chat.memory.repository.jdbc.schema |
초기화에 사용할 스키마 스크립트 위치. classpath: URL과 플랫폼 자리표시자를 지원. |
classpath:org/springframework/ai/chat/memory/repository/jdbc/schema-@@platform@@.sql |
spring.ai.chat.memory.repository.jdbc.platform |
@@platform@@ 자리표시자 사용 시 초기화 스크립트에 쓸 플랫폼. |
자동 감지 |
스키마 초기화
자동 구성은 시작 시 데이터베이스에 맞는 벤더별 SQL 스크립트로 SPRING_AI_CHAT_MEMORY 테이블을 자동 생성합니다. 기본적으로 스키마 초기화는 임베디드 데이터베이스(H2, HSQL, Derby 등)에서만 실행됩니다.
NOTE: 2.0.0부터 스키마는 기존 timestamp 컬럼 옆에 메시지 순서를 결정하는 sequence_id 컬럼을 추가합니다(이제 메시지 메타데이터로 노출). 기존 애플리케이션을 업그레이드하려면 새 컬럼을 추가해야 합니다. 마이그레이션 단계는 upgrade-notes를 참고하세요.
spring.ai.chat.memory.repository.jdbc.initialize-schema 속성으로 스키마 초기화를 제어할 수 있습니다.
spring.ai.chat.memory.repository.jdbc.initialize-schema=embedded # Only for embedded DBs (default)
spring.ai.chat.memory.repository.jdbc.initialize-schema=always # Always initialize
spring.ai.chat.memory.repository.jdbc.initialize-schema=never # Never initialize (useful with Flyway/Liquibase)
스키마 스크립트 위치를 재정의하려면:
spring.ai.chat.memory.repository.jdbc.schema=classpath:/custom/path/schema-mysql.sql
Dialect 확장
새 데이터베이스 지원을 추가하려면 JdbcChatMemoryRepositoryDialect 인터페이스를 구현하고 메시지 선택·삽입·삭제 SQL을 제공하세요. 그런 다음 사용자 지정 dialect를 저장소 빌더에 전달할 수 있습니다.
ChatMemoryRepository chatMemoryRepository = JdbcChatMemoryRepository.builder()
.jdbcTemplate(jdbcTemplate)
.dialect(new MyCustomDbDialect())
.build();
CassandraChatMemoryRepository
CassandraChatMemoryRepository는 Apache Cassandra를 사용해 메시지를 저장합니다. 특히 가용성, 내구성, 확장, 그리고 TTL(time-to-live) 기능을 활용하고자 하는 채팅 메모리 영속 저장 애플리케이션에 적합합니다.
CassandraChatMemoryRepository는 모든 과거 채팅 윈도우의 기록을 유지하는 시계열 스키마를 가지며, 거버넌스와 감사에 유용합니다. TTL을 예를 들어 3년 같은 값으로 설정하는 것을 권장합니다.
메시지는 오름차순 타임스탬프 순서(오래된 것부터)로 검색되는데, 이는 LLM 대화 기록의 예상 형식입니다.
CassandraChatMemoryRepository를 쓰려면 먼저 프로젝트에 의존성을 추가합니다:
Maven:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-cassandra</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-cassandra'
}
Spring AI는 바로 쓸 수 있는 CassandraChatMemoryRepository 자동 구성을 제공합니다.
@Autowired
CassandraChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
CassandraChatMemoryRepository를 직접 만들고 싶다면 CassandraChatMemoryRepositoryConfig 인스턴스를 제공하면 됩니다.
ChatMemoryRepository chatMemoryRepository = CassandraChatMemoryRepository
.create(CassandraChatMemoryRepositoryConfig.builder().withCqlSession(cqlSession));
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
구성 속성
| Property | Description | Default Value |
|---|---|---|
spring.cassandra.contactPoints |
클러스터 발견을 시작할 호스트 | 127.0.0.1 |
spring.cassandra.port |
연결할 Cassandra 네이티브 프로토콜 포트 | 9042 |
spring.cassandra.localDatacenter |
연결할 Cassandra 데이터센터 | datacenter1 |
spring.ai.chat.memory.cassandra.time-to-live |
Cassandra에 기록되는 메시지의 TTL | |
spring.ai.chat.memory.cassandra.keyspace |
Cassandra keyspace | springframework |
spring.ai.chat.memory.cassandra.messages-column |
메시지용 Cassandra 컬럼 이름 | springframework |
spring.ai.chat.memory.cassandra.table |
Cassandra 테이블 | ai_chat_memory |
spring.ai.chat.memory.cassandra.initialize-schema |
시작 시 스키마 초기화 여부 | true |
스키마 초기화
자동 구성이 ai_chat_memory 테이블을 자동 생성합니다.
spring.ai.chat.memory.repository.cassandra.initialize-schema 속성을 false로 설정해 스키마 초기화를 비활성화할 수 있어요.
WARNING: CassandraChatMemoryRepository는 도구 호출 메시지를 지원하지 않습니다. 도구 호출을 포함한 AssistantMessage 인스턴스와 ToolResponseMessage 인스턴스는 저장 시 조용히 걸러져 검색된 대화 기록에 나타나지 않아요. 도구 호출을 쓴다면, 모든 메시지 타입을 제대로 영속하는 Spring AI Session 프로젝트와 JDBC session store를 고려하세요.
Neo4j ChatMemoryRepository
Neo4jChatMemoryRepository는 Neo4j를 사용해 채팅 메시지를 속성 그래프 데이터베이스의 노드와 관계로 저장하는 내장 구현입니다. 채팅 메모리 영속에 Neo4j의 그래프 기능을 활용하려는 애플리케이션에 적합합니다.
메시지는 오름차순 메시지 인덱스 순서(오래된 것부터)로 검색되는데, 이는 LLM 대화 기록의 예상 형식입니다.
먼저 프로젝트에 아래 의존성을 추가합니다:
Maven:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-neo4j</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-neo4j'
}
Spring AI는 바로 쓸 수 있는 Neo4jChatMemoryRepository 자동 구성을 제공합니다.
@Autowired
Neo4jChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
Neo4jChatMemoryRepository를 직접 만들고 싶다면 Neo4j Driver 인스턴스를 제공하면 됩니다.
ChatMemoryRepository chatMemoryRepository = Neo4jChatMemoryRepository.builder()
.driver(driver)
.build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
구성 속성
| Property | Description | Default Value |
|---|---|---|
spring.ai.chat.memory.repository.neo4j.session-label |
대화 세션을 저장하는 노드의 라벨 | Session |
spring.ai.chat.memory.repository.neo4j.message-label |
메시지를 저장하는 노드의 라벨 | Message |
spring.ai.chat.memory.repository.neo4j.tool-call-label |
도구 호출을 저장하는 노드의 라벨(예: Assistant 메시지 내) | ToolCall |
spring.ai.chat.memory.repository.neo4j.metadata-label |
메시지 메타데이터를 저장하는 노드의 라벨 | Metadata |
spring.ai.chat.memory.repository.neo4j.tool-response-label |
도구 응답을 저장하는 노드의 라벨 | ToolResponse |
spring.ai.chat.memory.repository.neo4j.media-label |
메시지와 연관된 미디어를 저장하는 노드의 라벨 | Media |
NOTE: 라벨 값은 유효한 Neo4j 식별자여야 합니다. 문자나 밑줄로 시작하고 문자·숫자·밑줄만 포함해야 해요. 이 요구 사항을 충족하지 않는 값을 주면 애플리케이션 시작 시 IllegalArgumentException이 던져집니다.
인덱스 초기화
Neo4j 저장소는 성능 최적화를 위해 conversation ID와 메시지 인덱스용 인덱스를 자동 생성합니다. 사용자 지정 라벨을 쓰면 그 라벨에도 인덱스가 생성돼요. 스키마 초기화는 필요 없지만, 애플리케이션이 Neo4j 인스턴스에 접근 가능한지 확인하세요.
CosmosDBChatMemoryRepository
CosmosDBChatMemoryRepository 채팅 메모리 저장소 지원은 Azure Cosmos DB 팀이 유지보수하는 외부 모듈로 제공됩니다. 자세한 내용은 관련 문서를 참고하세요.
MongoChatMemoryRepository
MongoChatMemoryRepository는 MongoDB를 사용해 메시지를 저장하는 내장 구현입니다. 채팅 메모리 영속을 위해 유연하고 문서 지향적인 데이터베이스가 필요한 애플리케이션에 적합합니다.
메시지는 오름차순 타임스탬프 순서(오래된 것부터)로 검색되는데, 이는 LLM 대화 기록의 예상 형식입니다. 이 순서는 모든 채팅 메모리 저장소 구현에서 일관됩니다.
WARNING: MongoChatMemoryRepository는 도구 호출 메시지를 지원하지 않습니다. 도구 호출을 포함한 AssistantMessage 인스턴스와 ToolResponseMessage 인스턴스는 저장 시 조용히 걸러져 검색된 대화 기록에 나타나지 않아요. 도구 호출을 쓴다면, 모든 메시지 타입을 제대로 영속하는 Spring AI Session 프로젝트와 JDBC session store를 고려하세요.
먼저 프로젝트에 아래 의존성을 추가합니다:
Maven:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-mongodb</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-mongodb'
}
Spring AI는 바로 쓸 수 있는 MongoChatMemoryRepository 자동 구성을 제공합니다.
@Autowired
MongoChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
MongoChatMemoryRepository를 직접 만들고 싶다면 MongoTemplate 인스턴스를 제공하면 됩니다.
ChatMemoryRepository chatMemoryRepository = MongoChatMemoryRepository.builder()
.mongoTemplate(mongoTemplate)
.build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
구성 속성
| Property | Description | Default Value |
|---|---|---|
spring.ai.chat.memory.repository.mongo.create-indices |
시작 시 인덱스를 자동 생성·재생성할지. 참고: TTL 값 변경 시 TTL 인덱스를 드롭하고 재생성합니다 | false |
spring.ai.chat.memory.repository.mongo.ttl |
MongoDB에 기록되는 메시지의 TTL(초). 설정하지 않으면 메시지가 무기한 저장됩니다. | 0 |
컬렉션 초기화
자동 구성은 시작 시 ai_chat_memory 컬렉션이 없으면 자동 생성합니다.
RedisChatMemoryRepository
RedisChatMemoryRepository는 Redis Stack(Redis Query Engine과 RedisJSON 포함)을 사용해 채팅 메시지를 저장하는 내장 구현입니다. 선택적 TTL 지원과 고급 쿼리 능력을 갖춘 고성능·저지연 채팅 메모리 영속이 필요한 애플리케이션에 적합합니다.
저장소는 메시지를 JSON 문서로 저장하고 효율적인 쿼리를 위한 검색 인덱스를 만듭니다. 또한 AdvancedRedisChatMemoryRepository 인터페이스를 통해 내용, 타입, 시간 범위, 메타데이터로 메시지를 검색하는 확장된 쿼리 능력도 제공해요.
메시지는 오름차순 타임스탬프 순서(오래된 것부터)로 검색되는데, 이는 LLM 대화 기록의 예상 형식입니다.
먼저 프로젝트에 아래 의존성을 추가합니다:
Maven:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-redis</artifactId>
</dependency>
Gradle:
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-model-chat-memory-repository-redis'
}
Spring AI는 바로 쓸 수 있는 RedisChatMemoryRepository 자동 구성을 제공합니다.
@Autowired
RedisChatMemoryRepository chatMemoryRepository;
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
RedisChatMemoryRepository를 직접 만들고 싶다면 RedisClient 클라이언트를 제공하면 됩니다.
RedisClient jedisClient = RedisClient.builder().hostAndPort("localhost", 6379).build();
ChatMemoryRepository chatMemoryRepository = RedisChatMemoryRepository.builder()
.jedisClient(jedisClient)
.indexName("my-chat-index")
.keyPrefix("my-chat:")
.timeToLive(Duration.ofHours(24))
.build();
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(10)
.build();
구성 속성
| Property | Description | Default Value |
|---|---|---|
spring.ai.chat.memory.repository.redis.host |
Redis 서버 호스트 | localhost |
spring.ai.chat.memory.repository.redis.port |
Redis 서버 포트 | 6379 |
spring.ai.chat.memory.repository.redis.username |
Redis ACL 사용자명 | 없음 |
spring.ai.chat.memory.repository.redis.password |
Redis 서버 비밀번호 | 없음 |
spring.ai.chat.memory.repository.redis.index-name |
Redis 검색 인덱스 이름 | chat-memory-idx |
spring.ai.chat.memory.repository.redis.key-prefix |
채팅 메모리 항목의 키 접두사 | chat-memory: |
spring.ai.chat.memory.repository.redis.time-to-live |
채팅 메모리 항목의 TTL (예: 24h, 30d) |
만료 없음 |
spring.ai.chat.memory.repository.redis.initialize-schema |
시작 시 Redis 스키마 초기화 여부 | true |
spring.ai.chat.memory.repository.redis.max-conversation-ids |
반환할 최대 conversation ID 수 | 1000 |
spring.ai.chat.memory.repository.redis.max-messages-per-conversation |
대화당 반환할 최대 메시지 수 | 1000 |
고급 쿼리
RedisChatMemoryRepository는 확장된 쿼리 능력을 제공하는 AdvancedRedisChatMemoryRepository도 구현합니다.
// Cast to access advanced features
AdvancedRedisChatMemoryRepository advancedRepo = (AdvancedRedisChatMemoryRepository) chatMemoryRepository;
// Find messages by type across all conversations
List<MessageWithConversation> userMessages = advancedRepo.findByType(MessageType.USER, 100);
// Find messages containing specific content
List<MessageWithConversation> results = advancedRepo.findByContent("Spring AI", 50);
// Find messages within a time range
List<MessageWithConversation> recentMessages = advancedRepo.findByTimeRange(
conversationId,
Instant.now().minus(Duration.ofHours(1)),
Instant.now(),
100
);
// Find messages by metadata
List<MessageWithConversation> priorityMessages = advancedRepo.findByMetadata("priority", "high", 50);
// Execute custom Redis queries
List<MessageWithConversation> customResults = advancedRepo.executeQuery("@type:USER @content:Redis", 100);
메타데이터 필드 인덱싱
사용자 지정 메타데이터 필드의 효율적인 쿼리를 위해 메타데이터 필드 정의를 구성할 수 있습니다.
spring.ai.chat.memory.repository.redis.metadata-fields[0].name=priority
spring.ai.chat.memory.repository.redis.metadata-fields[0].type=tag
spring.ai.chat.memory.repository.redis.metadata-fields[1].name=score
spring.ai.chat.memory.repository.redis.metadata-fields[1].type=numeric
spring.ai.chat.memory.repository.redis.metadata-fields[2].name=category
spring.ai.chat.memory.repository.redis.metadata-fields[2].type=tag
지원되는 필드 타입: tag(정확히 일치 필터링), text(전문 검색), numeric(범위 쿼리).
스키마 초기화
자동 구성은 시작 시 Redis 검색 인덱스가 없으면 자동 생성합니다. spring.ai.chat.memory.repository.redis.initialize-schema=false로 이 동작을 비활성화할 수 있어요.
요구 사항
- Redis Stack 7.0 이상(Redis Query Engine과 RedisJSON 모듈 포함)
- Jedis 클라이언트 라이브러리(의존성으로 포함)
Chat Client에서의 메모리
ChatClient API를 쓸 때, 여러 상호작용에 걸쳐 대화 컨텍스트를 유지하도록 ChatMemory 구현을 제공할 수 있습니다.
Spring AI는 요구에 따라 ChatClient의 메모리 동작을 구성하는 데 쓸 수 있는 몇 가지 내장 Advisors를 제공합니다.
WARNING: 현재 도구 호출을 수행할 때 LLM과 주고받는 중간 메시지는 메모리에 저장되지 않습니다. 이는 현재 구현의 제약이며 향후 릴리스에서 다뤄질 예정이에요. 이 메시지들을 저장해야 한다면 User Controlled Tool Execution 지침을 참고하세요.
MessageChatMemoryAdvisor. 이 advisor는 제공된ChatMemory구현으로 대화 메모리를 관리합니다. 상호작용마다 메모리에서 대화 기록을 검색해 메시지 모음으로 프롬프트에 포함해요.VectorStoreChatMemoryAdvisor. 이 advisor는 제공된VectorStore구현으로 대화 메모리를 관리합니다. 상호작용마다 벡터 스토어에서 대화 기록을 검색해 일반 텍스트로 시스템 메시지에 덧붙여요.
예를 들어 MessageWindowChatMemory를 MessageChatMemoryAdvisor와 함께 쓰고 싶다면 이렇게 구성할 수 있습니다.
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
ChatClient에 호출을 수행하면 메모리가 MessageChatMemoryAdvisor에 의해 자동으로 관리됩니다. 지정된 conversation ID를 기반으로 메모리에서 대화 기록이 검색돼요.
IMPORTANT: ChatMemory.CONVERSATION_ID 파라미터는 모든 메모리 advisor에 필수입니다. 이 파라미터를 생략한 호출은 런타임에 IllegalArgumentException을 던집니다. 기본 conversation ID는 없어요.
String conversationId = "007";
chatClient.prompt()
.user("Do I have license to code?")
.advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))
.call()
.content();
VectorStoreChatMemoryAdvisor
사용자 지정 템플릿
VectorStoreChatMemoryAdvisor는 기본 템플릿을 사용해 검색된 대화 메모리로 시스템 메시지를 증강합니다. .promptTemplate() 빌더 메서드로 직접 PromptTemplate 객체를 제공해 이 동작을 커스터마이즈할 수 있어요.
NOTE: 여기서 제공하는 PromptTemplate은 advisor가 검색된 메모리를 시스템 메시지와 병합하는 방식을 커스터마이즈합니다. 이는 ChatClient 자체에서 TemplateRenderer를 구성하는 것(.templateRenderer() 사용)과는 다릅니다. 후자는 advisor가 실행되기 전에 초기 사용자/시스템 프롬프트 내용 렌더링에 영향을 줍니다. 클라이언트 수준 템플릿 렌더링에 대한 자세한 내용은 ChatClient Prompt Templates를 참고하세요.
사용자 지정 PromptTemplate은 어떤 TemplateRenderer 구현이든 쓸 수 있습니다(기본값은 StringTemplate 엔진 기반 StPromptTemplate). 중요한 요구 사항은 템플릿이 다음 두 자리표시자를 포함해야 한다는 것입니다.
- 원래 시스템 메시지를 받는
instructions자리표시자. - 검색된 대화 메모리를 받는
long_term_memory자리표시자.
Chat Model에서의 메모리
ChatClient 대신 ChatModel로 직접 작업하고 있다면 메모리를 명시적으로 관리할 수 있어요.
// Create a memory instance
ChatMemory chatMemory = MessageWindowChatMemory.builder().build();
String conversationId = "007";
// First interaction
UserMessage userMessage1 = new UserMessage("My name is James Bond");
chatMemory.add(conversationId, userMessage1);
ChatResponse response1 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response1.getResult().getOutput());
// Second interaction
UserMessage userMessage2 = new UserMessage("What is my name?");
chatMemory.add(conversationId, userMessage2);
ChatResponse response2 = chatModel.call(new Prompt(chatMemory.get(conversationId)));
chatMemory.add(conversationId, response2.getResult().getOutput());
// The response will contain "James Bond"
더 알아보기
- ChatClient — 대화 클라이언트 API
- Advisors — 메모리 advisor 패턴
- Observability — 대화 메모리 관측