
在 AI 应用开发中,大模型常面临 “知识过期”“不懂私有数据” 的痛点,而检索增强生成(RAG)技术正是解决这一问题的核心方案。RAG 能够将大模型与私有知识库结合,让生成的回答更准确、更具针对性。本文将深入拆解 RAG 的技术原理、核心流程,重点讲解其与 Embedding 技术的协同逻辑,并结合智普 AI 实现本地知识库检索的完整实操,帮助大家掌握 Spring AI 中 RAG 模块的开发精髓。
Retrieval-Augmented Generation(RAG)即检索增强生成,是一种结合 “检索” 与 “生成” 的 AI 技术。它通过在大模型生成回答前,从外部知识库中检索与用户问题相关的信息,并将这些信息作为上下文传递给大模型,从而让大模型基于外部知识生成更准确、更可靠的回答。
RAG 的核心价值是为大模型 “外接动态知识库”,在不重训 / 微调的前提下,低成本解决知识过期、私有数据不可用、幻觉与高成本四大核心痛点,让回答更准、更新、更合规、更易维护。
RAG 技术的核心是 “精准检索”,而 Embedding 技术是实现这一目标的关键前提,二者是 “基础支撑” 与 “上层应用” 的紧密协同关系。
简单来说:Embedding 是 RAG 技术的 “语义翻译官”,它将非结构化的文本(知识库片段、用户问题)翻译成机器可理解、可计算的向量语言,没有 Embedding 提供的语义量化能力,RAG 就无法实现高效的 “检索增强”,只能依赖大模型的原生知识库。
RAG 技术的完整流程可分为三个核心阶段,每个阶段都与 Embedding 技术深度协同:
将检索到的相关文本片段与用户问题作为上下文传递给大模型,辅助生成准确回答。而这些片段之所以能精准匹配用户问题,本质是 Embedding 技术保证了 “问题语义” 与 “片段语义” 的一致性映射。
下图是 AI 如何处理文档并回答用户问题的完整流程图:

三、实操准备:环境配置(基于 Spring AI + 智普 AI)
需提前完成智普 AI 账号注册与 API Key 获取(参考上一篇 Embedding 文章的配置步骤),并创建 Spring Boot 项目。
1. 创建 Spring Boot 项目
项目结构如下:
Weiz-SpringAI-RAG/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/
│ │ │ └── example/
│ │ │ └── weizspringai/
│ │ │ ├── WeizSpringAiRAGApplication.java
│ │ │ ├── controller/
│ │ │ └── service/
│ │ └── resources/
│ │ ├── application.properties
│ └── test/
└── pom.xml<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>Weiz-SpringAI</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>Weiz-SpringAI-RAG</artifactId>
<name>Weiz-SpringAI-RAG</name>
<description>Weiz-SpringAI-RAG</description>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-zhipuai</artifactId>
</dependency>
<!-- spring-ai-client-chat 中包括 TokenTextSplitter、TextReader、Document 等工具 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-client-chat</artifactId>
<version>${spring-ai.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>3. application.properties 配置
# 应用名称
spring.application.name=Weiz-SpringAI-RAG
# 服务端口
server.port=8080
# 智普 AI 基础配置
spring.ai.zhipuai.api-key=你的智普 AI API Key
spring.ai.zhipuai.base-url=https://open.bigmodel.cn/api/paas
# Embedding 模型配置(用于文本向量化)
spring.ai.zhipuai.embedding.options.model=embedding-2
# Chat 模型配置(用于生成回答)
spring.ai.zhipuai.chat.options.model=GLM-4-Flash注意,需将 “你的智普 AI API Key” 替换为实际获取的密钥。
在 src/main/resources 目录下创建 户外旅行安全指南.txt,作为本地知识库,内容如下:
户外旅行安全指南
----
1.露营安全
- 选址:避开低洼积水区、陡坡和可能落石的区域,优先选择平坦硬质地面。
- 防火:远离干草、枯枝等易燃物,使用炉具时保持通风,睡前彻底熄灭明火。
- 防虫:携带驱蚊液、防虫网,避免在草丛密集处搭建帐篷,睡前检查帐篷内是否有虫类。
- 应急:随身携带手电筒、急救包和足够的饮用水,提前下载离线地图。
----
2.徒步安全
- 路线规划:提前查询路线难度、天气情况,选择与自身体能匹配的路线,告知亲友行程。
- 装备要求:必须穿防滑登山鞋、戴防晒帽,携带充足食物和水,配备登山杖和护膝。
- 环境应对:遇到暴雨立即寻找高地躲避,避免涉险过河;遭遇野生动物保持冷静,切勿投喂或驱赶。
- 体能管理:保持匀速前进,每小时休息 10-15 分钟,避免过度疲劳。
----
3.城市漫游安全
- 交通:骑行遵守交通规则,不逆行、不闯红灯,佩戴安全头盔;乘坐公共交通保管好个人财物。
- 选址:避开偏僻小巷和治安较差区域,优先选择人流密集的商业街区和景点。
- 应急:保存当地派出所、医院的联系方式,携带少量现金备用,手机保持电量充足。本案例将实现 RAG 的全流程:加载本地知识库并向量化、接收用户问题并检索相似文本、结合上下文生成回答。
可以复用上一篇文章中的SimilarityCalculator.java类,该类封装了余弦相似度、欧氏距离等多种主流算法,支持 RAG 检索阶段的相似度计算。
创建 com.example.weizspringai.service.RagService 类,整合索引、检索、生成三个RAG 核心逻辑:
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@Service
public class RagService {
// Embedding 模型(文本向量化)
private final EmbeddingModel embeddingModel;
// Chat 模型(生成回答)
private final ChatClient chatClient;
// 本地知识库文本片段
private final List<String> docChunks = new ArrayList<>();
// 文本片段对应的向量
private final List<float[]> docVectors = new ArrayList<>();
// 相似度算法(默认余弦相似度,可根据需求修改)
private final EmbeddingService.SimilarityAlgorithm similarityAlgorithm = EmbeddingService.SimilarityAlgorithm.COSINE;
// 构造方法注入依赖,初始化知识库(索引阶段)
public RagService(EmbeddingModel embeddingModel, ChatClient.Builder chatClientBuilder) throws IOException {
this.embeddingModel = embeddingModel;
this.chatClient = chatClientBuilder.build();
// 加载本地知识库文件并切分片段
loadAndSplitDocument();
}
/**
* 加载本地知识库文件,切分为文本片段(索引阶段第一步)
*/
private void loadAndSplitDocument() throws IOException {
// 读取 resources 目录下的知识库文件
Resource resource = new ClassPathResource("户外旅行安全指南.txt");
String content = new String(resource.getInputStream().readAllBytes(), StandardCharsets.UTF_8);
// 按 "----" 切分文本(根据文件格式自定义切分规则)
String[] chunks = content.split("----");
for (String chunk : chunks) {
String cleanChunk = chunk.strip();
if (!cleanChunk.isBlank()) {
docChunks.add(cleanChunk);
// 文本片段向量化并缓存(索引阶段第二步、第三步)
docVectors.add(embeddingModel.embed(cleanChunk));
}
}
}
/**
* 处理用户提问,返回 RAG 增强后的回答(检索+生成阶段)
* @param question 用户问题
* @return 大模型生成的回答
*/
public String answer(String question) {
// 1. 用户问题向量化(检索阶段第一步)
float[] questionVector = embeddingModel.embed(question);
// 2. 检索 Top2 最相似的文本片段(检索阶段第二步、第三步)
List<String> topRelevantChunks = retrieveTopRelevantChunks(questionVector, 2);
// 3. 构建上下文(生成阶段第一步)
String context = String.join("\n---\n", topRelevantChunks);
// 4. 构建提示词(生成阶段第二步)
String prompt = String.format(
"以下是户外旅行安全指南的知识:\n%s\n请基于上述知识,简洁明了地回答问题:%s",
context, question
);
// 5. 调用 Chat 模型生成回答(生成阶段第三步)
return chatClient.prompt()
.system("你是户外旅行安全助手,仅基于提供的上下文回答问题,不添加额外信息。")
.user(prompt)
.call()
.content();
}
/**
* 检索 TopK 最相似的文本片段(检索阶段核心逻辑)
* @param questionVector 用户问题向量
* @param topK 返回前 K 个相似片段
* @return TopK 相似文本片段
*/
private List<String> retrieveTopRelevantChunks(float[] questionVector, int topK) {
List<ChunkSimilarity> similarityList = new ArrayList<>();
// 计算问题向量与所有文本片段向量的相似度(使用指定算法)
for (int i = 0; i < docVectors.size(); i++) {
double sim = calculateSimilarity(questionVector, docVectors.get(i));
similarityList.add(new ChunkSimilarity(i, sim));
}
// 按相似度降序排序,取前 topK 个
similarityList.sort((a, b) -> Double.compare(b.similarity, a.similarity));
return similarityList.stream()
.limit(topK)
.map(item -> docChunks.get(item.index))
.toList();
}
/**
* 相似度计算(调用工具类,支持算法切换)
*/
private double calculateSimilarity(float[] a, float[] b) {
return switch (similarityAlgorithm) {
case COSINE -> SimilarityCalculator.cosineSimilarity(a, b);
case EUCLIDEAN -> SimilarityCalculator.euclideanSimilarity(a, b);
case PEARSON -> SimilarityCalculator.pearsonCorrelation(a, b);
case MANHATTAN -> SimilarityCalculator.manhattanSimilarity(a, b);
default -> SimilarityCalculator.cosineSimilarity(a, b); // 默认 fallback 到余弦相似度
};
}
/**
* 辅助类:存储文本片段索引与相似度
*/
private static class ChunkSimilarity {
int index;
double similarity;
ChunkSimilarity(int index, double similarity) {
this.index = index;
this.similarity = similarity;
}
}
}创建 com.example.weizspringai.controller.RagController 类,提供 RAG 检索接口:
import com.example.springaiembedding.service.RagService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping("/rag")
public class RagController {
@Autowired
private RagService ragService;
/**
* RAG 本地知识库检索接口
* @param question 用户问题
* @return 包含问题与回答的响应
*/
@GetMapping("/ask")
public Map<String, String> ask(@RequestParam("question") String question) {
String answer = ragService.answer(question);
return Map.of(
"question", question,
"answer", answer
);
}
}{
"question": "露营选址有什么安全要求?",
"answer": "露营选址需避开低洼积水区、陡坡和可能落石的区域,优先选择平坦硬质地面,同时要远离干草、枯枝等易燃物,避免在草丛密集处搭建帐篷。"
}再测试其他问题,如http://localhost:8080/rag/ask?question=徒步时遇到暴雨该怎么办?,响应:
{
"question": "徒步时遇到暴雨该怎么办?",
"answer": "徒步遇到暴雨时,应立即寻找高地躲避,避免涉险过河,同时注意远离陡坡和可能落石的区域,确保自身安全。"
}测试结果表明,大模型能够基于本地知识库文件的内容生成准确回答,验证了 RAG 技术的核心价值。
当前 RAG 实现存在一些可优化点,后续文章将深入讲解:
根据知识库类型(如短文本、专业文档、稀疏向量)自动匹配最优相似度算法,例如专 业文档优先使用皮尔逊相关系数,稀疏向量优先使用杰卡德相似度。
本文深入解析了 RAG 技术的核心原理、核心流程,重点厘清了其与 Embedding 技术的协同逻辑,并通过 Spring AI + 智普 AI 实现了 RAG 本地知识库检索的完整案例。RAG 技术通过 “检索外部知识 + 增强生成能力” 的模式,完美解决了大模型的知识局限问题,而 Embedding 技术提供的语义向量能力,是 RAG 实现精准检索的核心支撑。
核心结论如下:
后续文章将针对这些优化点展开详细讲解,帮助大家构建生产级别的 RAG 应用。如果本文对你有帮助,欢迎点赞、在看、转发,持续关注获取更多 Spring AI 进阶干货!