作为多年供应链 / WMS 方向 Java 后端开发,结合我过往仓储业务背景,花费两天独立搭建一套轻量化私有化 RAG 问答系统:SpringBoot 3.5.16+ SpringAI1.0 + Ollama 本地大模型 + MySQL9.7 原生向量,单库同时持久化业务文档原文与 Embedding 向量,无需第三方向量中间件,适配企业内网离线场景,最终实现仓储规则自然语言问答,下面完整记录全流程实现、踩坑与架构 选型思考。
一、项目整体架构
1. 架构流程图(自行配图)
前端 SSE 对话页面 → SpringBoot 后端(SpringAI 核心层) → Ollama 本地大模型 (对话大模型 + Embedding 向量化模型) → MySQL9.7(存储业务文档、文本、向量)
2. 各层职责拆解
前端层:极简对话页面,基于 SSE 长连接实现打字机流式输出,解决一次性加载长回答卡顿问题;
后端服务层
SpringAI 封装 Ollama 调用,统一管理对话请求、文本切片、Embedding 向量生成;
RAG 核心逻辑:用户提问→问题向量化→MySQL 向量相似度检索匹配知识库文档→拼接上下文 Prompt 送入大模型→流式返回答案;
大模型层:Ollama 本地离线部署,全程不调用任何付费云端 API,私有化无网络依赖;
存储层:MySQL9.7 原生VECTOR字段存储向量,TEXT 字段存原始业务文档,结构化字段记录文档分类、创建时间,单库保证原文与向量事务一致性。
3. 业务场景定位
面向仓储供应链内部员工,知识库录入仓库管理制度、差旅报销标准、公司基础信息、售后流程等文档;员工通过自然语言提问,自动检索对应业务规则给出回答,替代翻阅零散 Word 文档,提升内部咨询效率。
二、核心技术选型与选型权衡(拔高核心,面试重点)
1. 向量存储:为什么选用 MySQL9.7,放弃 Milvus/Chroma?
优势
零额外运维:现有业务均使用 MySQL,无需部署、维护独立向量服务,降低小团队运维成本;
数据强一致性:业务文档、文本、向量存在同一张数据表,新增 / 修改 / 删除可通过 MySQL 事务原子操作,彻底规避「向量库与业务库数据不同步」的经典 RAG 痛点;
学习成本低:开发无需额外掌握向量库专属 API,全部使用标准 SQL 完成向量检索;
适用边界(客观说明短板,体现架构思考) MySQL 仅支持精确 KNN 全表距离计算,无 HNSW、IVF 等 ANN 近似向量索引,仅适合10 万条以内文档的内部后台系统;若面向 C 端、百万级海量向量高并发检索,仍需 Milvus、PGVector 等专业向量引擎。
对比 PGVector:团队技术栈统一 MySQL,无需额外搭建 PostgreSQL 环境,优先选用 MySQL 原生向量能力。
2. AI 框架:SpringAI 2.0
Spring 官方 AI 集成框架,统一封装各大模型厂商 API,本次对接本地 Ollama;踩坑重点:2.0 版本发生破坏性 API 重构,大量旧类弃用、包路径变更,网上多数教程为旧版本,存在大量兼容问题。
3. 本地大模型:Ollama
离线私有化部署,本地完成对话、文本 Embedding 向量化,无 API 调用费用,适配企业内网、涉密场景,无需申请任何模型 Key。
4. 流式输出:SSE 服务端推送
区别于普通同步接口一次性返回完整文本,采用 SSE 长连接分段推送大模型输出内容,前端实现打字机实时展示效果,提升交互体验。
三、核心功能实现代码
3.1 MySQL9.7 向量数据表结构(附完整 SQL,自行截图)
支持存储文档原文、分类、向量、创建时间,VECTOR (768) 适配主流 Embedding 模型 768 维向量:
CREATE TABLE IF NOT EXISTS knowledge_source (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
category VARCHAR(64) COMMENT '数据分类,如 wms / finance / company',
content TEXT NOT NULL COMMENT '原始知识文本',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE IF NOT EXISTS vector_store (
id VARCHAR(64) PRIMARY KEY,
content TEXT,
metadata JSON,
embedding TEXT -- 向量以 JSON 数组文本存储,如 [0.12,-0.34,...]
);
INSERT INTO knowledge_source (category, content) VALUES
('wms', '商品库存低于安全库存时,必须在24小时内提交补货申请'),
('wms', '入库货物外包装破损、水渍,需生成质检异常单,禁止上架'),
('wms', '拣货作业优先匹配就近库位,同一订单多商品合并拣货,减少动线'),
('wms', '仓库A01存放包装耗材,B02存放大件货物,不可混放'),
('finance', '员工差旅费住宿单日上限300元,超出部分财务不予报销'),
('finance', '采购增值税发票必须匹配对应入库单,票货不一致直接驳回审核'),
('finance', '仓储耗材采购单次金额超过5000元,需要部门经理审批'),
('aftersale', '客户签收7天内商品无损坏,支持无理由退货'),
('aftersale', '缺货订单需当日联系客户,选择补发或全额退款二选一'),
('company', '公司全称:北京智联仓储科技有限公司,成立于2020年,总部位于北京海淀区'),
('company', '公司目前员工总数350人,其中仓储运营部120人、技术研发部80人...'),
('company', 'CEO张伟、CTO李明、COO王芳,电话:010-88886666...'),
('company', '公司在上海、广州、成都设有分公司...');
3.2 Maven 关键依赖(踩坑点:SpringAI 依赖下载失败解决方案)
SpringAI 官方仓库国内镜像同步延迟高,提供阿里云 镜像加速配置,解决依赖拉取超时、缺失问题,目前因为springAi2.0.0刚上市不久,aliyun服务器目前针对这个版本的jar包不全,所以暂时用旧版稳定版
3.3 SpringAI 整合 Ollama 配置
application.yml Ollama 模型地址、超时配置; 完整 Config 配置类:OllamaChatModel、OllamaEmbeddingModel Bean 注入。
3.4 RAG 核心业务逻辑
文档入库流程:原始文本分段切片 → 调用 Embedding 模型生成向量 → SQL 动态插入 MySQL,文本与向量同步落库;
问答检索流程:用户提问 → 问题向量化 → MySQL 向量相似度召回匹配文档 → 组装上下文 Prompt 传入对话模型 → SSE 流式返回结果。
package com.my.springai.controller;
import com.my.springai.dto.AiQueryDTO;
import com.my.springai.service.RagService;
import com.my.springai.vectorstore.MysqlVectorStore;
import lombok.RequiredArgsConstructor;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;
import java.util.LinkedHashMap;
import java.util.Map;
@RestController
@RequestMapping("/ollama/rag")
@RequiredArgsConstructor
public class RagController {
private final RagService ragService;
private final MysqlVectorStore mysqlVectorStore;
/** 加载本地业务规则文档,分片向量化后存入 MySQL */
@GetMapping("/load")
public String loadDoc() {
return ragService.loadDoc();
}
/** 同步问答 */
@PostMapping("/chat")
public String ragChat(@RequestBody AiQueryDTO dto) {
return ragService.chat(dto.getQuestion());
}
/** SSE 流式问答,前端 sse-test.html 调用 */
@GetMapping(value = "/stream/chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String question) {
return ragService.streamChat(question);
}
/** 手动上传单据文本,分片入库 */
@PostMapping("/upload/doc")
public String uploadDoc(@RequestParam String docText) {
return ragService.uploadDoc(docText);
}
/** 从 knowledge_source 表重新同步数据到向量库 */
@GetMapping("/sync")
public String syncFromDb() {
return ragService.syncFromDb();
}
/** 诊断接口:查看向量库状态 */
@GetMapping("/status")
public Map<String, Object> status() {
Map<String, Object> result = new LinkedHashMap<>();
result.put("向量库文档总数", mysqlVectorStore.count());
result.put("提示", "如果总数为 0,请先调用 GET /ollama/rag/sync 或重启应用");
return result;
}
}
3.5 SSE 流式对话 Controller
标准 SSE 接口实现,分段推送大模型输出,前端持续监听打印回答内容,附带跨域、编码处理逻辑。
最终前端展示的效果图如下:

现有短板
MySQL 无 ANN 向量索引,十万条以上文档向量检索速度大幅下降;
文本切片规则简单,未做递归分割、重叠分块优化,长文档召回精度一般;
无向量缓存,重复提问会重复计算向量,存在性能浪费;
未实现文档增量更新、向量批量刷新能力。
后续迭代优化方向
海量数据场景:拆分存储架构,MySQL 存业务原文,引入 Milvus 做高性能向量检索;
检索优化:增加 Redis 缓存高频问题向量结果,提升重复问答响应速度;
文本处理:完善递归文本分割、关键词增强检索,提升 RAG 召回准确率;
功能扩展:接入文档上传解析(PDF/Word 自动切片入库)、多轮对话记忆、角色权限控制。
七、总结
对于中小企业内部低并发知识库场景,MySQL9.7 原生向量完全可以替代独立向量数据库,大幅简化系统架构,降低运维成本;
SpringAI 大幅降低 Java 后端接入大模型的开发门槛,但版本迭代快,2.0 存在大量不兼容改动,开发时需要对照官方最新文档;
私有化 Ollama+MySQL 的 RAG 方案,适合内网、无外网环境的企业业务 AI 落地,是传统后端向 AI 业务拓展低成本实践路径;
本次实战完整覆盖「文档存储→向量化→向量检索→大模型问答→流式前端交互」整条 RAG 链路,弥补传统 Java 后端缺少 AI 落地实战经验的短板,相关技术、选型思路、踩坑记录均可用于面试沟通。
完整项目开源地址:https://gitee.com/danruoshui/springai-ollama-rag.git; https://github.com/316959108/springai-ollama-rag.git
配套资料:MySQL9.7 向量官方文档、SpringAI2.0 官方参考文档;
欢迎交流后端 AI 落地相关问题。
夜雨聆风