Redis

このセクションでは、ドキュメントの埋め込みを保存し、類似性検索を実行するための RedisVectorStore のセットアップについて説明します。

Redis (英語) は、データベース、キャッシュ、メッセージブローカー、ストリーミングエンジンとして使用されるオープンソース (BSD ライセンス) のインメモリデータ構造ストアです。Redis は、文字列、ハッシュ、リスト、セット、範囲クエリを備えたソートセット、ビットマップ、ハイパーログログ、地理空間インデックス、ストリームなどのデータ構造を提供します。

Redis 検索とクエリ (英語) は、Redis OSS のコア機能を継承し、Redis をベクトルデータベースとして使用できるようにします。

  • ベクトルと関連するメタデータをハッシュまたは JSON ドキュメント内に保存します

  • ベクトルの取得

  • ベクトル類似性検索を実行する (KNN)

  • 半径しきい値を使用した範囲ベースのベクトル検索を実行する

  • TEXT フィールドで全文検索を実行する

  • 複数の距離メトリクス(COSINE、L2、IP)とベクトルアルゴリズムのサポート (HNSW, FLAT)

前提条件

  1. Redis スタックインスタンス

  2. EmbeddingModel インスタンスを使用してドキュメントの埋め込みを計算します。いくつかのオプションが利用可能です:

    • 必要に応じて、EmbeddingModel が RedisVectorStore によって保存される埋め込みを生成するための API キー。

自動構成

Spring AI 自動構成、スターターモジュールのアーティファクト名に大きな変更がありました。詳細については、アップグレードノートを参照してください。

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'
}
Spring AI BOM をビルドファイルに追加するには、"依存関係管理" セクションを参照してください。
アーティファクトリポジトリセクションを参照して、ビルドファイルに Maven Central リポジトリやスナップショットリポジトリを追加します。

ベクトルストアの実装では必要なスキーマを初期化できますが、適切なコンストラクターで initializeSchema ブール値を指定するか、application.properties ファイルで …​initialize-schema=true を設定することによってオプトインする必要があります。

これは重大な変更です。Spring AI の以前のバージョンでは、このスキーマの初期化はデフォルトで行われていました。

デフォルト値と構成オプションについては、ベクトルストアの構成パラメーターのリストを参照してください。

さらに、設定済みの EmbeddingModel Bean が必要です。詳細については、"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

redis 接続構成の場合、代わりに、Spring Boot の 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 を構成するために使用されます。

プロパティ 説明 デフォルト値

spring.ai.vectorstore.redis.initialize-schema

必要なスキーマを初期化するかどうか

false

spring.ai.vectorstore.redis.index-name

ベクトルを格納するインデックスの名前

spring-ai-index

spring.ai.vectorstore.redis.prefix

Redis キーのプレフィックス

embedding:

spring.ai.vectorstore.redis.distance-metric

ベクトル類似度の距離測定 (コサイン、L2、IP)

COSINE

spring.ai.vectorstore.redis.vector-algorithm

ベクトルインデックスアルゴリズム (HNSW, FLAT)

HNSW

spring.ai.vectorstore.redis.hnsw-m

HNSW: 最大送信接続数

16

spring.ai.vectorstore.redis.hnsw-ef-construction

HNSW: インデックス構築中の最大接続数

200

spring.ai.vectorstore.redis.hnsw-ef-runtime

HNSW: 検索中に考慮する接続の数

10

spring.ai.vectorstore.redis.default-range-threshold

範囲検索のデフォルトの半径しきい値

0.8

spring.ai.vectorstore.redis.text-scorer

テキストスコアリングアルゴリズム (BM25, TFIDF, BM25STD, DISMAX, DOCSCORE)

BM25

メタデータフィルタリング

Redis でも、汎用的でポータブルなメタデータフィルターを活用できます。

例: 次のいずれかのテキスト式言語を使用できます。

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 検索クエリ (英語) に変換されます。

例: この移植可能なフィルター式:

country in ['UK', 'NL'] && year >= 2020

独自の Redis フィルター形式に変換されます。

@country:{UK | NL} @year:[2020 inf]

手動構成

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 を作成します。

@Bean
public RedisClient jedisClient() {
    return RedisClient.builder().hostAndPort("<host>", 6379).build();
}

次に、ビルダーパターンを使用して RedisVectorStore Bean を作成します。

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

フィルター式で使用されるメタデータフィールドのすべてのメタデータフィールド名と型 (TAGTEXT、または NUMERIC) を明示的にリストする必要があります。上記の metadataFields は、フィルター可能なメタデータフィールド (型 TAG の country、型 NUMERIC の year ) を登録します。

ネイティブクライアントへのアクセス

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 固有の機能と操作にアクセスできます。

距離メトリクス

Redis ベクトルストアは、ベクトル類似性の 3 つの距離メトリクスをサポートしています。

  • COSINE : コサイン類似度(デフォルト) - ベクトル間の角度のコサインを測定します

  • L2 : ユークリッド距離 - ベクトル間の直線距離を測定する

  • IP : 内積 - ベクトル間の内積を測定する

各指標は自動的に 0 ~ 1 の類似度スコアに正規化され、1 が最も類似していることを示します。

RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
    .distanceMetric(DistanceMetric.COSINE)  // or L2, IP
    .build();

HNSW アルゴリズム構成

Redis ベクトルストアは、効率的な近似最近傍探索のために、デフォルトで HNSW(階層型ナビゲーション可能なスモールワールド)アルゴリズムを使用します。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 : 値が高いほど想起率は向上しますが、メモリ使用量とインデックス作成時間が増加します。典型的な値: 12-48.

  • EF_CONSTRUCTION : 値が大きいほどインデックスの品質は向上しますが、構築時間も長くなります。標準的な値: 100-500.

  • EF_RUNTIME : 値が大きいほど検索精度は向上しますが、レイテンシが増加します。一般的な値: 10-100.

データセットが小さい場合や、正確な結果が必要な場合は、代わりに FLAT アルゴリズムを使用してください。

RedisVectorStore vectorStore = RedisVectorStore.builder(jedisClient, embeddingModel)
    .vectorAlgorithm(Algorithm.FLAT)
    .build();

Redis ベクトルストアは、Redis クエリエンジンの全文検索機能を利用したテキスト検索機能を提供します。これにより、テキストフィールド内のキーワードやフレーズに基づいてドキュメントを検索できます。

// 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 が真の場合に完全一致によるフレーズ検索

  • inOrder が偽の場合の OR セマンティクスによる用語ベース検索

  • ストップワードフィルタリングで一般的な単語を無視する

  • 複数のテキストスコアリングアルゴリズム

構築時にテキスト検索の動作を設定する:

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 : 用語飽和を考慮した TF-IDF の最新版 (default)

  • TFIDF : 典型的な用語頻度 - 逆ドキュメント頻度

  • BM25STD : 標準化された BM25

  • DISMAX : 最大分離

  • DOCSCORE : ドキュメントスコア

スコアは、ベクトル類似度スコアとの整合性を保つため、0 ~ 1 の範囲に正規化されます。

範囲検索では、固定数の近傍ドキュメントではなく、指定された半径しきい値内のすべてのドキュメントが返されます。

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

範囲検索は、特定の件数に限定するのではなく、類似性の閾値を超えるすべての関連ドキュメントを取得したい場合に便利です。

セマンティックキャッシング

セマンティックキャッシングは、Redis ベクトル検索機能を活用した強力な最適化手法であり、文字列の完全一致ではなく、ユーザーのクエリの意味的な類似性に基づいて AI チャットレスポンスをキャッシュおよび取得します。これにより、ユーザーが類似した質問を異なる表現で尋ねた場合でも、レスポンスをインテリジェントに再利用することが可能になります。

セマンティックキャッシングのメリットとは?

従来のキャッシュはキーの完全一致に依存しているため、ユーザーが意味的に同等の質問を異なる表現で尋ねた場合、うまく機能しない。

  • フランスの首都はどこですか?

  • 「フランスの首都を教えてください」

  • 「フランスの首都はどの都市ですか?」

これら 3 つのクエリはすべて同じ回答を返しますが、従来のキャッシュではこれらを異なるリクエストとして扱い、結果として LLM API 呼び出しが重複してしまいます。セマンティックキャッシュは、ベクトル埋め込みを用いてクエリの意味を比較することでこの問題を解決します。

メリット:

  • Reduced API costs : 高コストな LLM API への冗長な呼び出しを避ける

  • Lower latency : モデル推論を待つことなく、キャッシュされたレスポンスを即座に返す

  • Improved scalability : API コストを比例的に増加させることなく、クエリ量の増加に対応します。

  • Consistent responses : 意味的に類似した質問に対して、同一の回答を返す

自動構成

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 Bean を指定することで、これを上書きできます。

プロパティの構成

spring.ai.vectorstore.redis.semantic-cache.* で始まるプロパティは、セマンティックキャッシュを構成します。

プロパティ 説明 デフォルト値

spring.ai.vectorstore.redis.semantic-cache.enabled

セマンティックキャッシュを有効または無効にする

true

spring.ai.vectorstore.redis.semantic-cache.host

Redis サーバーホスト

localhost

spring.ai.vectorstore.redis.semantic-cache.port

Redis サーバーポート

6379

spring.ai.vectorstore.redis.semantic-cache.similarity-threshold

キャッシュヒットの類似度閾値(0.0-1.0)。値が高いほど、より厳密な意味的一致が求められます。

0.95

spring.ai.vectorstore.redis.semantic-cache.index-name

キャッシュエントリの Redis 検索インデックスの名前

semantic-cache-index

spring.ai.vectorstore.redis.semantic-cache.prefix

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
}

アドバイザーは自動的に次のことを行います。

  1. LLM を呼び出す前に、意味的に類似したクエリがキャッシュに存在するかを確認します。

  2. Returns cached responses when a match is found above the similarity threshold

  3. Caches new responses after successful LLM calls

  4. Supports both synchronous and streaming chat operations

Direct Cache Usage

You can also interact with the SemanticCache directly for fine-grained control:

@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();

手動構成

For more control, you can manually configure the semantic cache components:

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

Cache Isolation with Namespaces

For multi-tenant applications or when you need separate cache spaces, use different index names to isolate cache entries:

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

System Prompt Isolation

The SemanticCacheAdvisor automatically isolates cached responses based on the system prompt. This ensures that the same user query with different system prompts returns different cached responses, which is essential for applications with multiple AI personas or context-dependent behavior.

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

使い方:

The advisor computes a deterministic hash of the system prompt and uses it as a metadata filter when storing and retrieving cached responses:

  • Same user question + same system prompt → cache hit

  • Same user question + different system prompt → cache miss (separate cache entry)

  • Queries without a system prompt share a common cache space

Context-Aware Cache API

For advanced use cases, you can use the context-aware cache methods directly:

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

Tuning the Similarity Threshold

The similarity threshold determines how closely a query must match a cached entry to be considered a hit. The threshold is expressed as a value between 0.0 and 1.0:

  • Higher threshold (e.g., 0.95) : Requires very close semantic matches. Reduces false positives but may miss valid cache hits.

  • Lower threshold (e.g., 0.70) : Allows broader semantic matches. Increases cache hit rate but may return less relevant cached responses.

// 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();
Start with a higher threshold (stricter matching) and gradually lower it based on your application’s tolerance for semantic variation.

TTL and Cache Expiration

Cached responses can be configured with a time-to-live (TTL) for automatic expiration. This is essential for time-sensitive data:

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

使い方

The semantic cache operates using the following flow:

  1. Query embedding : When a query arrives, it is converted to a vector embedding using the configured EmbeddingModel

  2. Vector search : Redis performs a range-based vector search (VECTOR_RANGE) to find cached entries within the similarity threshold

  3. Cache hit : If a semantically similar query is found, the cached ChatResponse is returned immediately

  4. Cache miss : If no match is found, the query proceeds to the LLM, and the response is cached for future use

The implementation leverages Redis’s efficient vector indexing (HNSW algorithm) for fast similarity searches, even with large cache sizes.