本文主要研究一下Spring AI的RedisVectorStore

示例

pom.xml

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>

配置

spring:
  ai:
    vectorstore:
      type: redis
      redis:
        initialize-schema: true
        indexName: default-idx
        prefix: "default:"

代码

    @Test
    public void testAddAndSearch() {
        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 Milvus Vector Store
        redisVectorStore.add(documents);

        // Retrieve documents similar to a query
        List<Document> results = this.redisVectorStore.similaritySearch(SearchRequest.builder().query("Spring").topK(5).build());
        log.info("results:{}", JSON.toJSONString(results));
    }

输出如下:

results:[{"contentFormatter":{"excludedEmbedMetadataKeys":[],"excludedInferenceMetadataKeys":[],"metadataSeparator":"\n","metadataTemplate":"{key}: {value}","textTemplate":"{metadata_string}\n\n{content}"},"formattedContent":"distance: 0.21754569\nvector_score: 0.21754569\n\nSpring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!","id":"8edf4d98-6730-4b19-8681-67bbd7aa002d","metadata":{"distance":0.21754569,"vector_score":0.21754569},"score":0.7824543118476868,"text":"Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!"},{"contentFormatter":{"$ref":"$[0].contentFormatter"},"formattedContent":"distance: 0.21754569\nvector_score: 0.21754569\n\nSpring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!","id":"dbdcc61f-7f7c-4c61-9491-aaf3ddb15ae9","metadata":{"distance":0.21754569,"vector_score":0.21754569},"score":0.7824543118476868,"text":"Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!"},{"contentFormatter":{"$ref":"$[0].contentFormatter"},"formattedContent":"distance: 0.2854656\nvector_score: 0.2854656\n\nThe World is Big and Salvation Lurks Around the Corner","id":"a28f2344-3389-427a-9a3d-69e3a1000a05","metadata":{"distance":0.2854656,"vector_score":0.2854656},"score":0.7145344018936157,"text":"The World is Big and Salvation Lurks Around the Corner"},{"contentFormatter":{"$ref":"$[0].contentFormatter"},"formattedContent":"distance: 0.2854656\nvector_score: 0.2854656\n\nThe World is Big and Salvation Lurks Around the Corner","id":"874aff9b-cd3d-41d9-9114-47157f3e4ccc","metadata":{"distance":0.2854656,"vector_score":0.2854656},"score":0.7145344018936157,"text":"The World is Big and Salvation Lurks Around the Corner"},{"contentFormatter":{"$ref":"$[0].contentFormatter"},"formattedContent":"distance: 0.2968012\nvector_score: 0.2968012\n\nYou walk forward facing the past and you turn back toward the future.","id":"c90a7824-dcdb-4855-ab07-0ac3629fba83","metadata":{"distance":0.2968012,"vector_score":0.2968012},"score":0.7031987905502319,"text":"You walk forward facing the past and you turn back toward the future."}]

源码

RedisVectorStoreAutoConfiguration

org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreAutoConfiguration.java

@AutoConfiguration(after = RedisAutoConfiguration.class)
@ConditionalOnClass({ JedisPooled.class, JedisConnectionFactory.class, RedisVectorStore.class, EmbeddingModel.class })
@ConditionalOnBean(JedisConnectionFactory.class)
@EnableConfigurationProperties(RedisVectorStoreProperties.class)
@ConditionalOnProperty(name = SpringAIVectorStoreTypes.TYPE, havingValue = SpringAIVectorStoreTypes.REDIS,
        matchIfMissing = true)
public class RedisVectorStoreAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(BatchingStrategy.class)
    BatchingStrategy batchingStrategy() {
        return new TokenCountBatchingStrategy();
    }

    @Bean
    @ConditionalOnMissingBean
    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())
            .build();
    }

    private JedisPooled jedisPooled(JedisConnectionFactory jedisConnectionFactory) {

        String host = jedisConnectionFactory.getHostName();
        int port = jedisConnectionFactory.getPort();

        JedisClientConfig clientConfig = DefaultJedisClientConfig.builder()
            .ssl(jedisConnectionFactory.isUseSsl())
            .clientName(jedisConnectionFactory.getClientName())
            .timeoutMillis(jedisConnectionFactory.getTimeout())
            .password(jedisConnectionFactory.getPassword())
            .build();

        return new JedisPooled(new HostAndPort(host, port), clientConfig);
    }

}
RedisVectorStoreAutoConfiguration在spring.ai.vectorstore.typeredis时启用,它在RedisAutoConfiguration之后自动配置,它依赖EmbeddingModel及RedisVectorStoreProperties来创建RedisVectorStore

RedisVectorStoreProperties

org/springframework/ai/vectorstore/redis/autoconfigure/RedisVectorStoreProperties.java

@ConfigurationProperties(RedisVectorStoreProperties.CONFIG_PREFIX)
public class RedisVectorStoreProperties extends CommonVectorStoreProperties {

    public static final String CONFIG_PREFIX = "spring.ai.vectorstore.redis";

    private String indexName = "default-index";

    private String prefix = "default:";

    public String getIndexName() {
        return this.indexName;
    }

    public void setIndexName(String indexName) {
        this.indexName = indexName;
    }

    public String getPrefix() {
        return this.prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

}
RedisVectorStoreProperties主要是配置spring.ai.vectorstore.redis配置,它从CommonVectorStoreProperties继承了initializeSchema属性,自己提供了indexName、prefix属性

RedisAutoConfiguration

org/springframework/boot/autoconfigure/data/redis/RedisAutoConfiguration.java

@AutoConfiguration
@ConditionalOnClass(RedisOperations.class)
@EnableConfigurationProperties(RedisProperties.class)
@Import({ LettuceConnectionConfiguration.class, JedisConnectionConfiguration.class })
public class RedisAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(RedisConnectionDetails.class)
    PropertiesRedisConnectionDetails redisConnectionDetails(RedisProperties properties) {
        return new PropertiesRedisConnectionDetails(properties);
    }

    @Bean
    @ConditionalOnMissingBean(name = "redisTemplate")
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnSingleCandidate(RedisConnectionFactory.class)
    public StringRedisTemplate stringRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        return new StringRedisTemplate(redisConnectionFactory);
    }

}
RedisAutoConfiguration依赖RedisProperties以及LettuceConnectionConfiguration或者JedisConnectionConfiguration

RedisProperties

org/springframework/boot/autoconfigure/data/redis/RedisProperties.java

@ConfigurationProperties(prefix = "spring.data.redis")
public class RedisProperties {

    /**
     * Database index used by the connection factory.
     */
    private int database = 0;

    /**
     * Connection URL. Overrides host, port, username, and password. Example:
     * redis://user:password@example.com:6379
     */
    private String url;

    /**
     * Redis server host.
     */
    private String host = "localhost";

    /**
     * Login username of the redis server.
     */
    private String username;

    /**
     * Login password of the redis server.
     */
    private String password;

    /**
     * Redis server port.
     */
    private int port = 6379;

    /**
     * Read timeout.
     */
    private Duration timeout;

    /**
     * Connection timeout.
     */
    private Duration connectTimeout;

    /**
     * Client name to be set on connections with CLIENT SETNAME.
     */
    private String clientName;

    /**
     * Type of client to use. By default, auto-detected according to the classpath.
     */
    private ClientType clientType;

    private Sentinel sentinel;

    private Cluster cluster;

    private final Ssl ssl = new Ssl();

    private final Jedis jedis = new Jedis();

    private final Lettuce lettuce = new Lettuce();

    //......
}    
RedisProperties主要是配置spring.data.redis,它提供了database、url、host、username、password、port、timeout、connectTimeout、clientName、clientType、sentinel、cluster、ssl、jedis、lettuce属性

小结

Spring AI提供了spring-ai-starter-vector-store-redis用于自动装配RedisVectorStore。可以使用redis/redis-stack:latest这个docker镜像来验证。

doc


codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...