【第6篇】玩透RAG之RAG + Elasticsearch
📋 概述
开源地址:spring-ai-alibaba-rag-example
这个项目展示了如何使用 Spring AI Alibaba 结合 Elasticsearch 实现 RAG(检索增强生成) 功能。简单来说,就是让 AI 能"查阅资料"再回答你的问题,而不是凭空瞎编。
Elasticsearch可以参考专栏Elasticsearch从零开始
核心流程一览
🛠️ 环境准备
1. 必要软件清单
| 软件 | 版本要求 | 说明 |
|---|---|---|
| JDK | 17+ | 必须!Spring Boot 3.x 强制要求 |
| Elasticsearch | 9.3.x | 向量数据库,存储和检索向量 |
| Maven | 3.6+ | 项目构建工具 |
| Docker | 20.10+ | 推荐用 Docker 运行 ES(可选但方便) |
2. 获取 DashScope API Key 🔑
DashScope 是阿里云提供的大模型服务平台,我们需要它的 API Key 来调用 Embedding 模型和对话模型。
获取步骤: 参考前文这里不再赘述。
🚀 部署实操
步骤一:启动 Elasticsearch
推荐方式:Docker 一键启动 🐳
# 启动单节点 Elasticsearch(生产环境请用集群)
docker run -d \
--name elasticsearch \
-p 9200:9200 \
-p 9300:9300 \
-e "discovery.type=single-node" \
-e "xpack.security.enabled=false" \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
docker.elastic.co/elasticsearch/elasticsearch:9.3.1
参数说明:
discovery.type=single-node:单节点模式,适合开发学习xpack.security.enabled=false:⚠️ 仅学习时关闭,生产环境务必启用安全认证ES_JAVA_OPTS:限制内存 512MB,防止吃掉太多系统资源
验证启动成功:
curl http://localhost:9200
返回类似以下内容说明成功:
{
"name": "elasticsearch",
"cluster_name": "docker-cluster",
"version": {
"number": "9.3.1",
"build_flavor": "default"
},
"tagline": "You Know, for Search"
}
步骤二:配置项目
在项目根目录创建或修改 application.yml:
spring:
application:
name: rag-elasticsearch-example
# ✅ 正确配置(README里的有错误!)
ai:
dashscope:
api-key: ${AI_DASHSCOPE_API_KEY} # 你的API Key
# 向量存储配置
vectorstore:
elasticsearch:
index-name: spring-ai-alibaba-index
similarity: cosine # 余弦相似度
dimensions: 1536 # 向量维度(text-embedding-v3 默认1024,但可配置1536)
# Elasticsearch 连接配置
elasticsearch:
uris: http://localhost:9200
🔧 环境变量设置:
# Linux/Mac
export AI_DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx
# Windows CMD
set AI_DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxx
# Windows PowerShell
$env:AI_DASHSCOPE_API_KEY="sk-xxxxxxxxxxxxxxxx"
# 或者在 IDE 运行配置中设置(推荐开发时用)
⚠️ README 错误纠正:当前的git库里README 写的配置路径
ai.dashscope应该是错的!Spring AI 正确的路径是spring.ai.dashscope。同样elasticsearch.uri应该是elasticsearch.uris(复数形式)。
步骤三:编译运行
# 进入项目目录
cd /home/tht/examples-main/spring-ai-alibaba-rag-example/rag-elasticsearch-example
# 编译(-DskipTests 跳过测试,加快编译)
./mvnw clean package -DskipTests
# 运行
./mvnw spring-boot:run
或者直接运行主类 RagExampleApplication.java(IDE 中右键运行,适合调试)。
📡 API 接口使用
项目启动后,默认在 http://localhost:8080 提供服务:
1. Local RAG(本地模式)
| 接口 | 方法 | 功能 |
|---|---|---|
/ai/rag/importDocument |
GET | 导入 PDF 文档到 ES(首次必须执行) |
/ai/rag |
GET | 问答接口(需传 message 参数) |
实操命令:
# ① 导入文档(首次运行必须执行!否则知识库是空的)
curl -X GET http://127.0.0.1:8080/ai/rag/importDocument
# ② 开始问答
curl -G 'http://127.0.0.1:8080/ai/rag' \
--data-urlencode 'message=如何快速开始 Spring AI Alibaba'
2. Cloud RAG(云端模式)
| 接口 | 方法 | 功能 |
|---|---|---|
/ai/cloud/rag/importDocument |
GET | 导入到云向量库(阿里云向量检索服务) |
/ai/cloud/rag |
GET | 云端问答 |
云端模式适合生产环境,利用阿里云托管的向量检索服务,免去自己维护 ES 集群的麻烦。
🔬 核心原理详解
1. 文档导入流程(Indexing)
为什么要分块?
想象你有一本1000页的技术手册,如果直接把整本书变成一个向量,当用户问"如何配置 API Key"时,这个向量代表的是"整本书的语义",而不是"API Key 配置方法"的语义。这样检索精度会很差。
把文档切成 500-1000 Token 的小块,每块聚焦一个具体话题,检索时就能精准匹配到相关内容。
代码对应(LocalRagService.java):
// 1. 解析PDF - 提取文本内容和元数据(如页码、文件名)
DocumentReader reader = new PagePdfDocumentReader(springAiResource);
List<Document> documents = reader.get();
// 2. 分块 - 默认按token分,便于控制上下文长度
// 默认策略:每块约 800 Token,重叠 200 Token(防止语义断裂)
List<Document> splitDocuments = new TokenTextSplitter().apply(documents);
// 3. 向量化 + 存储(Spring AI 自动调用 DashScope API 生成向量)
// 每个 Document 会被转换成 1536 维的浮点数向量
vectorStore.add(splitDocuments);
2. 问答流程(Retrieval & Generation)
代码对应:
// 构建检索请求 - 配置检索策略
SearchRequest searchRequest = SearchRequest.builder()
.topK(4) // 返回最相似的4条文档块
.similarityThresholdAll() // 接受所有结果(不过滤)
.filterExpression(...) // 可添加过滤条件(如按文件名过滤)
.build();
// 使用 RetrievalRerankAdvisor 完成检索+重排序+生成一体化
// 这是 Spring AI Alibaba 的高级特性,简化了 RAG 流程
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new RetrievalRerankAdvisor(
vectorStore, // 向量存储(ES)
rerankModel, // 重排序模型(优化检索结果顺序)
searchRequest, // 检索配置
systemPrompt, // 系统提示词(定义 AI 角色和回答规则)
0.1 // 最小相关度阈值(低于此值的文档会被过滤)
))
.build();
// 执行对话 - 自动完成:问题向量化→检索→重排序→组装Prompt→调用LLM
return chatClient.prompt()
.user(message) // 用户问题
.stream() // 流式返回(打字机效果)
.chatResponse();
什么是重排序(Rerank)?
第一次向量检索(KNN)是"粗排",快速找出大致相关的文档。但向量相似度≠语义相关性,可能找到一些表面相似但实际无关的内容。
重排序模型是"精排",用更复杂的算法(通常是交叉编码器)重新计算问题与每个候选文档的真实相关性,把最相关的排在最前面,显著提升回答质量。
3. Elasticsearch 向量索引结构
项目启动时会自动创建如下索引结构:
{
"mappings": {
"properties": {
"embedding": {
"type": "dense_vector", // 密集向量类型
"dims": 1536, // 向量维度
"index": true, // 开启索引(支持检索)
"similarity": "cosine" // 余弦相似度算法
},
"content": {
"type": "text", // 原始文本内容
"analyzer": "ik_max_word" // IK分词器(中文友好)
},
"metadata": {
"properties": {
"source": { "type": "keyword" }, // 来源文件名
"page_number": { "type": "integer" } // 页码
}
}
}
}
}
⚠️ 常见问题与解决方案
Q1: 启动报 NoClassDefFoundError?
原因:Maven 依赖没下载完整,或者版本冲突
解决:
# 强制更新依赖
./mvnw clean install -U
# 如果还不行,删除本地仓库重新下载
rm -rf ~/.m2/repository/com/alibaba/cloud/ai
./mvnw clean install
Q2: 连接不上 Elasticsearch?
排查步骤:
# 1. 检查 ES 是否启动
curl http://localhost:9200
# 2. 检查防火墙(Linux)
sudo ufw allow 9200/tcp
# 或
sudo iptables -I INPUT -p tcp --dport 9200 -j ACCEPT
# 3. 查看 ES 日志
docker logs elasticsearch
# 4. 检查内存(ES 默认需要较大内存)
docker stats elasticsearch
常见原因:内存不足导致 ES 启动后自动退出,建议 Docker 分配至少 2GB 内存。
Q3: DashScope API 调用失败?
检查清单:
-
API Key 是否正确设置
echo $AI_DASHSCOPE_API_KEY # 检查环境变量 -
是否超过配额
- 登录 DashScope 控制台查看用量
-
网络能否访问阿里云
- 检查代理/VPN 设置
- 测试连通性:
curl https://dashscope.aliyuncs.com
-
查看详细错误日志
- 开启 DEBUG 日志:
logging.level.com.alibaba.cloud.ai: DEBUG
- 开启 DEBUG 日志:
Q4: 向量维度不匹配?
错误信息:dimension mismatch 或 Incorrect dimension for field 'embedding'
解决:确认 application.yml 中 dimensions 与使用的 Embedding 模型匹配:
| 模型 | 默认维度 | 可配置维度 |
|---|---|---|
text-embedding-v1 |
1536 | 固定 |
text-embedding-v2 |
1536 | 固定 |
text-embedding-v3 |
1024 | 512/768/1024 |
text-embedding-v4 |
1024 | 64-2048 多档 |
建议:v3/v4 模型默认 1024 维,如需 1536 维需显式配置:
spring:
ai:
dashscope:
embedding:
options:
model: text-embedding-v3
dimensions: 1536 # 显式指定
Q5: 检索结果不准确?
优化建议:
| 优化方向 | 具体方法 |
|---|---|
| 调整 topK | 增大到 10-20,召回更多候选 |
| 调整重排序阈值 | 从 0.1 调到 0.3-0.5,过滤低质量文档 |
| 优化 Prompt | 修改 system-qa.st 模板,明确回答规则 |
| 调整分块策略 | 改 TokenTextSplitter 参数:chunkSize 和 chunkOverlap |
| 混合检索 | 结合关键词检索(BM25)+ 向量检索 |
高级技巧 - 查询改写:
如果用户问"怎么开始",可能匹配不到"快速入门指南"。可以在检索前用 LLM 把问题改写成多个同义表达,提升召回率。
📊 技术栈版本对照
| 组件 | 本项目版本 | 说明 |
|---|---|---|
| Spring Boot | 3.x | 框架基础,必须 JDK 17+ |
| Spring AI | 1.0.0-M6+ | AI 抽象层 |
| Spring AI Alibaba | 1.0.0-M6.1+ | 阿里云 AI 集成 |
| **Elasticsearch ** | 9.3.1 | ES 官方客户端 |
| DashScope SDK | 最新版 | 阿里云模型服务 |
| JDK | 17+ | 最低要求,推荐 21 |
🏗️ 生产环境建议
Elasticsearch 配置
# 生产级 ES 配置
elasticsearch:
uris: https://your-es-cluster.com:9200 # HTTPS 必须
username: elastic # 启用认证
password: ${ES_PASSWORD}
ssl:
verification-mode: certificate # 证书验证
性能优化
| 方面 | 建议 |
|---|---|
| ES 集群 | 至少 3 节点,主节点与数据节点分离 |
| 内存 | ES 堆内存建议 4GB+,向量运算耗内存 |
| 分片 | 向量索引分片数 = 节点数 × 2 |
| 刷新频率 | 导入时调大 refresh_interval 到 30s,减少刷盘 |
| 批量导入 | 使用 Bulk API,每批 500-1000 条 |
监控与运维
- 监控:接入 Prometheus + Grafana,关注
search_latency和indexing_rate - 备份:定期快照备份向量索引
- 限流:DashScope API 有 QPS 限制(默认 10-50),生产环境加熔断降级
- 缓存:热点查询结果缓存到 Redis,减少重复调用
安全加固
- 网络安全:ES 不暴露公网,通过 VPC 或 IP 白名单访问
- API Key 管理:使用阿里云 KMS 或 Vault 托管,不要硬编码
- 输入过滤:对用户问题做敏感词检测,防止 Prompt 注入
- 输出审计:记录 LLM 输入输出,便于问题追溯
附:完整 Mermaid 架构图
希望本文能帮助你顺利搭建 RAG 应用!如有问题,欢迎留言讨论交流。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐



所有评论(0)