本文手把手教你在本地部署RAG系统:
- 用 Spring AI 整合 Ollama(运行DeepSeek中文模型)
- ChromaDB 存储本地文档(PDF/TXT)向量
- Java程序实现:文档解析 → 语义检索 → 增强生成
最终效果:模型回答更准确,减少幻觉,全程离线运行!
检索增强生成 (RAG)
RAG(Retrieval-Augmented Generation,检索增强生成) 是一种将信息检索与大语言模型(LLM)生成能力相结合的技术,旨在提升模型回答的准确性、时效性和可靠性,尤其擅长处理需要专业知识或实时数据的任务。
好处:
- 减少“幻觉”:强制模型基于提供的事实生成,避免编造不存在的信息。
- 突破训练数据限制:回答依赖最新或特定领域资料(如2025年政策、企业内部文档)。
- 提升可信度:答案可追溯来源(如引用文档页码),便于验证。
- 降低微调成本:无需重新训练模型,通过检索动态扩展知识。
应用场景:
- 企业知识库问答:基于内部文档(产品手册/合同)回答专业问题。
- 学术研究助手:根据论文库生成文献综述。
- 客服机器人:用最新产品信息回答用户咨询。
- 代码助手:结合项目文档解释代码逻辑。
实现的核心原理:
- 检索(Retrieve)
当用户提问时,系统先从外部知识库(如文档、数据库)中快速查找与问题相关的信息片段。
例如:从公司内部技术手册中检索“如何配置Spring AI连接Ollama”。
- 增强(Augment)
将检索到的相关文本片段(如段落、表格)插入到给LLM的提示词(Prompt)中,作为生成答案的参考依据。
例如:原本问题是“如何在Spring AI中调用Ollama的DeepSeek模型?”,那么接下来会把检索到的内容 + 问题发送给大模型。
- 生成(Generate)
LLM 基于增强后的提示词生成最终回答,确保答案紧扣提供的参考资料,而非仅依赖模型自身的训练数据。
本博客代码基于 上一篇博客 零基础搭建Spring AI本地开发环境指南-CSDN博客 中的代码继续编写,不知道怎么搭建Spring AI + ollama 的同学可以参照下
接下来我们来逐步完成一个基于RAG的问答接口,第一步我们需要一个存放文档向量的向量数据库,这里选择使用Chroma
ChromaDB的安装和使用
Chroma 是一个开源的向量数据库,专为 AI 应用设计,特别是用于存储和检索嵌入向量
安装,注意需要提前安装python环境,打开命令行执行命令
pip install chromadb
启动,这里Spring AI支持的是基于服务的处理,暂时没找到基于本地存储的ChromaDB处理,安装完成后打开
chroma run --path "本地存储路径" --host 0.0.0.0 --port 8000
启动后效果
更多详细的内容可以参照这个博客
Embedding模型
安装好数据库后,接下来需要安装Embedding模型,用于把文档转换为可识别的内容, Embedding(嵌入) 是将离散的符号(如单词、Token)映射为连续向量空间中的稠密向量的技术。其核心目标是让机器理解语言语义。
简单点讲,就是把文字转换为数字格式,让计算机更好的理解文字
例子: 张三 -> [0.1,1,3] ; 喜欢 -> [0.1,0.8]
这里的张三就是文档中的内容,[0.1,1,3] 就是转换后的向量,通过Embedding的转换让AI理解普通的话语,相当于现实世界和AI之间的桥梁
embedding有多种不同的方式,Spring AI也允许自定义处理,这里我们使用ollama中提供的模型Granite Embedding,
Granite Embedding,IBM 推出,支持 100+ 语言,Apache 2.0 许可,可商用免授权费,当然也可以去官网换其他的 Embedding models · Ollama Search
和大模型的处理差不多,打开命令行运行命令即可,和聊天模型不同的是下载完就好,不会打开聊天的处理
ollama pull granite-embedding
运行前需要安装 ollama ,如果不知道怎么安装的可以参照上一篇博客 零基础搭建Spring AI本地开发环境指南-CSDN博客 , 后面的代码也是基于上一篇博客中的项目为基础做的
安装完成后可以执行 ollama list 命令查看是否安装完成
java 下操作 ChromaDB
代码基于零基础搭建Spring AI本地开发环境指南-CSDN博客 继续编写
1、在Springboot java项目的pom.xml添加RAG相关依赖
<!--RAG-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chroma-store</artifactId>
</dependency>
2、 demo 包下创建config包,在包下创建ChromaConfig类,在这类里面主要用于配置ChromaDB,chromaUrl配置ChromaDB服务的启动地址
@Configuration
public class ChromaConfig {
@Bean
public RestClient.Builder builder() {
return RestClient.builder().requestFactory(new SimpleClientHttpRequestFactory());
}
@Bean
public ChromaApi chromaApi(RestClient.Builder restClientBuilder) {
String chromaUrl = "http://localhost:8000";
ChromaApi chromaApi = new ChromaApi(chromaUrl, restClientBuilder, null);
return chromaApi;
}
@Bean
public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi) {
return ChromaVectorStore.builder(chromaApi, embeddingModel)
.collectionName("TestCollection")
.initializeSchema(true)
.build();
}
}
3、controller类中增加操作接口,主要的内容使用vectorStore API操作chromaDB数据库,添加文档使用add函数,查询文档使用query函数,这里只是展示下常用API,不是非要创建接口,也可以使用其他方式。
@Autowired
VectorStore vectorStore;
@GetMapping("/ai/vector/add")
public String vectorAdd() {
/**
* Document 文档类型
* text 文档内容
* metadata 元数据,用于增强检索能力,标注文档额外数据,例如来源,时间等
*/
List<Document> documents = List.of(
new Document("张三是一个java开发,性别男,爱好女", Map.of("meta1", "meta1")));
vectorStore.add(documents);
return "success";
}
@GetMapping("/ai/vector/query")
public List<Document> vectorQuery(@RequestParam(value = "message") String message) {
// similaritySearch 相似性查询
List<Document> results = vectorStore.similaritySearch(SearchRequest.builder()
.query(message)
.topK(5)
.build());
return results;
}
这种方式适合添加数据库中存在的数据,你还可以使用读取文档的一些API来读取文档写入,一样的道理,后面也会描述。
4、添加完文档后,接下来开始访问,添加访问接口,更方便展示效果
@GetMapping("/ai/generateByRAG")
public Map<String,String> generateByRAG(@RequestParam(value = "message") String message) {
String result = ChatClient.builder(chatModel)
.build().prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore))
.user(message)
.call().content();
return Map.of("generation", result);
}
添加完成后,使用Apipost访问generateByRAG接口,message参数数据“张三是谁”,发送后访问结果如下
{
"generation": "<think>\n好的,我现在需要帮助用户回答关于“张三是谁”的问题。
根据提供的上下文信息,张三是一位Java开发人员,性别是男性,并且喜欢女性。\n\n首先,
我要仔细阅读用户的问题和提供的背景信息。用户没有提到任何其他限制或上下
文,所以可以直接利用现有的信息来回答。\n\n接下来,我会考虑如何组织答案的结构。
因为这是一个简单的问答问题,我只需要明确地指出张三的身份、性别和他的兴趣爱好即可。
\n\n然后,我要确保我的回答准确无误,完全基于提供的上下文内容,而不添加任何假设或推测。
如果有不确定的地方,我应该礼貌地询问用户是否有更多信息。\n\n最后,我会将信息简洁明
了地呈现出来,让用户能够快速理解并得到他们需要的答案。\n</think>\n\n根据提供的背景信息,
张三是一位Java开发人员,性别为男性,并且喜欢女性。"
}
在原本情况下大模型并不知道张三是谁,但是基于RAG访问后,可以明显看出生成的结果是按照我们传入的文档数据生成。
基于文档生成
除了直接添加后还可以让程序读取文档内容,然后写入,这里使用的是tika,当然也可以用其他的,这个无所谓
1、添加依赖
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-tika-document-reader</artifactId>
</dependency>
2、创建文档 “测试文档.docx”,这里注意文档要放在resource文件夹下,文档中添加内容
张三是个开发人员,喜欢玩游戏。
技能有java,python
平时最喜欢的游戏是星露谷,七日杀,永劫无间
点个关注呗
3、添加完成后,开始写代码,代码逻辑非常简单,创建tikaReader对象,然后调用read函数,最后循环添加到vectorStore
@GetMapping("/ai/vector/addDocument")
public String addDocument() {
String path = "测试文档.docx";
String label = "document";
// 读取文件
TikaDocumentReader tikaReader = new TikaDocumentReader(path);
List<Document> docbatch = tikaReader.read();
// 文件发送给适量存储
docbatch = TokenTextSplitter.builder().withChunkSize(512).withMaxNumChunks(100).build().apply(docbatch);
System.out.println("添加的文档大小:" + docbatch.size());
docbatch.forEach(doc -> {
System.out.println("添加的文档内容:"+doc.getText());
doc.getMetadata().put("label",label);
vectorStore.add(List.of(doc));
});
return "success";
}
4、还是原来的generateByRAG接口, 使用Apipost访问,message的值是“请介绍下张三”
生成结果如下,从结果中可以看出内容是基于“测试文档.docx”生成的
{
"generation": "<think>\n好的,我现在需要分析用户的请求。用户要求我介绍张三,
并提供了上下文信息。首先,看看提供的上下文内容:张三是个开发人员,喜欢玩游戏,
技能有Java和Python,最喜欢的游戏是星露谷、七日杀和永劫无间。\n\n然后,检查是否
有其他背景信息,比如性别或特定爱好。用户提到李浩是一个Java开发者,男性,喜欢女性,
但这是另一个用户的信息,与张三无关。\n\n接下来,分析用户的请求是否在提供的上下文
中能找到答案。张三的个人介绍包括开发技能和游戏偏好,这些都是明确给出的,所以可以回答。
此外,用户提到关注的数量,这可能是在其他平台上发布内容的方式,并不影响张三的介绍。
\n\n最后,确保回复简洁明了,不添加额外信息,只基于提供的上下文。因此,我应该直接列
出张三的基本情况,包括开发技能和最喜欢的游戏,同时避免无关的信息。\n</think>\n\n
张三是位开发人员,擅长Java和Python编程,并喜欢玩游戏。他的最爱游戏包括星露谷、
七日杀和永劫无间。"
}
本次实战清晰地印证了 Spring AI 作为 Java 开发生态接入 AI 能力的强大桥梁作用。通过整合 Ollama(本地模型运行)、DeepSeek(强大语义理解)和 ChromaDB(高效向量检索),我们构建了一个完全本地化的 RAG 文档问答系统 。
Spring AI 不仅简化了集成,更开启了 Java 应用智能化的新篇章,除了RAG功能外,还有声明式 Prompt 工程一系列功能,在构建企业知识助手、智能文档分析工具,还是个人研究方面提供一系列便利。