上一篇文章我们聊了对话记忆——用Redis做滑动窗口,让AI在一轮会话中记住最近几轮说了什么。但 Redis 方案有两个绕不开的局限:

  • 会话结束就丢:用户关掉页面重开,之前的对话历史就没了,Redis过期一清理,AI直接“失忆”。
  • 语义检索能力弱:Redis只能按时间顺序取最近N条消息,但你没办法做到“用户问了一个新问题后,从过去的对话中找到语义上相似的那几段历史”。

这两个问题,恰好指向了AI应用的下一个技术台阶:长期记忆

所谓长期记忆,就是你今天问AI“我之前提到过什么爱好”,它能从几个月前的对话里翻出来。这听起来有点像搜索引擎,但比搜索引擎更精细——它需要理解语义,而不是靠关键词匹配。

本文将围绕两个问题展开:向量数据库为什么适合做长期记忆?Java开发者怎么把它用到项目里?

一、长期记忆 vs 短期记忆:有什么区别

先理清概念。上一篇文章的“对话记忆”本质上是一种短期记忆

  • 存储在Redis中,按时间顺序逐条保存;
  • 每次请求把最近N条消息拼接到Prompt里;
  • 会话结束(比如用户关闭页面)或者TTL过期,数据就没了。

短期记忆解决的是“多轮对话连贯性”的问题——AI能记住你上一句说了什么。但它解决不了“跨会话的记忆”:你昨天说过喜欢看科幻电影,今天重启对话后它已经不记得了。

长期记忆要做的,恰恰是解决这个“跨会话”的问题。它需要具备几个基本能力:

  1. 持久化存储:数据不会因为会话结束或服务重启而丢失。
  2. 语义检索:能根据当前问题的语义,从历史记忆中召回相关内容,而不是简单地取最近几条。
  3. 高效检索:在海量历史数据中快速找到最相关的内容。

那为什么传统的关系型数据库(MySQL)或者Redis做不了这件事?

二、向量数据库:为什么它是长期记忆的理想方案

要理解向量数据库,先得知道什么是“向量化”。

2.1 什么是向量

大语言模型在理解文本之前,会把文本转换成一组数字——也就是向量。比如“我喜欢看科幻电影”这句话,经过嵌入模型处理后,会变成一个几百维的浮点数数组。在向量空间中,语义相似的文本在空间里的距离也更近

举个例子:

  • “我喜欢科幻片”
  • “我对科幻电影很感兴趣”

这两句话的关键词不完全一样,但它们的向量在空间中的位置非常接近。这就是向量检索能做到“语义搜索”的原理——不像传统数据库那样依赖关键词匹配,而是靠计算向量之间的距离来判断语义相关性。

2.2 向量数据库的核心能力

向量数据库就是专门为存储和检索向量而设计的数据库。它有几个关键能力:

  • 高效相似性检索:使用ANN算法在海量向量中快速找到最相似的Top-K结果。有些向量数据库在10亿级数据中检索仅需15毫秒
  • 混合检索:支持“向量相似度 + 元数据过滤”的组合查询,比如“在最近30天的对话中找到与‘退货流程’语义相近的记录”
  • 动态写入:支持高吞吐的实时写入和更新

2.3 向量数据库 vs 传统数据库

维度 MySQL / PostgreSQL 向量数据库
检索方式 精确匹配 / 全文索引 相似度匹配
语义理解 依赖分词和关键词 自动捕捉语义
“苹果”和“iPhone” 不匹配 语义相近
适合存储 结构化数据 向量+元数据

三、主流向量数据库选型对比

目前市面上主流的向量数据库方案有好几种,笔者按三个维度做了分类:

3.1 开源自建方案

  • Milvus:目前功能最完整的开源向量数据库,支持分布式部署、GPU加速、多种索引类型(HNSW、IVF_FLAT等),社区活跃,文档齐全。缺点是部署和运维有一定门槛。
  • Qdrant:Rust语言编写,性能优异,内存管理高效。在相同硬件条件下吞吐量高于行业平均水平。部署相对简单,适合中小规模。
  • pgvector:PostgreSQL的向量扩展插件。优点是如果你的业务数据已经在PostgreSQL里,直接加一个扩展就行,不需要引入新组件。缺点是纯向量检索性能不如专门的向量数据库,适合百万级以内的中小规模数据。

3.2 云托管方案

  • 阿里云AnalyticDB PostgreSQL版(ADB-PG) :云原生数据仓库,内置向量检索引擎优化,天然与Spring AI Alibaba集成。适合不想自己运维数据库、数据量大的场景。
  • Pinecone:全托管云服务,开箱即用,零运维。缺点是使用海外服务可能有数据合规问题,成本也比较高。

3.3 Java生态兼容性

对于Java开发者来说,选型时需要重点考虑两个问题:

  • SDK成熟度:主流的向量数据库基本都提供了Java SDK。Milvus官方SDK在GitHub上有完整的文档示例;Qdrant有REST API + Java客户端;pgvector用标准JDBC就能操作。
  • 与Spring AI Alibaba的集成:Spring AI Alibaba内置了对多种向量存储的支持,包括Redis、Milvus、阿里云ADB-PG等,通过统一API即可切换底层存储。

3.4 如何选型

建议根据发展阶段选择:

阶段 推荐方案 理由
原型/POC验证 Milvus Lite 或 pgvector 轻量、快速上手、无需复杂运维
中小企业生产(<100万条) pgvector 或 Milvus单机 性能足够,运维简单
大规模生产(>100万条) Milvus分布式 或 Qdrant集群 支持高并发、可扩展
不想管运维 云托管方案 全托管、弹性伸缩

如果团队已经在使用阿里云,或者项目需要对接通义大模型,那么ADB-PG + Spring AI Alibaba的组合会比较顺手,生态上支持得比较好。

四、实战:用Milvus + Spring AI Alibaba实现长期记忆

接下来,以Milvus为例,演示如何用Java操作向量数据库,实现长期记忆的存储和检索。

4.1 搭建Milvus环境

最简单的方式是用Docker跑一个Milvus单机版:

# 下载docker-compose配置
wget https://github.com/milvus-io/milvus/releases/download/v2.3.3/milvus-standalone-docker-compose.yml -O docker-compose.yml

# 启动服务
docker-compose up -d

服务启动后,Milvus会暴露19530端口供客户端连接

4.2 添加依赖

在pom.xml中添加Milvus官方Java SDK:

<dependency>
    <groupId>io.milvus</groupId>
    <artifactId>milvus-sdk-java</artifactId>
    <version>2.3.0</version>
</dependency>

4.3 配置Milvus连接

用Spring配置类初始化客户端:

@Configuration
public class MilvusConfig {
    
    @Bean
    public MilvusServiceClient milvusClient() {
        ConnectParam connectParam = ConnectParam.newBuilder()
            .withHost("localhost")
            .withPort(19530)
            .build();
        return new MilvusServiceClient(connectParam);
    }
}

4.4 创建集合(Collection)

在Milvus中,“集合”类似于关系型数据库中的“表”。我们需要创建一个专门存储对话记忆的集合:

@Service
public class MemoryVectorService {
    
    @Autowired
    private MilvusServiceClient milvusClient;
    
    private static final String COLLECTION_NAME = "chat_memory";
    private static final int VECTOR_DIMENSION = 768;  // 取决于使用的Embedding模型
    
    public void createCollection() {
        // 定义向量字段
        FieldType vectorField = FieldType.newBuilder()
            .withName("embedding")
            .withDataType(DataType.FLOAT_VECTOR)
            .withDimension(VECTOR_DIMENSION)
            .build();
        
        // 定义标量字段(元数据),用于过滤
        FieldType userField = FieldType.newBuilder()
            .withName("user_id")
            .withDataType(DataType.VARCHAR)
            .withMaxLength(64)
            .build();
        
        FieldType contentField = FieldType.newBuilder()
            .withName("content")
            .withDataType(DataType.VARCHAR)
            .withMaxLength(2000)
            .build();
        
        FieldType timestampField = FieldType.newBuilder()
            .withName("timestamp")
            .withDataType(DataType.INT64)
            .build();
        
        CreateCollectionParam createParam = CreateCollectionParam.newBuilder()
            .withCollectionName(COLLECTION_NAME)
            .withDescription("对话长期记忆存储")
            .withFieldTypes(Arrays.asList(vectorField, userField, contentField, timestampField))
            .build();
        
        milvusClient.createCollection(createParam);
    }
}

4.5 嵌入文本并存储

存储对话记忆时,需要先把对话内容转换成向量,然后连同原始文本和元数据一起存入Milvus:

public void storeMemory(String userId, String content) {
    // 1. 调用嵌入模型生成向量(这里用伪代码示意)
    List<Float> embedding = generateEmbedding(content);  // 实际调用OpenAI或本地Embedding模型
    
    // 2. 构造插入数据
    List<List<Float>> vectors = Collections.singletonList(embedding);
    
    InsertParam insertParam = InsertParam.newBuilder()
        .withCollectionName(COLLECTION_NAME)
        .withFields(
            Collections.singletonList(new InsertParam.Field("embedding", vectors))
        )
        .withFields(
            Arrays.asList(
                new InsertParam.Field("user_id", Collections.singletonList(userId)),
                new InsertParam.Field("content", Collections.singletonList(content)),
                new InsertParam.Field("timestamp", Collections.singletonList(System.currentTimeMillis()))
            )
        )
        .build();
    
    milvusClient.insert(insertParam);
}

4.6 语义检索记忆

当用户发起新的对话时,将当前问题向量化,在向量库中检索最相似的K条历史记录:

public List<String> retrieveRelevantMemories(String userId, String query) {
    // 1. 将查询文本向量化
    List<Float> queryVector = generateEmbedding(query);
    
    // 2. 构造检索参数
    SearchParam searchParam = SearchParam.newBuilder()
        .withCollectionName(COLLECTION_NAME)
        .withFloatVectors(Collections.singletonList(queryVector))
        .withVectorFieldName("embedding")
        .withTopK(5)  // 返回最相似的5条
        .withMetricType(MetricType.IP)  // 内积相似度
        .withExpr(String.format("user_id == '%s'", userId))  // 按用户过滤
        .build();
    
    // 3. 执行检索
    SearchResults searchResults = milvusClient.search(searchParam);
    
    // 4. 解析结果,返回相似记忆的内容
    List<String> memories = new ArrayList<>();
    for (SearchResults.SearchResult result : searchResults.getResults()) {
        memories.add(result.get("content").toString());
    }
    return memories;
}

4.7 集成到Spring AI Alibaba的对话流程

检索到的记忆可以作为系统级上下文,和当前对话一起发给大模型:

@Service
public class ChatService {
    
    @Autowired
    private MemoryVectorService memoryService;
    
    @Autowired
    private ChatClient chatClient;
    
    public String chat(String userId, String userMessage) {
        // 1. 从向量数据库检索与当前问题相关的历史记忆
        List<String> relevantMemories = memoryService.retrieveRelevantMemories(userId, userMessage);
        
        // 2. 构建带记忆的上下文
        StringBuilder context = new StringBuilder();
        if (!relevantMemories.isEmpty()) {
            context.append("以下是用户之前提到的相关内容:\n");
            for (String memory : relevantMemories) {
                context.append("- ").append(memory).append("\n");
            }
        }
        context.append("当前用户说:").append(userMessage);
        
        // 3. 调用大模型(LLM自动结合记忆和当前问题生成回复)
        String reply = chatClient.prompt()
            .user(context.toString())
            .call()
            .content();
        
        // 4. 将本轮对话存入向量库(异步处理,不阻塞响应)
        asyncStoreMemory(userId, userMessage, reply);
        
        return reply;
    }
}

五、工程落地的几个坑

在把向量数据库集成到生产环境时,有几个实际踩过的坑值得提一下。

5.1 Embedding模型选哪个

嵌入模型直接决定了检索质量。如果面向中文对话场景,推荐使用通义千问Embedding、M3E等中文优化过的模型,比直接用英文模型的检索准确率高不少。笔者在测试中试过,同样的中文查询,用中文Embedding模型召回的相关内容比用英文模型多了大概三到四成。

5.2 实时写入 vs 批量写入

如果每次对话后都立即写入向量库,高并发场景下写入压力会比较大。一个常见的工程优化是:用消息队列(如RocketMQ)异步处理写入任务,把对话内容丢进MQ,后台消费再批量写入向量数据库。这样可以削峰填谷,降低对主业务流程的影响。

5.3 冷热数据分层

大部分对话历史实际上很少被检索到,没必要全放在高性能存储里。可以设计一个简单的策略:最近7天的对话数据放在热库(高性能向量库),7天以上的迁移到成本更低的存储上。很多向量数据库(包括Milvus、ADB-PG)都支持数据分层存储。

5.4 检索结果的准确性问题

这是最需要关注的问题:向量检索不是100%精确的,有时候会召回语义不相关的内容。可以在生产环境中增加一个“相关性分”阈值,只采纳分数高于某个值的结果,低于阈值的直接丢弃。同时建议在用户侧保留“查看检索来源”的能力,方便调试。

六、阿里云百炼“记忆库”:更省心的选择

如果你不想自己搭建和维护向量数据库,阿里云百炼平台提供了一个开箱即用的选择。

2026年4月9日,阿里云百炼正式上线了“记忆库”功能,内置了“提取-存储-检索-注入”四大模块。用户与AI对话结束后,系统可以根据配置的记忆规则自动提取关键信息并存储;下次提问时,自动检索相关记忆并附加到上下文中。目前该功能限时免费开放,可以通过API直接调用

对于中小型项目来说,使用“记忆库”可以大幅降低开发成本,快速上线带长期记忆的AI应用。

七、总结

长期记忆是AI应用走向智能化的关键一环。本文介绍了向量数据库在长期记忆场景中的应用价值和选型思路,并通过Milvus给出了Java端的完整实现。

几点核心结论:

  1. 短期记忆用Redis,长期记忆用向量数据库。前者解决单次会话内的连贯性,后者解决跨会话的语义检索需求。
  2. 选型根据数据规模走渐进式路线:原型验证用轻量方案,大规模生产再考虑分布式部署。
  3. 生产环境注意Embedding模型选择和异步写入,这两个问题直接影响系统的召回效果和吞吐能力。
  4. 不想自建就考虑云方案,阿里云百炼的记忆库功能是目前开箱即用的不错选择

最后,如果觉得这篇文章对你有帮助,欢迎点赞、收藏、评论。下期会写《AI Agent工具调用的Java实现:让LLM自己决定调用哪个API》,敬请关注。

更多推荐