Spring Boot 集成 Ollama:Java后端本地大模型对话实战

发布时间:2026/9/24 18:20:24
Spring Boot 集成 Ollama:Java后端本地大模型对话实战
本地大模型最近两年成了Java后端绕不开的话题。数据不出内网、按需部署、成本可控这三点让 Ollama 成了本地跑大模型的首选而 Spring AI 则是 Spring Boot 生态里接入 AI 最顺手的官方组件。这篇文章我完整梳理一遍怎么装 Ollama、怎么拉模型、怎么用 Spring AI 把智能对话能力接进 Spring Boot 工程全程给可直接复现的配置和代码。这个方案适合谁如果你是在做 Java 后端想在项目里加一个不依赖云厂商的对话助手或者你们环境对数据外发有要求需要完全本地的推理服务又或者你就是想在开发机上跑个千问、Llama给内部工具做一个私人问答入口——这篇文章都适用。内容是我反复搭环境、调接口、踩坑之后沉淀下来的东西照着做基本一遍过不用再去翻一堆零散的官方文档。1. 方案选型为什么是 Ollama Spring AI1.1 Ollama 解决了本地部署的什么问题先聊 Ollama。它本质上是一个大模型运行时管理器把模型下载、启动、暴露 HTTP 接口这几件事全部封装好了。你不需要写 Transformer 推理脚本不需要折腾 Python 环境装好之后一条命令把模型拉下来、跑起来就是一个完整的推理服务。我在选本地推理方案的时候不是没考虑过 LM Studio。LM Studio 更偏个人桌面使用命令行和 Docker 环境不如 Ollama 干净接口风格也没有 Ollama 这么贴近 OpenAI 规范。Ollama 的胜出点有三个安装简单Windows、macOS、Linux 都有安装包Linux 上一条命令也能装模型管理方便ollama pull拉取、ollama ls查看、ollama rm删除全命令行操作接口兼容性好原生支持 OpenAI 风格的/v1/chat/completions接口这一点对接 Spring AI 非常关键。1.2 Spring AI 是接入层的合理抽象很多 Java 项目接大模型习惯自己用 RestTemplate 或者 WebClient 拼 HTTP 请求对着接口文档手撸 DTO、拼 JSON、处理流式响应。短平快接一个模型没问题一旦要切换模型、做多轮对话、接 RAG、做 Agent代码就会迅速膨胀每个模型都要维护一套客户端逻辑。Spring AI 是 Spring 官方推出的 AI 应用开发框架核心思路是把对大模型的调用抽象成统一接口。你面向ChatClient、ChatModel这些高层 API 编程底层接 OpenAI、Ollama、通义还是智谱切换时只需要改配置和依赖业务代码基本不动。我知道有人会提 LangChain4j功能也很全面但论 Spring 生态的贴合度、自动装配能力以及上下文管理Spring AI 更省心毕竟是官方出品。Spring AI 1.0 GA 版本发布之后API 趋于稳定依赖坐标也清晰了当前正是接入的好时机。1.3 整体链路与版本搭配整个调用链路非常短Spring Boot 应用 → Spring AIOllama Starter → Ollama 本地服务 → 本地大模型模型推理完全发生在本机或内网服务器外部网络只负责把模型文件下载到本地。这个链路带来的直接好处是对话数据不出内网适合企业内部工具、金融政务类系统不按 Token 计费CPU 也能跑部署成本可控模型文件提前备好之后离线环境也能运行。版本搭配上我实测下来这套最稳直接贴给你组件推荐版本说明JDK17 或 21Spring Boot 3.x 要求至少 1721 表现更好Spring Boot3.3.x 或 3.4.x当前主流稳定版本Spring AI1.0.0 GA 或更高必须和 Boot 版本匹配Ollama0.5.x 及以上新版本对 Function Calling 支持更完善注意Spring AI 1.0 GA 要求 Spring Boot 3.4 及以上如果你还在用 Spring Boot 3.2 或 3.3需要选用对应的 Spring AI 旧版本0.8.x 那套依赖坐标。动手前先把版本对应关系查清楚能省掉大量排错时间。2. 环境准备Ollama 安装、模型选择与离线导入2.1 三平台安装与联通性验证Ollama 的安装基本没有难度官网下载安装包按引导装完就行。我顺便把命令行的装法列出来服务器环境经常用得上。macOSbrew install ollamaLinuxcurl -fsSL https://ollama.com/install.sh | shWindows 直接下载安装包装完之后手动启动 Ollama 应用程序。装好后先确认服务状态ollama serve ollama --version如果ollama serve已经在后台运行直接访问http://127.0.0.1:11434能看到 Ollama 返回的信息说明服务起来了。这里有一个很容易忽略的点Ollama 默认只绑定127.0.0.1:11434。如果 Spring Boot 和 Ollama 在同一台机器没问题如果 Spring Boot 部署在另一台服务器上就必须在启动 Ollama 前设置环境变量OLLAMA_HOST0.0.0.0否则远端连不上而且这个坑比较恶心因为用本机 curl 测是通的换一台机器就不行。2.2 本地模型怎么选从硬件出发的选型这是所有人都会问的问题本地部署大模型用哪个模型最佳我的回答很实在——没有绝对最佳只有匹配你硬件的选型。我的判断标准是这么几条显存 8G 以下老老实实用 7B~8B 参数的量化模型推荐qwen2.5:7b或者llama3.1:8b显存 16G可以上 14B 参数的 Q4 量化版比如qwen2.5:14b32G 及以上再考虑 32B 或更大模型纯 CPU 跑且内存 16G 以下建议选qwen2.5:3b或gemma2:2b能用但首字延迟会偏高。中文场景我首推通义千问系列。原因很简单中文语料占比高指令跟随能力好输出质量在同尺寸下明显优于 Llama而且量化后的模型体积控制得不错Q4 量化的 7B 模型大概 4.7GB普通开发机跑得动。拉取模型的命令ollama pull qwen2.5:7b拉完用ollama list检查确认模型已经就位。模型参数规模量化后体积建议硬件qwen2.5:3b3B约 1.9GB任意开发机qwen2.5:7b7B约 4.7GB8G 显存或 16G 内存qwen2.5:14b14B约 9.0GB16G 显存llama3.1:8b8B约 4.9GB8G 显存或 16G 内存2.3 下载慢的稳妥解法本地导入模型ollama pull从官方模型库下载网络状况不好的时候几 GB 的模型能下好几个小时有时候下到一半断了还得重来。这个坑我估计每个人都踩过。我的建议是别在ollama pull上死磕改用国内模型托管平台下载模型文件再导入 Ollama整个过程完全可控。具体操作分三步第一步去魔搭社区ModelScope等平台找到同名模型的 GGUF 格式文件搜索关键字用“qwen2.5 7b gguf”就能找到下载到本地。第二步写一个 Modelfile指明本地文件路径FROM ./qwen2.5-7b-instruct-q4_k_m.gguf第三步用 Ollama 从本地创建模型ollama create qwen2.5-7b -f ./Modelfile创建完成后ollama list里会出现这个模型后续使用方式和pull下来的没有任何区别。注意如果目标环境完全离线建议在开发机上先把模型导入一次然后把整个模型目录拷贝到目标机器。模型默认存放在~/.ollama/models目标机器上设置环境变量OLLAMA_MODELS指向该目录即可省去重新下载的时间。3. Spring Boot 工程搭建与智能对话代码实现3.1 创建项目与引入依赖项目创建我直接用 Spring Initializr勾选 Spring Web 就够用。核心依赖是 Spring AI 的 Ollama Starter建议同时加上 Spring Boot Starter Web 和 Validation后面写对话接口会用到。需要提醒的是Spring AI 的依赖坐标在 1.0 GA 和之前的版本之间差别不小。我现在用的这套是最主流的先通过 BOM 管理版本dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version1.0.0-GA/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement然后在 dependencies 里加dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency如果是 Spring Boot 3.2 的老项目Spring AI 1.0 装不上需要回退到 0.8.1 那套依赖但那版 API 跟现在差别比较大。我的建议是直接升级 Boot 版本别在老版本上迁就。3.2 配置文件一个 application.yml 搞定依赖引入之后大部分接入工作都可以通过配置完成。application.yml里最核心的就是告诉 Spring AI 去连哪个 Ollama 服务、用哪个模型server: port: 8080 spring: application: name: local-chat-app ai: ollama: base-url: http://127.0.0.1:11434 chat: options: model: qwen2.5:7b temperature: 0.7 top-p: 0.9几个参数说明一下base-urlOllama 服务地址默认就是http://127.0.0.1:11434model默认模型名必须和ollama list里看到的完全一致temperature回答随机性0 表示尽量确定值越大越发散。客服问答建议 0.3~0.5闲聊创作可以 0.8 以上top-p核采样阈值配合 temperature 一起调整通常保持 0.9 左右。如果你想让所有请求都带一套默认系统提示词配置里也能预设但我更推荐放在代码里因为不同业务场景提示词差别很大写死配置后期不好维护。3.3 核心实现ChatClient 一行出答案Spring AI 1.0 之后最常用的是ChatClient它是高层封装的入口用法很像 WebClient 的链式调用。先写一个普通对话接口RestController RequestMapping(/api/chat) public class ChatController { private final ChatClient chatClient; public ChatController(ChatClient.Builder builder) { this.chatClient builder.build(); } PostMapping public String chat(RequestBody ChatRequest request) { return chatClient.prompt() .user(request.message()) .call() .content(); } }ChatClient由 Spring AI 自动装配生成直接构造器注入到 Controller 里就能用。ChatRequest是很简单的 DTO一个message字段就够了这里不展开。调用逻辑跟平时调 OpenAI 的思维方式完全不同你不需要关心 HTTP 报文、鉴权、模型参数这些细节只要描述“用户问什么”Spring AI 负责和 Ollama 通信并返回结果。3.4 流式输出让对话体验更像 ChatGPT如果回答内容比较长同步等待接口会非常难受。比如让模型写一段代码调用要卡几十秒才返回前端一直转圈体验很差。更实际的做法是流式输出用 SSE 实时展示生成过程。PostMapping(/stream) public FluxString chatStream(RequestBody ChatRequest request) { return chatClient.prompt() .user(request.message()) .stream() .content(); }FluxString返回后Spring MVC 会自动以text/event-stream格式把内容推给前端。前端用 EventSource 或 fetch 流式读取逐字渲染体验和 ChatGPT 官网基本一致。这个流式能力在本地模型场景下尤其值得优先实现。本地模型推理速度没有云端快首字延迟可能有两三秒如果还让用户等全部生成完才看到结果用户大概率会以为系统挂了。流式输出至少让用户感觉到“模型在干活”。3.5 多轮对话记忆别让模型当“金鱼”直接调大模型接口的初学者经常踩一个坑模型没有记忆。每次调用都是无状态的你告诉它“我叫张三”下一句问“我叫什么”它答不上来。解决办法是把历史消息一起发给模型。Spring AI 里可以用ChatMemory管理会话历史Configuration public class ChatConfig { Bean public ChatMemory chatMemory() { return new InMemoryChatMemory(); } Bean public ChatClient chatClient(ChatClient.Builder builder, ChatMemory chatMemory) { return builder .defaultSystem(你是一个智能助手回答要简洁、准确。) .defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory)) .build(); } }MessageChatMemoryAdvisor会自动把上下文塞进每次请求你需要做的只是在请求时带上会话 IDchatClient.prompt() .user(message) .advisors(a - a.param(ChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY, conversationId)) .call() .content();会话内存默认在 JVM 内存里单机部署没问题。多机部署或需要跨实例共享上下文时建议把ChatMemory换成 Redis 实现Spring AI 有现成的扩展点。这里有一个细节本地模型上下文窗口有限qwen2.5:7b是 32K 上下文但聊天记录塞太多既慢又费显存所以我通常把历史控制在最近 10 轮左右超出后丢最旧的消息。3.6 系统提示词与结构化输出对话类项目里系统提示词决定了模型的“人设”。可以在运行时代码里动态指定比写死在配置里灵活得多String response chatClient.prompt() .system(你是一名中文客服语气友好回答不超过50字。) .user(请介绍一下退款流程) .call() .content();另外一个很实用的能力是结构化输出。业务系统里经常需要模型返回 JSON而不是一坨自由文本比如解析用户意图public record Intent(String action, String target, String reason) {} Intent intent chatClient.prompt() .user(帮我把空调温度调到26度) .call() .entity(Intent.class);模型会按照 Java record 的字段名输出 JSON并由 Spring AI 自动反序列化成对象。这个能力在实际业务对接里非常有用省掉了自己写提示词让模型输出 JSON 再手写解析的一整套麻烦。4. 进阶实践超时控制、多模型切换与提示词模板4.1 让对话接口可配置超时与连接设置本地模型推理通常比云端 API 慢很多7B 模型在纯 CPU 机器上回答一个问题可能要几十秒。这种情况下HTTP 客户端默认超时时间完全不够用必须显式设置。Spring AI 的 Ollama 客户端底层使用自建 WebClient通过配置可以调整超时和连接设置spring: ai: ollama: base-url: http://127.0.0.1:11434 connect-timeout: 5s read-timeout: 120sconnect-timeout是建立连接的超时read-timeout是读取响应的超时。本地模型推理慢read-timeout给到 120 秒比较稳妥。我见过不少项目因为没设置这两个参数出现“答案还没生成完客户端先超时断开”的问题排查起来非常费劲。4.2 多模型切换一次接入按需调用实际项目里往往不止用一个大模型。比如线上环境用qwen2.5:14b保证回答质量开发环境用qwen2.5:3b节省资源。Spring AI 支持预配置多个本地模型实例Configuration public class OllamaConfig { Bean public OllamaChatModel qwenSmallChatModel() { return OllamaChatModel.builder() .ollamaApi(new OllamaApi(http://127.0.0.1:11434)) .defaultOptions(OllamaOptions.builder() .model(qwen2.5:3b) .build()) .build(); } Bean public OllamaChatModel qwenLargeChatModel() { return OllamaChatModel.builder() .ollamaApi(new OllamaApi(http://127.0.0.1:11434)) .defaultOptions(OllamaOptions.builder() .model(qwen2.5:14b) .build()) .build(); } }然后按业务场景注入不同的 Bean 名使用。需要说明的是如果只用一个模型默认自动配置就够完全不需要手写Bean只有多模型场景才需要手动定义。这种“按需切换模型”的能力是直接拼 HTTP 请求很难做到的也是用 Spring AI 这类抽象带来的核心价值。4.3 OpenAI 兼容接口的连带收益Ollama 本身暴露了 OpenAI 兼容的/v1/chat/completions接口理论上拿任何一套 OpenAI SDK 都能接入。但用 Spring AI 的好处是接口抽象统一如果后续从本地 Ollama 平滑切换到云上的 OpenAI、通义或智谱只需要调整依赖和配置Controller 层代码不用动。这一点我在一个内部知识问答项目里深有体会。最开始用 Ollama 跑后来业务方要求对比云端模型的效果我直接换了依赖坐标和配置文件业务代码一行没改就完成了切换。这就是抽象层的价值——你今天花在 Spring AI 上的学习成本后面会在无数次模型替换中赚回来。4.4 让回答更“懂业务”提示词模板实际项目里用户的提问通常需要包装成更结构化的提示词而不是直接丢给模型。Spring AI 的PromptTemplate就是干这个的public String answerWithContext(String question, String context) { PromptTemplate template new PromptTemplate( 请基于以下资料回答用户问题。 如果资料中没有相关信息请明确说“资料中未找到相关内容”。 资料 {context} 问题 {question} ); Message message template.createMessage(Map.of( context, context, question, question )); return chatClient.prompt(message).call().content(); }{context}可以是产品说明、工单知识库内容。这个模板式提示词是接入 RAG 之前的必经步骤先用起来后续接向量数据库时只需要把“资料”换成语义检索的结果业务层结构完全不用改。5. 常见问题与排查技巧实录这部分是我实际操作中踩过的真实坑每一条都对应一个具体的线上或开发环境问题。5.1 连接失败Connection refusedSpring Boot 启动后调用接口报Connection refused: localhost/127.0.0.1:11434八成是 Ollama 服务没启动。终端执行ollama serve启动即可。如果是服务器部署Ollama 和 Spring Boot 不在同一台机器需要确认两方面一是 Ollama 设置OLLAMA_HOST0.0.0.0允许外部访问二是 Spring AI 配置里的base-url要改成 Ollama 所在机器的实际 IP而不是127.0.0.1。这两点缺一个都会报连接失败。5.2 模型不存在model not found提示model xxx not found, try pulling it first说明配置里的模型名和实际ollama list结果对不上。常见原因有两个一是模型真的没下载二是模型名写错了比如qwen2.5:7b和qwen2.5:7b-instruct是两个不同的模型名。排查方式就是执行ollama list看完整名称再回去改配置。5.3 中文流式输出乱码或乱序流式输出时中文乱码大部分是 HTTP 响应编码问题。确认接口返回头是Content-Type: text/event-stream; charsetutf-8前端用response.text()按 UTF-8 解析不要硬编码其他编码。Spring 的FluxString默认按 UTF-8 处理我这边遇到乱码基本都是前端解析写错了导致的。还有一种情况是字序不对文字一段段地出来但顺序偶尔颠倒这通常是前端对 SSE 分帧处理不当把多个数据块拼接顺序搞错了。处理思路是严格按照 SSE 的data:帧格式解析每个事件单独处理不要跨帧拼接。5.4 推理性能慢和资源占用过高本地模型首字延迟高常见原因有三个模型太大硬件撑不住。7B Q4 模型在 Apple Silicon 上大概每秒 20~30 tokenCPU 机器更慢GPU 没被用上。NVIDIA 显卡需要 CUDA 版 OllamamacOS 上如果没有正确启用 Metal 加速默认 CPU 跑会很慢模型常驻内存。Ollama 默认模型加载后 5 分钟无请求自动释放但高并发情况下多个模型可能同时驻留内存瞬间被吃满。排查时先执行ollama ps看当前模型是否在内存中再用系统资源监控确认瓶颈是 CPU 还是内存最后决定是升级硬件还是换小模型。想要控制内存占用可以设置OLLAMA_KEEP_ALIVE0让模型回答完立即释放缺点是每次请求都要重新加载模型冷启动时间变长。5.5 并发场景下的请求排队问题本地模型一次通常只能跑一个推理任务并发请求会排队延迟飙升属于正常现象。生产环境能做的事情有三件接口层做限流控制并发数用异步化或消息队列削峰把对话任务排队处理多卡或多机部署通过负载均衡分散请求压力。我之前一个项目上线后高峰期接口超时率很高排查根因不是 Java 代码问题而是 Ollama 单实例的处理能力上限。后来加了限流和异步化服务质量才恢复正常。5.6 版本不兼容的诡异报错Spring Boot 和 Spring AI 版本不匹配时会出现一些看起来特别奇怪的异常比如NoSuchMethodError、ClassNotFoundException、Bean 创建失败等等。遇到这类问题先查版本号Spring AI 1.0.0 GA 需要 Spring Boot 3.4 及以上Spring AI 的里程碑版本M1、M2 这种不建议上生产所有 Spring AI 相关依赖统一走 BOM 管理不要多个版本混用。我踩过最大的坑是 Spring Boot 3.3 加 Spring AI 1.0.0-M3各种诡异报错排了两天最后把 Boot 升到 3.4 才彻底解决。版本问题一定要在最开始就确认好别抱着侥幸心理。6. 扩展从智能对话到知识库问答6.1 RAG 的接入思路对话能跑通之后最值得做的扩展就是接入向量数据库实现知识库问答。RAG检索增强生成的思路不复杂把文档切片用 Embedding 模型转成向量存入向量数据库用户提问时先把问题转成向量在库里做相似度检索找出最相关的文本片段把检索到的片段作为上下文拼进提示词模板交给对话模型生成回答。这里有一个好消息Embedding 模型同样可以用 Ollama 跑比如nomic-embed-text或bge-m3和对话模型共用一个服务不需要额外部署一套推理环境。6.2 向量数据库怎么选Spring AI 对向量数据库做了抽象Redis、Milvus、PgVector 都有对应的 Starter。我的建议是从 Redis 的向量检索开始试部署成本最低功能对内部工具来说已经够用后面规模大了再迁移到 Milvus 这类专业向量库。接入之后知识库问答的链路就是第 4.4 节那个提示词模板的升级版把{context}从“手动传入的文本”替换成“向量检索返回的相关文档片段”。其他逻辑完全复用这就是前面把提示词模板单独抽出来的价值所在。这套东西我陆陆续续调了一周多最深的体会是本地大模型的落地难点不在模型本身而在工程链路的长尾问题——版本兼容、超时处理、并发控制、上下文管理每一样都比“把接口跑通”更花心思。用 Spring AI 把这层抽象用好后面无论是换模型、加 RAG 还是做 Agent都能省下大量重复劳动。最后再分享一个小技巧开发阶段先不做任何业务包装直接在 Controller 里暴露原始请求参数用 Postman 或浏览器把同步和流式接口都调通确认模型侧没问题之后再往上叠加提示词模板、会话记忆、多模型切换这些能力。这样每一步出问题都能快速定位到是模型、配置还是代码的锅排错成本会低很多。