spring-projects / spring-projects/spring-ai

RedisVectorStore auto-configuration does not support metadataFields, causing chat memory retrieval failure

Open
#3,690 2 comments 1 reaction 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Java
Stars
9.5k
Forks
2.9k
Avg merge
1d 10h
Merged PRs (30d)
5

Description

Bug description
When using VectorStoreChatMemoryAdvisor to implement chat memory with Redis vector store, if spring.ai.vectorstore.redis.initialize-schema is set to true, the program will automatically create a Redis search schema, which causes RedisVectorStore to be unable to retrieve related records.

The reason is that VectorStoreChatMemoryAdvisor writes "conversationId" as metadata into the document and uses conversationId as the query condition. However, RedisVectorStoreProperties does not support setting metadataFields when automatically configuring RedisVectorStore, leading to retrieval failure.

It is necessary to manually configure RedisVectorStore to retrieve records correctly, like this (see code below), which is not elegant because it defeats the purpose of auto-configuration.

@Bean
public RedisVectorStore vectorStore(EmbeddingModel embeddingModel, RedisVectorStoreProperties properties,
                                    JedisConnectionFactory jedisConnectionFactory, ObjectProvider<ObservationRegistry> observationRegistry,
                                    ObjectProvider<VectorStoreObservationConvention> customObservationConvention,
                                    BatchingStrategy batchingStrategy) {

    JedisPooled jedisPooled = this.jedisPooled(jedisConnectionFactory);
    return RedisVectorStore.builder(jedisPooled, embeddingModel)
            .initializeSchema(properties.isInitializeSchema())
            .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
            .customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
            .batchingStrategy(batchingStrategy)
            .indexName(properties.getIndexName())
            .prefix(properties.getPrefix())
            // Set metadata fields to include conversationId for retrieval
            .metadataFields(RedisVectorStore.MetadataField.tag("conversationId"))
            .build();
}

Environment

  • Spring Boot: 3.4.6
  • Spring AI: 1.0.0
  • Java: 21
  • Vector store: redis-stack v7.2.0

Steps to reproduce

  1. Use VectorStoreChatMemoryAdvisor with Redis vector store.
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-advisors-vector-store</artifactId>
    <version>1.0.0</version>
</dependency>
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-redis</artifactId>
    <version>1.0.0</version>
</dependency>
// configuration
@Bean
public ChatClient chatClient(ChatClient.Builder clientBuilder, VectorStore vectorStore) {
    return clientBuilder
            .defaultSystem("You are a helpful assistant.")
            .defaultAdvisors(VectorStoreChatMemoryAdvisor.builder(vectorStore).build())
            .build();
}
  1. Set spring.ai.vectorstore.redis.initialize-schema=true.
spring:
  data:
    redis:
      client-type: jedis
  ai:
    ollama:
      chat:
        options:
          model: deepseek-r1
      embedding:
        options:
          model: bge-m3
    vectorstore:
      redis:
        initialize-schema: true
  1. Attempt to retrieve chat memory by conversationId in a conversation.
// controller
@GetMapping(value = "/stream/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam @NotBlank String prompt, @RequestParam @NotBlank String conversationId) {
    return chatClient
            .prompt(Prompt.builder().content(prompt).build())
            .advisors(advisorSpec -> advisorSpec.param(ChatMemory.CONVERSATION_ID, conversationId))
            .stream().content();
}

Expected behavior
Chat memory relevant to the current conversationId should be retrievable automatically without manual configuration of RedisVectorStore.

Possible solutions

  1. Set spring.ai.vectorstore.redis.initialize-schema=false and manually create the schema in Redis.
  2. Add a List<MetadataField> metadataFields property to RedisVectorStoreProperties.
  3. Use RedisVectorStore.Builder as a dependency to build RedisVectorStore, where RedisVectorStore.Builder depends on RedisVectorStoreProperties and provides a RedisVectorStoreBuilderCustomizer for builder enhancement, like this:
// VectorStoreBuilderCustomizer.java
public interface VectorStoreBuilderCustomizer<T extends VectorStore.Builder<T>> {
    void customize(T builder);
}
// RedisVectorStoreBuilderCustomizer.java
public interface RedisVectorStoreBuilderCustomizer extends VectorStoreBuilderCustomizer<RedisVectorStore.Builder> {

}
// RedisVectorStoreAutoConfiguration.java
@Bean
@ConditionalOnMissingBean
public RedisVectorStore.Builder vectorStoreBuilder(EmbeddingModel embeddingModel, RedisVectorStoreProperties properties,
                                    JedisConnectionFactory jedisConnectionFactory, ObjectProvider<ObservationRegistry> observationRegistry,
                                    ObjectProvider<VectorStoreObservationConvention> customObservationConvention,
                                    BatchingStrategy batchingStrategy, ObjectProvider<RedisVectorStoreBuilderCustomizer> vectorStoreBuilderCustomizers) {

    JedisPooled jedisPooled = this.jedisPooled(jedisConnectionFactory);
    RedisVectorStore.Builder builder = RedisVectorStore.builder(jedisPooled, embeddingModel)
            .initializeSchema(properties.isInitializeSchema())
            .observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
            .customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
            .batchingStrategy(batchingStrategy)
            .indexName(properties.getIndexName())
            .prefix(properties.getPrefix());
    vectorStoreBuilderCustomizers.orderedStream().forEach(customizer -> customizer.customize(builder));
    return builder;
}

@Bean
@ConditionalOnMissingBean
public RedisVectorStore vectorStore(RedisVectorStore.Builder vectorStoreBuilder) {
    return vectorStoreBuilder.build();
}

I am not sure why RedisVectorStoreProperties does not directly provide a metadataFields property. If there is a reason, please consider solution 3. In fact, solution 3 does not explicitly depend on RedisVectorStoreProperties, but instead uses RedisVectorStore.Builder to determine how to build the RedisVectorStore.

If you are open to my suggestion, I can propose a PR.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reading RedisVectorStoreProperties and RedisVectorStoreAutoConfiguration, then trace how the auto-configured RedisVectorStore builder handles metadata fields and schema initialization. Check the VectorStoreChatMemoryAdvisor conversationId retrieval path and existing Redis auto-configuration tests. Done means automatically configured Redis storage can retrieve records by conversationId without manual bean configuration.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, redis, spring-boot
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.