Java工程师如何用向量数据库为AI构建长期记忆?
上一篇文章我们聊了对话记忆——用Redis做滑动窗口,让AI在一轮会话中记住最近几轮说了什么。但 Redis 方案有两个绕不开的局限:
- 会话结束就丢:用户关掉页面重开,之前的对话历史就没了,Redis过期一清理,AI直接“失忆”。
- 语义检索能力弱:Redis只能按时间顺序取最近N条消息,但你没办法做到“用户问了一个新问题后,从过去的对话中找到语义上相似的那几段历史”。
这两个问题,恰好指向了AI应用的下一个技术台阶:长期记忆。
所谓长期记忆,就是你今天问AI“我之前提到过什么爱好”,它能从几个月前的对话里翻出来。这听起来有点像搜索引擎,但比搜索引擎更精细——它需要理解语义,而不是靠关键词匹配。
本文将围绕两个问题展开:向量数据库为什么适合做长期记忆?Java开发者怎么把它用到项目里?
一、长期记忆 vs 短期记忆:有什么区别
先理清概念。上一篇文章的“对话记忆”本质上是一种短期记忆:
- 存储在Redis中,按时间顺序逐条保存;
- 每次请求把最近N条消息拼接到Prompt里;
- 会话结束(比如用户关闭页面)或者TTL过期,数据就没了。
短期记忆解决的是“多轮对话连贯性”的问题——AI能记住你上一句说了什么。但它解决不了“跨会话的记忆”:你昨天说过喜欢看科幻电影,今天重启对话后它已经不记得了。
长期记忆要做的,恰恰是解决这个“跨会话”的问题。它需要具备几个基本能力:
- 持久化存储:数据不会因为会话结束或服务重启而丢失。
- 语义检索:能根据当前问题的语义,从历史记忆中召回相关内容,而不是简单地取最近几条。
- 高效检索:在海量历史数据中快速找到最相关的内容。
那为什么传统的关系型数据库(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
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端的完整实现。
几点核心结论:
- 短期记忆用Redis,长期记忆用向量数据库。前者解决单次会话内的连贯性,后者解决跨会话的语义检索需求。
- 选型根据数据规模走渐进式路线:原型验证用轻量方案,大规模生产再考虑分布式部署。
- 生产环境注意Embedding模型选择和异步写入,这两个问题直接影响系统的召回效果和吞吐能力。
- 不想自建就考虑云方案,阿里云百炼的记忆库功能是目前开箱即用的不错选择。
最后,如果觉得这篇文章对你有帮助,欢迎点赞、收藏、评论。下期会写《AI Agent工具调用的Java实现:让LLM自己决定调用哪个API》,敬请关注。
更多推荐




所有评论(0)