本地RAG实战:用Spring AI+Ollama+DeepSeek+ChromaDB增强文档问答

本文手把手教你在本地部署RAG系统

  • Spring AI 整合 Ollama(运行DeepSeek中文模型)
  • ChromaDB 存储本地文档(PDF/TXT)向量
  • Java程序实现:文档解析 → 语义检索 → 增强生成
    最终效果:模型回答更准确,减少幻觉,全程离线运行!

检索增强生成 (RAG)

RAG(Retrieval-Augmented Generation,检索增强生成) 是一种将信息检索大语言模型(LLM)生成能力相结合的技术,旨在提升模型回答的准确性、时效性和可靠性,尤其擅长处理需要专业知识或实时数据的任务。

好处:

  • 减少“幻觉”:强制模型基于提供的事实生成,避免编造不存在的信息。
  • 突破训练数据限制:回答依赖最新或特定领域资料(如2025年政策、企业内部文档)。
  • 提升可信度:答案可追溯来源(如引用文档页码),便于验证。
  • 降低微调成本:无需重新训练模型,通过检索动态扩展知识。

应用场景:

  • 企业知识库问答:基于内部文档(产品手册/合同)回答专业问题。
  • 学术研究助手:根据论文库生成文献综述。
  • 客服机器人:用最新产品信息回答用户咨询。
  • 代码助手:结合项目文档解释代码逻辑。

实现的核心原理:

  1. 检索(Retrieve)

当用户提问时,系统先从外部知识库(如文档、数据库)中快速查找与问题相关的信息片段。

例如:从公司内部技术手册中检索“如何配置Spring AI连接Ollama”。

  1. 增强(Augment)

将检索到的相关文本片段(如段落、表格)插入到给LLM的提示词(Prompt)中,作为生成答案的参考依据。

例如:原本问题是“如何在Spring AI中调用Ollama的DeepSeek模型?”,那么接下来会把检索到的内容 + 问题发送给大模型。

  1. 生成(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

启动后效果

更多详细的内容可以参照这个博客

向量数据库ChromaDB的使用-CSDN博客

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 {@Beanpublic RestClient.Builder builder() {return RestClient.builder().requestFactory(new SimpleClientHttpRequestFactory());}@Beanpublic ChromaApi chromaApi(RestClient.Builder restClientBuilder) {String chromaUrl = "http://localhost:8000";ChromaApi chromaApi = new ChromaApi(chromaUrl, restClientBuilder, null);return chromaApi;}@Beanpublic VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi) {return ChromaVectorStore.builder(chromaApi, embeddingModel).collectionName("TestCollection").initializeSchema(true).build();}}

3、controller类中增加操作接口,主要的内容使用vectorStore API操作chromaDB数据库,添加文档使用add函数,查询文档使用query函数,这里只是展示下常用API,不是非要创建接口,也可以使用其他方式。

    @AutowiredVectorStore 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 工程一系列功能,在构建企业知识助手、智能文档分析工具,还是个人研究方面提供一系列便利。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/87162.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/87162.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Python 数据分析:DataFrame,生成,用字典创建 DataFrame ,键值对数量不一样怎么办?

目录 1 示例代码2 欢迎纠错3 论文写作/Python 学习智能体------以下关于 Markdown 编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右S…

Java 并发编程的 CAS(Compare and Swap)是什么?

CAS&#xff08;Compare and Swap&#xff0c;比较并交换&#xff09; 并非 Java 语言特有的概念&#xff0c;而是现代计算机硬件提供的一条核心原子指令。在 Java 并发编程中&#xff0c;它扮演着“幕后英雄”的角色&#xff0c;是构建高性能、无锁并发工具&#xff08;如原子…

【UnityAssetBundle】AssetBundle打包

AssetBundle生成AB包资源文件方式&#xff1a; Unity编辑器开发&#xff0c;自定义打包工具&#xff1b;官方提供好的打包工具&#xff0c;Asset Bundle Browser 打包 选择一个资源&#xff0c;new一个压缩包名称或选择一个压缩包名称 点击Window->AssetBundle Browser&…

Hush Puppies大中华区鞋类业务移交品牌方继续经营

据悉&#xff0c;随着百丽集团运营的暇步士&#xff08;Hush Puppies&#xff09;大中华区鞋类授权的到期&#xff0c;暇步士&#xff08;Hush Puppies&#xff09;鞋类业务已开始运营权移交。其中线上渠道授权于2025年6月30日正式到期&#xff0c;线下渠道将于2025年12月31日前…

解释LLM怎么预测下一个词语的

解释LLM怎么预测下一个词语的 通过上文词的向量进行映射 在Transformer架构的大语言模型(如GPT系列、BERT等)中,词语会先被转化为词向量。在预测下一个词时,模型会基于之前所有词的向量表示(并非仅仅上一个词,但上一个词的向量是重要信息来源之一)进行计算。 以GPT-2…

DAY 49 CBAM注意力

目录 DAY 49 CBAM注意力1.通道注意力模块复习2.空间注意力模块3.CBAM的定义作业&#xff1a;尝试对今天的模型检查参数数目&#xff0c;并用tensorboard查看训练过程 DAY 49 CBAM注意力 1.通道注意力模块复习 2.空间注意力模块 3.CBAM的定义 import torch import torch.nn …

【网络】Linux 内核优化实战 - net.ipv4.conf.all.rp_filter

目录 net.ipv4.conf.all.rp_filter 参数详解一、参数基本概念二、参数取值及含义三、反向路径过滤的工作原理四、配置示例与注意事项五、与其他参数的关联六、总结 net.ipv4.conf.all.rp_filter 参数详解 一、参数基本概念 net.ipv4.conf.all.rp_filter 是 Linux 内核中用于控…

ElementUI el-select多选下拉框,回显数据后无法重新选择和修改

问题 ElementUI el-select多选下拉框&#xff0c;回显数据后无法重新选择和修改&#xff0c;点击选择和删除都没有反应&#xff0c;页面也没有报错 方案一 网上搜出来的基本上都是这个解决办法&#xff0c;但是我设置后没有生效&#xff0c;还是无法选择和修改 原因 下拉框数…

计算机视觉的新浪潮:扩散模型(Diffusion Models)技术剖析与应用前景

近年来&#xff0c;扩散模型&#xff08;Diffusion Models, DMs&#xff09;迅速崛起&#xff0c;成为计算机视觉领域最令人瞩目的生成模型之一。从生成高质量图像到风格迁移、图像修复&#xff0c;再到文本驱动图像生成&#xff08;如 DALLE 2、Stable Diffusion、Midjourney&…

「Java流程控制」跳转语句

今天来聊聊Java里的两个重要跳转语句——break和continue。它们就像马路上的交通信号灯,能够控制程序执行的流向。 break和continue break和continue在循环中的作用,特别像快递分拣中心的工作场景: break:就像发现一个破损包裹,直接停止当前分拣流程,把它扔进异常品处理…

R1-Searcher使用强化学习增强语言模型解决问题的搜索能力

R1-Searcher&#xff1a;Incentivizing the Search Capability in LLMs via Reinforcement Learning 2025.3 https://github.com/RUCAIBox/R1-Searcher 针对的问题&#xff1a; 现有大型推理模型在时间敏感或知识密集型问题上通常仅使用模型内部知识&#xff0c;导致回答不准…

C++中的虚函数与纯虚函数

文章目录 虚函数 (Virtual Function)纯虚函数 (Pure Virtual Function)主要区别实际应用示例 C中的虚函数和纯虚函数是实现多态性的重要机制。 虚函数 (Virtual Function) 虚函数是在基类中用virtual关键字声明的函数&#xff0c;它允许派生类重写(override)该函数的实现。当…

(LeetCode 每日一题) 3330. 找到初始输入字符串 I (字符串)

题目&#xff1a;3330. 找到初始输入字符串 I 思路&#xff1a;字符串&#xff0c;时间复杂度0(n)。 默认没有输错的情况ans1&#xff0c;而输错的情况&#xff0c;只会出现在连续相等字符串&#xff0c;假设这段字符串长度为ct&#xff0c;那么可能的情况为ct-1。累计这些和到…

Deep semi-supervised learning for medical image segmentation: A review

概述 医学图像分割的重要性&#xff1a;它是计算机辅助诊断&#xff08;CAD&#xff09;的关键部分&#xff0c;能帮助医生定位病变、评估治疗效果&#xff0c;减轻医生工作量。 深度学习技术的应用&#xff1a;U-Net等网络在医学图像分割中表现优异&#xff0c;近期大型视觉语…

[云上玩转Qwen3系列之四]PAI-LangStudio x AI搜索开放平台 x ElasticSearch: 构建AI Search RAG全栈应用

本文详细介绍了如何使用 PAI-LangStudio 和 Qwen3 构建基于AI搜索开放平台 x ElasticSearch 的 AI Search RAG 智能检索应用。该应用通过使用 AI 搜索开放平台、ElasticSearch 全文检索向量检索引擎的混合检索技术配合阿里云最新发布的 Qwen3 推理模型编排在一个 Agentic Workf…

前端请求浏览器提示net::ERR_UNSAFE_PORT的解决方案

起因 项目中后端给到了6666端口的服务地址, 随即前端项目访问中浏览器报错如下: 不安全端口在主流浏览器&#xff08;Chrome/Firefox/Edge/Safari&#xff09;中会被拦截&#xff0c;触发浏览器Network的status列显示 net::ERR_UNSAFE_PORT 错误, 以下是常见的不安全端口一览…

【Bluedroid】蓝牙设备管理器初始化全流程深度解析(BTA_dm_on_hw_on)

本文全面剖析Android蓝牙设备管理器在硬件启动时的初始化流程&#xff0c;涵盖控制块创建、服务发现启动、设备类配置、安全密钥加载、超时参数设置等核心环节。通过分析从底层硬件交互到上层服务注册的全链路调用&#xff0c;揭示蓝牙系统从硬件就绪到功能可用的完整启动机制&…

大语言模型:是逐字生成还是一次多词?

大语言模型(LLM)既可以按顺序逐个生成单词(token),也能实现一次生成多个 token 核心差异源于解码策略与模型架构设计 一、常规“逐个生成”模式(基础逻辑) 多数入门级演示或简单文本生成中,LLM 会默认按 “生成一个 token → 拼接回输入 → 再生成下一个” 的流程,…

通俗易懂的LangGraph图定义解析

LangGraph 是一个基于状态的工作流框架&#xff0c;它通过 节点&#xff08;Nodes&#xff09; 和 边&#xff08;Edges&#xff09; 的组合&#xff0c;构建出复杂的工作流逻辑。这种设计特别适合处理需要动态决策、循环、多步骤交互的场景&#xff08;比如对话系统、智能代理…

K8s Pod调度基础——2

目录 一、Deployment ‌一、Deployment 原理‌ ‌二、核心特性‌ ‌三、意义与场景‌ ‌四、示例与逐行解释‌ ‌五、总结‌ StatefulSet ‌一、StatefulSet 原理‌ ‌二、核心特性‌ ‌三、意义与场景‌ ‌四、示例与逐行解释‌ ‌五、总结‌ 彼此的区别 一、本质…