Redis 벡터 스토어
Redis 벡터 스토어 (Spring AI)
이미 Redis를 운영 중이라면, 벡터 검색까지 같은 저장소에서 해결할 수 있어요. 이 글은 RedisVectorStore를 설정해서 문서 임베딩을 저장하고 유사도 검색을 수행하는 과정을 안내해요.
Redis는 오픈소스(BSD 라이선스) 인메모리 데이터 구조 스토어로, 데이터베이스·캐시·메시지 브로커·스트리밍 엔진으로 쓰여요. 문자열, 해시, 리스트, 셋, 정렬된 셋(범위 쿼리 포함), 비트맵, 하이퍼로그로그, 지리공간 인덱스, 스트림 같은 데이터 구조를 제공해요.
Redis Search and Query는 Redis OSS의 핵심 기능을 확장해서 Redis를 벡터 데이터베이스로 쓸 수 있게 해 줘요.
- 해시나 JSON 문서 안에 벡터와 관련 메타데이터 저장
- 벡터 검색
- 벡터 유사도 검색(KNN)
- 반경 임계값 기반 범위 벡터 검색
- TEXT 필드에 대한 전문(full-text) 검색
- 여러 거리 메트릭(COSINE, L2, IP)과 벡터 알고리즘(HNSW, FLAT) 지원
사전 준비 (Prerequisites)
- Redis Stack 인스턴스
- Redis Cloud (권장)
- Docker 이미지
redis/redis-stack:latest
- 문서 임베딩을 계산할
EmbeddingModel인스턴스. 필요한 경우RedisVectorStore에 저장할 임베딩을 생성하는EmbeddingModel용 API 키를 준비해요.
자동 설정 (Auto-configuration)
중요: Spring AI 자동 설정과 스타터 모듈의 아티팩트 이름에 큰 변화가 있었어요. 자세한 내용은 upgrade notes를 확인해 주세요.
Spring AI는 Redis 벡터 스토어용 Spring Boot 자동 설정을 제공해요. 활성화하려면 Maven pom.xml에 다음 의존성을 추가해요.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>
또는 Gradle build.gradle 파일에 이렇게 넣어요.
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-redis'
}
참고: 스프링 AI BOM은 Dependency Management, Maven Central/Snapshot 저장소 추가는 Artifact Repositories 섹션을 참고해요.
벡터 스토어 구현이 필요한 스키마를 직접 초기화해 줄 수 있지만, 반드시 옵트인해야 해요. 적절한 생성자에서 initializeSchema 불리언을 지정하거나 application.properties에 ...initialize-schema=true를 설정하면 돼요.
중요: 이것은 breaking change예요! 이전 버전의 Spring AI에서는 이 스키마 초기화가 기본으로 동작했어요.
벡터 스토어의 기본값과 설정 옵션은 아래 configuration parameters 목록을 참고해 주세요.
또한 설정된 EmbeddingModel 빈이 필요해요. EmbeddingModel 섹션을 참고해 주세요.
이제 애플리케이션에서 RedisVectorStore를 벡터 스토어로 오토와이어해서 사용할 수 있어요.
@Autowired VectorStore vectorStore;
// ...
List <Document> documents = List.of(
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!", Map.of("meta1", "meta1")),
new Document("The World is Big and Salvation Lurks Around the Corner"),
new Document("You walk forward facing the past and you turn back toward the future.", Map.of("meta2", "meta2")));
// Add the documents to Redis
vectorStore.add(documents);
// Retrieve documents similar to a query
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());
설정 프로퍼티
Redis에 연결하고 RedisVectorStore를 사용하려면 인스턴스 접근 정보를 제공해야 해요. Spring Boot의 application.yml로 간단히 설정할 수 있어요.
spring:
data:
redis:
url: <redis instance url>
ai:
vectorstore:
redis:
initialize-schema: true
index-name: custom-index
prefix: custom-prefix
또는 application.properties로도 간단히 설정할 수 있어요.
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.username=default
spring.data.redis.password=
spring.ai.vectorstore.redis.*로 시작하는 프로퍼티가 RedisVectorStore를 구성해요.
| Property | Description | Default Value |
|---|---|---|
spring.ai.vectorstore.redis.initialize-schema |
Whether to initialize the required schema | false |
spring.ai.vectorstore.redis.index-name |
The name of the index to store the vectors | spring-ai-index |
spring.ai.vectorstore.redis.prefix |
The prefix for Redis keys | embedding: |
spring.ai.vectorstore.redis.distance-metric |
Distance metric for vector similarity (COSINE, L2, IP) | COSINE |
spring.ai.vectorstore.redis.vector-algorithm |
Vector indexing algorithm (HNSW, FLAT) | HNSW |
spring.ai.vectorstore.redis.hnsw-m |
HNSW: Number of maximum outgoing connections | 16 |
spring.ai.vectorstore.redis.hnsw-ef-construction |
HNSW: Number of maximum connections during index building | 200 |
spring.ai.vectorstore.redis.hnsw-ef-runtime |
HNSW: Number of connections to consider during search | 10 |
spring.ai.vectorstore.redis.default-range-threshold |
Default radius threshold for range searches | 0.8 |
spring.ai.vectorstore.redis.text-scorer |
Text scoring algorithm (BM25, TFIDF, BM25STD, DISMAX, DOCSCORE) | BM25 |
메타데이터 필터링 (Metadata Filtering)
Redis에서도 일반적이고 이식 가능한 metadata filters를 활용할 수 있어요.
예를 들어 텍스트 표현 언어로 필터링할 수 있고,
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression("country in ['UK', 'NL'] && year >= 2020").build());
Filter.Expression DSL로 프로그래밍 방식으로도 필터링할 수 있어요.
FilterExpressionBuilder b = new FilterExpressionBuilder();
vectorStore.similaritySearch(SearchRequest.builder()
.query("The World")
.topK(TOP_K)
.similarityThreshold(SIMILARITY_THRESHOLD)
.filterExpression(b.and(
b.in("country", "UK", "NL"),
b.gte("year", 2020)).build()).build());
참고: 이 (이식 가능한) 필터 표현식들은 Redis search queries로 자동 변환돼요.
예를 들어 이 이식 가능한 필터 표현식:
country in ['UK', 'NL'] && year >= 2020
은 Redis 고유 필터 형식으로 이렇게 변환돼요.
@country:{UK | NL} @year:[2020 inf]
수동 설정 (Manual Configuration)
Spring Boot 자동 설정 대신 Redis 벡터 스토어를 수동으로 구성할 수도 있어요. 그러려면 spring-ai-redis-store를 프로젝트에 추가해야 해요.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-redis-store</artifactId>
</dependency>
또는 Gradle build.gradle 파일에 이렇게 넣어요.
dependencies {
implementation 'org.springframework.ai:spring-ai-redis-store'
}
RedisClient 빈을 만들어요.
@Bean
public RedisClient jedisClient() {
return RedisClient.builder().hostAndPort("<host>", 6379).build();
}
그런 다음 빌더 패턴으로 RedisVectorStore 빈을 만들어요.
@Bean
public VectorStore vectorStore(RedisClient jedisClient, EmbeddingModel embeddingModel) {
return RedisVectorStore.builder(jedisClient, embeddingModel)
.indexName("custom-index") // Optional: defaults to "spring-ai-index"
.prefix("custom-prefix") // Optional: defaults to "embedding:"
.contentFieldName("content") // Optional: field for document content
.embeddingFieldName("embedding") // Optional: field for vector embeddings
.vectorAlgorithm(Algorithm.HNSW) // Optional: HNSW or FLAT (defaults to HNSW)
.distanceMetric(DistanceMetric.COSINE) // Optional: COSINE, L2, or IP (defaults to COSINE)
.hnswM(16) // Optional: HNSW connections (defaults to 16)
.hnswEfConstruction(200) // Optional: HNSW build parameter (defaults to 200)
.hnswEfRuntime(10) // Optional: HNSW search parameter (defaults to 10)
.defaultRangeThreshold(0.8) // Optional: default radius for range searches
.textScorer(TextScorer.BM25) // Optional: text scoring algorithm (defaults to BM25)
.metadataFields( // Optional: define metadata fields for filtering
MetadataField.tag("country"),
MetadataField.numeric("year"),
MetadataField.text("description"))
.initializeSchema(true) // Optional: defaults to false
.batchingStrategy(new TokenCountBatchingStrategy()) // Optional: defaults to TokenCountBatchingStrategy
.build();
}
// This can be any EmbeddingModel implementation
@Bean
public EmbeddingModel embeddingModel() {
return new OpenAiEmbeddingModel(OpenAiEmbeddingOptions.builder().apiKey(System.getenv("OPENAI_API_KEY")).build());
}
참고: 필터 표현식에 사용하는 모든 메타데이터 필드 이름과 타입(
TAG,TEXT,NUMERIC)을 명시적으로 나열해야 해요. 위metadataFields는country(TAG 타입),year(NUMERIC 타입)라는 필터 가능 메타데이터 필드를 등록해요.
네이티브 클라이언트 접근
Redis 벡터 스토어 구현은 getNativeClient() 메서드를 통해 내부의 네이티브 Redis 클라이언트(RedisClient)에 접근할 수 있게 해 줘요.
RedisVectorStore vectorStore = context.getBean(RedisVectorStore.class);
Optional<RedisClient> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
RedisClient jedisClient = nativeClient.get();
// Use the native client for Redis-specific operations
}
네이티브 클라이언트는 VectorStore 인터페이스로 노출되지 않는 Redis 고유 기능과 연산에 접근할 수 있게 해 줘요.
거리 메트릭 (Distance Metrics)
Redis 벡터 스토어는 벡터 유사도에 세 가지 거리 메트릭을 지원해요.
- COSINE: 코사인 유사도(기본) — 벡터 사이 각도의 코사인 측정
- L2: 유클리드 거리 — 벡터 사이 직선 거리 측정
- IP: 내적(Inner Product) — 벡터 사이 점곱 측정
각 메트릭은 자동으로 0~1 유사도 점수로 정규화되며, 1이 가장 유사해요.
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.distanceMetric(DistanceMetric.COSINE) // or L2, IP
.build();
HNSW 알고리즘 설정
Redis 벡터 스토어는 효율적인 근사 최근접 이웃 검색을 위해 기본적으로 HNSW(Hierarchical Navigable Small World) 알고리즘을 사용해요. 사용 사례에 맞춰 HNSW 파라미터를 튜닝할 수 있어요.
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.vectorAlgorithm(Algorithm.HNSW)
.hnswM(32) // Maximum outgoing connections per node (default: 16)
.hnswEfConstruction(100) // Connections during index building (default: 200)
.hnswEfRuntime(50) // Connections during search (default: 10)
.build();
파라미터 가이드라인:
- M: 값이 높을수록 재현율(recall)이 좋아지지만 메모리와 인덱스 시간이 늘어나요. 일반적인 값: 12-48.
- EF_CONSTRUCTION: 값이 높을수록 인덱스 품질이 좋아지지만 빌드 시간이 늘어나요. 일반적인 값: 100-500.
- EF_RUNTIME: 값이 높을수록 검색 정확도가 좋아지지만 지연이 늘어나요. 일반적인 값: 10-100.
더 작은 데이터셋이거나 정확한 결과가 필요하면 FLAT 알고리즘을 대신 사용해요.
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.vectorAlgorithm(Algorithm.FLAT)
.build();
텍스트 검색 (Text Search)
Redis 벡터 스토어는 Redis Query Engine의 전문 검색 기능을 이용한 텍스트 검색을 제공해요. TEXT 필드의 키워드와 구절로 문서를 찾을 수 있어요.
// Search for documents containing specific text
List<Document> textResults = vectorStore.searchByText(
"machine learning", // search query
"content", // field to search (must be TEXT type)
10, // limit
"category == 'AI'" // optional filter expression
);
텍스트 검색이 지원하는 것:
- 단일 단어 검색
inOrder가 true일 때 정확히 일치하는 구절 검색inOrder가 false일 때 OR 의미론을 쓰는 용어 기반 검색- 일반 단어를 무시하는 불용어(stopword) 필터링
- 여러 텍스트 스코어링 알고리즘
구성 시점에 텍스트 검색 동작을 설정해요.
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.textScorer(TextScorer.TFIDF) // Text scoring algorithm
.inOrder(true) // Match terms in order
.stopwords(Set.of("is", "a", "the", "and")) // Ignore common words
.metadataFields(MetadataField.text("description")) // Define TEXT fields
.build();
텍스트 스코어링 알고리즘
여러 텍스트 스코어링 알고리즘을 사용할 수 있어요.
- BM25: 용어 포화(saturation)가 있는 TF-IDF의 현대 버전(기본)
- TFIDF: 고전적인 용어 빈도-역문서 빈도(term frequency-inverse document frequency)
- BM25STD: 표준화된 BM25
- DISMAX: Disjunction max
- DOCSCORE: 문서 점수
점수는 벡터 유사도 점수와 일관되도록 0~1 범위로 정규화돼요.
범위 검색 (Range Search)
범위 검색은 고정된 최근접 이웃 수 대신 지정된 반경 임계값 안의 모든 문서를 반환해요.
// Search with explicit radius
List<Document> rangeResults = vectorStore.searchByRange(
"AI and machine learning", // query
0.8, // radius (similarity threshold)
"category == 'AI'" // optional filter expression
);
구성 시점에 기본 범위 임계값을 설정할 수도 있어요.
RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
.defaultRangeThreshold(0.8) // Set default threshold
.build();
// Use default threshold
List<Document> results = vectorStore.searchByRange("query");
범위 검색은 특정 개수로 제한하는 대신 유사도 임계값 이상의 관련 문서를 모두 가져오고 싶을 때 유용해요.
시맨틱 캐싱 (Semantic Caching)
시맨틱 캐싱은 Redis 벡터 검색 기능을 활용해, 사용자 쿼리의 *의미적 유사도(semantic similarity)*를 기준으로 AI 채팅 응답을 캐시하고 재사용하는 강력한 최적화 기법이에요. 사용자가 비슷한 질문을 다르게 표현해도 지능적으로 응답을 재사용할 수 있어요.
왜 시맨틱 캐싱인가?
전통적인 캐싱은 정확한 키 일치에 의존해서, 의미상 동등한 질문을 다르게 표현하면 실패해요.
- "What is the capital of France?"
- "Tell me France's capital city"
- "Which city is the capital of France?"
세 질문 모두 동일한 답을 가지지만, 전통적인 캐싱은 서로 다른 요청으로 취급해서 불필요한 LLM API 호출이 발생해요. 시맨틱 캐싱은 벡터 임베딩으로 쿼리의 의미를 비교해서 이 문제를 해결해요.
이점:
- API 비용 절감: 비싼 LLM API에 대한 중복 호출을 피해요.
- 낮은 지연: 모델 추론을 기다리는 대신 캐시된 응답을 즉시 반환해요.
- 향상된 확장성: API 비용이 비례해서 늘지 않으면서 더 많은 쿼리 볼륨을 처리해요.
- 일관된 응답: 의미상 유사한 질문에 동일한 답을 반환해요.
자동 설정
Spring AI는 Redis 시맨틱 캐시용 Spring Boot 자동 설정을 제공해요. 활성화하려면 Maven pom.xml에 다음 의존성을 추가해요.
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-redis-semantic-cache</artifactId>
</dependency>
또는 Gradle build.gradle 파일에 이렇게 넣어요.
dependencies {
implementation 'org.springframework.ai:spring-ai-starter-vector-store-redis-semantic-cache'
}
참고: 자동 설정은 시맨틱 캐싱에 최적화된 기본 임베딩 모델(
redis/langcache-embed-v1)을 제공해요. 자신의EmbeddingModel빈을 제공해서 오버라이드할 수 있어요.
설정 프로퍼티
spring.ai.vectorstore.redis.semantic-cache.*로 시작하는 프로퍼티가 시맨틱 캐시를 구성해요.
| Property | Description | Default Value |
|---|---|---|
spring.ai.vectorstore.redis.semantic-cache.enabled |
Enable or disable the semantic cache | true |
spring.ai.vectorstore.redis.semantic-cache.host |
Redis server host | localhost |
spring.ai.vectorstore.redis.semantic-cache.port |
Redis server port | 6379 |
spring.ai.vectorstore.redis.semantic-cache.similarity-threshold |
Similarity threshold for cache hits (0.0-1.0). Higher values require closer semantic matches. | 0.95 |
spring.ai.vectorstore.redis.semantic-cache.index-name |
Name of the Redis search index for cache entries | semantic-cache-index |
spring.ai.vectorstore.redis.semantic-cache.prefix |
Key prefix for cached entries in Redis | semantic-cache: |
application.yml 예시 설정:
spring:
ai:
vectorstore:
redis:
semantic-cache:
enabled: true
host: localhost
port: 6379
similarity-threshold: 0.85
index-name: my-app-cache
prefix: "my-app:semantic-cache:"
SemanticCacheAdvisor 사용
SemanticCacheAdvisor는 Spring AI의 ChatClient 어드바이저 패턴과 자연스럽게 통합돼요. 응답을 자동으로 캐시하고 유사한 쿼리에 캐시된 결과를 반환해요.
@Autowired
private SemanticCache semanticCache;
@Autowired
private ChatModel chatModel;
public void example() {
// Create the cache advisor
SemanticCacheAdvisor cacheAdvisor = SemanticCacheAdvisor.builder()
.cache(semanticCache)
.build();
// First query - calls the LLM and caches the response
ChatResponse response1 = ChatClient.builder(chatModel)
.build()
.prompt("What is the capital of France?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// Similar query - returns cached response (no LLM call)
ChatResponse response2 = ChatClient.builder(chatModel)
.build()
.prompt("Tell me the capital city of France")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// response1 and response2 contain the same cached answer
}
어드바이저는 자동으로:
- LLM을 호출하기 전에 의미상 유사한 쿼리가 캐시에 있는지 확인해요.
- 유사도 임계값 이상으로 일치하면 캐시된 응답을 반환해요.
- 성공적인 LLM 호출 후 새 응답을 캐시해요.
- 동기 및 스트리밍 채팅 연산을 모두 지원해요.
직접 캐시 사용
미세한 제어를 위해 SemanticCache와 직접 상호작용할 수도 있어요.
@Autowired
private SemanticCache semanticCache;
// Store a response with a query
semanticCache.set("What is the capital of France?", chatResponse);
// Store with TTL (time-to-live) for automatic expiration
semanticCache.set("What's the weather today?", weatherResponse, Duration.ofHours(1));
// Retrieve a semantically similar response
Optional<ChatResponse> cached = semanticCache.get("Tell me France's capital");
if (cached.isPresent()) {
// Use the cached response
String answer = cached.get().getResult().getOutput().getText();
}
// Clear all cached entries
semanticCache.clear();
수동 설정
더 많은 제어가 필요하면 시맨틱 캐시 컴포넌트를 수동으로 구성할 수 있어요.
@Configuration
public class SemanticCacheConfig {
@Bean
public RedisClient jedisClient() {
return RedisClient.builder().hostAndPort("localhost", 6379).build();
}
@Bean
public SemanticCache semanticCache(RedisClient jedisClient, EmbeddingModel embeddingModel) {
return DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.distanceThreshold(0.3) // Lower = stricter matching
.indexName("my-semantic-cache")
.prefix("cache:")
.build();
}
@Bean
public SemanticCacheAdvisor semanticCacheAdvisor(SemanticCache cache) {
return SemanticCacheAdvisor.builder()
.cache(cache)
.build();
}
}
네임스페이스로 캐시 분리
멀티테넌트 애플리케이션이거나 별도의 캐시 공간이 필요할 때, 서로 다른 인덱스 이름으로 캐시 항목을 격리해요.
// Create isolated caches for different users or contexts
SemanticCache user1Cache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.indexName("user-1-cache")
.build();
SemanticCache user2Cache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.indexName("user-2-cache")
.build();
// Each user gets their own isolated cache space
SemanticCacheAdvisor user1Advisor = SemanticCacheAdvisor.builder()
.cache(user1Cache)
.build();
시스템 프롬프트 격리
SemanticCacheAdvisor는 시스템 프롬프트를 기준으로 캐시된 응답을 자동으로 격리해요. 이렇게 하면 같은 사용자 쿼리라도 시스템 프롬프트가 다르면 서로 다른 캐시 응답을 반환하는데, 여러 AI 페르소나나 문맥 의존 동작이 있는 애플리케이션에 필수적이에요.
SemanticCacheAdvisor cacheAdvisor = SemanticCacheAdvisor.builder()
.cache(semanticCache)
.build();
// Query with technical support persona
ChatResponse technicalResponse = ChatClient.builder(chatModel)
.build()
.prompt()
.system("You are a technical support specialist. Provide detailed technical answers.")
.user("How do I reset my password?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// Same query with customer service persona - cache MISS (different context)
ChatResponse serviceResponse = ChatClient.builder(chatModel)
.build()
.prompt()
.system("You are a friendly customer service agent. Keep responses brief and helpful.")
.user("How do I reset my password?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// Same query with technical support persona again - cache HIT
ChatResponse technicalAgain = ChatClient.builder(chatModel)
.build()
.prompt()
.system("You are a technical support specialist. Provide detailed technical answers.")
.user("How do I reset my password?")
.advisors(cacheAdvisor)
.call()
.chatResponse();
// Returns the cached technical response
동작 방식:
어드바이저는 시스템 프롬프트의 결정적 해시를 계산해서, 캐시 응답을 저장·검색할 때 메타데이터 필터로 사용해요.
- 같은 사용자 질문 + 같은 시스템 프롬프트 → 캐시 히트
- 같은 사용자 질문 + 다른 시스템 프롬프트 → 캐시 미스(별도 캐시 항목)
- 시스템 프롬프트가 없는 쿼리는 공통 캐시 공간을 공유
문맥 인지 캐시 API
고급 사용 사례에서는 문맥 인지 캐시 메서드를 직접 사용할 수 있어요.
// Store with explicit context hash
String contextHash = "technical-support-context";
semanticCache.set("How do I reset my password?", response, contextHash);
// Retrieve with context filtering
Optional<ChatResponse> cached = semanticCache.get("How do I reset my password?", contextHash);
// Different context hash returns empty (no match)
Optional<ChatResponse> otherContext = semanticCache.get("How do I reset my password?", "billing-context");
유사도 임계값 튜닝
유사도 임계값은 쿼리가 캐시 항목과 얼마나 가깝게 일치해야 히트로 간주되는지를 결정해요. 임계값은 0.0~1.0 사이 값으로 표현돼요.
- 높은 임계값(예: 0.95): 매우 가까운 시맨틱 일치를 요구해요. 오탐(false positives)은 줄지만 유효한 캐시 히트를 놓칠 수 있어요.
- 낮은 임계값(예: 0.70): 더 넓은 시맨틱 일치를 허용해요. 캐시 히트율은 높아지지만 덜 관련성 있는 캐시 응답을 반환할 수 있어요.
// Strict matching - only very similar queries hit the cache
SemanticCache strictCache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.distanceThreshold(0.2) // Strict (distance-based, lower = stricter)
.build();
// Lenient matching - broader semantic similarity accepted
SemanticCache lenientCache = DefaultSemanticCache.builder()
.jedisClient(jedisClient)
.embeddingModel(embeddingModel)
.distanceThreshold(0.5) // Lenient
.build();
참고: 높은 임계값(엄격한 매칭)에서 시작해서, 애플리케이션의 시맨틱 변화 허용 범위에 따라 점차 낮춰 보세요.
TTL과 캐시 만료
캐시된 응답에 time-to-live(TTL)를 설정해서 자동 만료시킬 수 있어요. 시간에 민감한 데이터에 필수적이에요.
// Cache weather data for 1 hour
semanticCache.set("What's the weather in New York?", weatherResponse, Duration.ofHours(1));
// Cache general knowledge indefinitely (no TTL)
semanticCache.set("What is photosynthesis?", scienceResponse);
// Redis automatically removes expired entries
동작 방식
시맨틱 캐시는 다음 흐름으로 동작해요.
- 쿼리 임베딩: 쿼리가 도착하면 설정된
EmbeddingModel로 벡터 임베딩으로 변환돼요. - 벡터 검색: Redis가 유사도 임계값 안의 캐시 항목을 찾기 위해 범위 기반 벡터 검색(
VECTOR_RANGE)을 수행해요. - 캐시 히트: 의미상 유사한 쿼리가 발견되면 캐시된
ChatResponse가 즉시 반환돼요. - 캐시 미스: 일치 항목이 없으면 쿼리가 LLM으로 진행되고, 응답이 추후 사용을 위해 캐시돼요.
이 구현은 Redis의 효율적인 벡터 인덱싱(HNSW 알고리즘)을 활용해서, 캐시 크기가 커도 빠른 유사도 검색을 제공해요.