最近在把 AI 能力接进业务系统,试了一圈之后,我个人最推荐 Spring AI + Ollama 这套本地大模型方案。Spring AI 是 Spring 官方出的 AI 框架,把对大模型的调用抽象成一套统一的 Java API,上层怎么接、底层换什么都由框架帮你挡掉;Ollama 是一个极简的本地大模型运行器,一条命令就能把 Qwen、Llama、DeepSeek 这类开源模型拉下来跑在你自己的电脑上。把这两者搭配起来,你可以在不把数据送出去的前提下,用熟悉的 Spring Boot 方式完成聊天问答、文档分析、文本总结这类功能。这篇内容是系列第 2 篇,主要服务两类人:一是刚接触大模型的 Java 开发者,想尽快跑通一条可复现的完整链路;二是想在内网低成本搭一套推理能力的技术负责人。本篇聚焦最核心的入门链路:Ollama 安装、模型拉取、Spring AI 接通、消息返回,再到高频问题排查。
1. 项目整体思路:为什么是 Spring AI + Ollama
1.1 这套组合到底解决了什么问题
先说一个很现实的痛点。企业内部做 AI 功能,最常见的卡点不是模型能力,而是“数据能不能出去”。调云端大模型 API,虽然效果不错,但业务数据、用户隐私、日志内容全部要过一遍第三方服务,这在很多公司是要走审批甚至直接不批的。所以本地部署大模型成了刚需,特别是金融、政务、医疗、企业内部知识库这类场景。
Ollama 解决了“模型怎么在本地跑起来”的问题。它把模型下载、量化、推理、内存管理全部封装好,你不用懂 PyTorch、不用配 CUDA 环境,装完就能跑。对于 Java 后端团队来说,这个门槛直接降了一大截。
Spring AI 解决了“Java 项目怎么优雅调用模型”的问题。如果没有 Spring AI,你得自己拼 HTTP 请求,自己处理流式输出、JSON 解析、错误重试,而且每换一家模型厂商就要重写一遍接入层。Spring AI 从 Spring Cloud 那套设计思路里继承了很多优秀习惯,把模型调用抽象成了 ChatClient、EmbeddingModel 这种接口,你只需要注入一个 Bean,业务代码里写几行就能完成一次对话。
1.2 技术选型背后的关键考量
我选这套组合之前,其实也对比过其他方案。最原始的做法是直接写 RestTemplate 调 Ollama 的 /api/chat 接口,代码量不大,但问题在于:一旦后面要接 OpenAI、要接云端 API,或者要换嵌入式模型,整个调用层全部要改。Spring AI 的抽象层把这个风险隔离掉了,换模型提供商只改配置,不改业务代码,这是它最大的价值。
另一个对比对象是 LangChain4j。它也很优秀,但在 Spring 生态的契合度上,Spring AI 明显更胜一筹,因为它本身就是 Spring 官方出的,和 Spring Boot、Spring Data、Spring Security 的集成都是亲儿子待遇。比如你可以在 AI 调用链路里直接用 Spring 的事务管理、缓存、配置中心,这些 LangChain4j 虽然也能做,但没有那么顺滑。
选 Ollama 而不是直接用 llama.cpp 或者 vLLM,理由也很简单:Ollama 的模型管理能力太方便了。它内置了 Model 仓库机制,支持 Tag 版本管理,一条命令拉模型、一条命令删模型,还能用 Modelfile 自定义模型的 system prompt 和参数模板。对于开发环境和个人电脑来说,Ollama 就是最省心的运行器。
1.3 整体方案架构
整个链路其实很简单,核心就三层:
- 第一层是模型层,Ollama 负责在本地跑开源大模型,默认监听
http://localhost:11434。 - 第二层是接入层,Spring AI 的 Ollama Starter 封装了对 Ollama 的调用,通过
ChatClient暴露给上层。 - 第三层是业务层,你写正常的 Spring Service,注入
ChatClient,调用chat()方法拿结果。
所以整个项目搭起来只需要三步:装 Ollama、拉模型、在 Spring Boot 里加依赖写代码。下面两个章节我会把每一步都拆开讲,特别是那些不写进官方文档的坑。
2. 动手前的环境准备与 Ollama 安装
2.1 官网下载太慢?国内镜像源直接安排
Ollama 的官方下载地址在海外,很多同学反馈下载速度感人,几十 MB 的安装包能下半小时,甚至直接超时失败。这个问题我实测最有效的方案是走国内镜像源。
Windows 用户可以直接去国内镜像站下载安装包,比如很多开源镜像站会同步 Ollama 的 Windows 安装包和 macOS 安装包。把OllamaSetup.exe下载下来后,正常双击安装就行,这个安装过程本身没有难度,默认装到用户目录下。
Linux 用户处理方式稍微不一样。官方给的安装脚本curl -fsSL https://ollama.com/install.sh | sh在国内大概率拉不动,建议分两步走:先去国内镜像源下载ollama-linux-amd64.tgz之类的二进制包,手动解压安装。解压后把二进制放到/usr/local/bin,把库文件放到/usr/local/lib,然后创建 systemd service 或者直接用nohup ollama serve &后台启动。
注意:不要想着走系统代理或者改 hosts 去加速官方下载,这个方向又慢又容易出问题。直接换成国内源是最稳的,几分钟就能装完。
另外再补一个细节:macOS 用户如果有 Homebrew,可以考虑brew install ollama,这个命令本身也很快,但国内 Homebrew 的源需要提前配好,否则同样会遇到下载慢的问题。
2.2 安装路径与模型存储位置调整
Ollama 默认的模型存储路径很有迷惑性,Windows 上默认会放在用户的.ollama/models目录里,也就是 C 盘。很多人的 C 盘本来就不富裕,一个大模型动辄 4GB、8GB,几个模型下来 C 盘直接红了。
解决办法是提前设置环境变量OLLAMA_MODELS,把它指到你的 D 盘或者其他数据盘。比如我自己的配置是:
# Windows PowerShell 示例,永久生效 setx OLLAMA_MODELS "D:\ollama_models"设置完之后,务必重启 Ollama 进程,否则改动不生效。可以从托盘图标退出 Ollama,再重新打开,或者直接把 Ollama 服务重启一遍。Linux 上同样可以在 systemd service 文件里加Environment="OLLAMA_MODELS=/data/ollama"来实现。
另外还有一个环境变量值得一起配,就是OLLAMA_HOST。默认 Ollama 只监听127.0.0.1:11434,如果你要开给局域网里的其他机器访问,需要设置OLLAMA_HOST=0.0.0.0:11434。不过这个要谨慎,局域网内开放意味着其他机器也能调你的模型,如果是在公司内网,建议确认网络策略允许。
2.3 GPU 与 CPU 运行的基础配置
Ollama 的一大优势就是自动检测 GPU,有 NVIDIA 显卡的话,装上对应版本的驱动和 CUDA runtime 后,Ollama 会默认用 GPU 推理,速度提升非常明显。
但这里有个常见的坑:AMD 显卡和核显用户。如果你的机器只有 AMD 核显,比如很多办公电脑是 5600G、5700G 这种 APU,Ollama 的 GPU 支持可能不生效,甚至因为尝试用 GPU 导致报错。这时候最简单的做法是强制 CPU 运行,设置环境变量OLLAMA_NUM_GPU=0,让 Ollama 老老实实吃 CPU。
我自己在 5600G + 32G 内存的机器上实测过,跑 7B 量化的 Qwen 模型,CPU 推理大概每秒输出 3-5 个 token,虽然不快,但做开发测试、搭个内部问答机器人是够用的。如果你用的是AMD Ryzen AI 9 HX 370这种带 NPU 的新一代处理器,Ollama 目前对 NPU 的支持还不算完善,多数情况下还是走 CPU 或核显,不用太纠结跑分,先把链路跑通更重要。
顺便说一句,如果日志里出现了类似expected m1 and m2 to have the same dtype的报错,十有八九是 GPU 和 CPU 混跑时的数据类型不一致问题。先按上面说的设OLLAMA_NUM_GPU=0强制 CPU 跑,看看能否恢复正常。这个我在后面的排查章节会详细说。
2.4 首次拉取模型与命令行验证
安装完成并调整好环境变量后,先确认 Ollama 版本:
ollama --version能正常输出版本号就说明安装成功。接着拉取一个适合入门的模型,我强烈推荐 Qwen 系列,中文效果好,社区活跃,文档也全:
ollama run qwen2.5:7b这条命令会先把模型下载到本地,然后自动进入一个交互式聊天界面。你在命令行里敲一句“你好”,如果模型能正常回复,就说明环境完全通了。这一步很重要,一定要先确认命令行能通,再往下做 Spring AI 集成,否则你会分不清是模型问题还是 Java 代码问题。
3. 模型管理与 Ollama 基础操作
3.1 模型命名规则与常用命令
当你进入 Ollama 的世界后,会发现模型 Tag 有一套自己的命名规则。比如qwen2.5:7b和qwen2.5:7b-instruct-q4_K_M就是两个不同的 Tag。前者是官方推荐的默认版本,后者是显式指定了量化等级的中间版本。
实际上 Ollama 的模型名由模型名:标签组成,标签不写时默认是latest,但我不建议依赖 latest,因为模型升级可能会导致行为变化,让你的应用不可控。最好每次都显式指定 Tag。
几个最常用的命令:
# 查看本地已经拉下来的模型 ollama list # 在命令行直接对话 ollama run qwen2.5:7b # 删除不再需要的模型,释放磁盘空间 ollama rm qwen2.5:7b # 查看模型详细信息 ollama show qwen2.5:7bollama show除了能看参数规模、量化等级以外,还会显示模型的上下文长度、参数配置等信息,这对我们后面调 Spring AI 很有帮助。
3.2 模型参数解读与上下文长度
不少同学会问“如何查看本地大模型上下文长度”,其实就是用上面提到的ollama show。
看输出里的context_length字段,或者看model_info里的context_length。默认情况下,Ollama 为大多数模型设置的是 2048 或 4096 的上下文长度,但 Qwen2.5 系列原生的最大上下文是 32K 甚至 128K,也就是说默认配置远远没有发挥出模型的真实水平。
如果在 Spring AI 里需要长文档处理,就需要调大上下文。Ollama 这边可以通过 Modelfile 或者 API 参数来设置num_ctx。比如在 Spring AI 的配置里我们可以传一个options参数:
spring: ai: ollama: chat: options: num-ctx: 8192这个参数一定要记得根据自己的任务类型调整。短问答用默认 2048 没问题,但你要丢一篇几千字的文档进去,上下文不够的话,模型会直接“失忆”,忘记你之前给它的信息。
3.3 通过 HTTP API 验证模型可用性
在写 Java 代码之前,建议先用 curl 直接调一次 Ollama 的接口,确认 HTTP 层面没有问题。Ollama 默认提供两个接口,一个是对话接口,一个是原生生成接口。对话接口走的是 OpenAI 兼容风格,长期看是主流,我建议直接用这个:
curl http://localhost:11434/api/chat -d '{ "model": "qwen2.5:7b", "messages": [ { "role": "user", "content": "你好,用一句话介绍你自己" } ] }'正常的话会返回一个 JSON 数组,里面包含了最终的回答内容。这一步验证通过后,说明 Ollama 的 HTTP 服务没问题,接下来就可以放心进入 Spring AI 的部分了。
4. Spring AI 接入本地大模型实操
4.1 创建项目与引入依赖
Spring AI 目前对 Spring Boot 3.x 和 Java 17+ 支持最好,建议直接用 start.spring.io 或者 IDE 内置的初始化器创建一个标准 Spring Boot 项目。然后在pom.xml里引入 Ollama 的 Starter:
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-ollama-spring-boot-starter</artifactId> </dependency>这里要特别注意版本对齐问题。Spring AI 的版本迭代比较快,不同版本之间 API 会有调整,最稳妥的做法是去 Spring AI 官网查当前 Release 对应的版本号,然后在pom.xml的parent里指定,或者通过dependencyManagement锁定版本。
如果你使用的是比较新的 Spring Boot 3.2+ 和 Spring AI 1.0.0 之后的版本,依赖坐标可能要写成spring-ai-starter-model-ollama。我推荐直接去 spring initializr 页面勾选 Ollama 依赖,让脚手架帮你搞定版本,这是最不容易出错的方法。
4.2 核心配置项与参数解析
Spring AI 接入 Ollama 的配置非常简洁,核心就是 base-url、模型名和模型参数。一个最基础的配置如下:
spring: ai: ollama: base-url: http://localhost:11434 chat: model: qwen2.5:7b options: temperature: 0.7 num-predict: 512 top-p: 0.9 num-ctx: 8192参数逐个解释一下:
base-url是 Ollama 的地址,默认就是 11434 端口,如果你改了OLLAMA_HOST,这里要对应改。model指定用哪个模型,要和ollama list里看到的名字完全一致。temperature控制输出的随机性。值越低越确定,适合做代码生成、信息抽取;值越高越有创造性,适合做文案创作。我建议服务端场景用 0.2-0.5 之间,避免模型胡编。num-predict控制生成的最大 token 数量。512 对于一般问答够用了,但如果你要模型输出长文章,这里要调大。top-p是核采样参数,默认 0.9 基本不用动。num-ctx就是前面提到的上下文长度,根据你的任务调整。
这里还有一个容易忽略的点:如果你在多个环境里部署,可以把这些配置放到 Spring Cloud Config 或者 Nacos 里,通过配置文件动态切换模型,真正做到“换模型不换代码”。
4.3 对话调用与流式输出实现
配置写好后,在业务代码里注入ChatClient就能用了。先看最基础的同步调用:
@Service public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient chatClient) { this.chatClient = chatClient; } public String ask(String question) { return chatClient.call(question); } }这段代码已经能完成一次完整的问答。但实际项目中,我更推荐使用Prompt和ChatResponse的方式,更方便加系统提示词和解析响应元数据:
public String askWithSystem(String question) { Prompt prompt = new Prompt( question, OllamaOptions.builder() .withModel("qwen2.5:7b") .withTemperature(0.3f) .build() ); ChatResponse response = chatClient.call(prompt); return response.getResult().getOutput().getText(); }如果你要做一个打字机效果的流式对话,用Flux:
public Flux<String> stream(String question) { return chatClient.stream(question); }前端通过 WebFlux 的 SSE 接口消费这个 Flux,就能做到逐字输出的效果。这个体验在 Web 聊天页面里非常重要,实测下来用 Ollama + Spring AI 做流式,基本没有额外延迟,模型出多少字就能推多少字。
注意:如果你用的是 Spring MVC 而不是 WebFlux,流式返回需要额外处理响应类型。最简单的方式是单独建一个
@RestController返回Flux<String>,并引入spring-boot-starter-webflux依赖。
4.4 结构化输出与 Function Calling 扩展
聊完基本的文本问答,我再分享两个我觉得很有价值的进阶能力。
第一个是结构化输出。LLM 返回的是自然语言,但业务系统往往需要 JSON。Spring AI 提供了OutputParser机制,可以强制模型输出指定格式。比如想得到一个实体列表,可以先定义 POJO,然后让ChatClient帮你自动解析:
public record Person(String name, int age) {} List<Person> persons = chatClient.call( new Prompt("从这段话中提取所有人的姓名和年龄:张三25岁,李四30岁"), BeanOutputParser.list(Person.class) );原理是在请求里附加一段“请只输出 JSON 格式,且符合以下 schema”的指令,然后把模型的 JSON 响应反序列化成对象。这类能力很适合做信息抽取、报表生成。
第二个是 Function Calling。Spring AI 允许你把自己的 Java 方法作为工具注册给模型。模型在回答问题时如果发现自己需要某个数据,会主动调用你的方法。举个简单例子,你有一个查询订单状态的方法,模型在回答用户“我的订单现在什么状态”时,会去调用这个方法拿数据,再组织语言返回。这种模式能极大扩展模型能力,解决模型“不知道实时数据”和“不会精确计算”的短板。
注册方式很简单,在一个@Bean方法上定义函数:
@Bean @Description("查询订单物流状态") public Function<OrderQueryRequest, String> queryOrderStatus() { return request -> orderService.getStatus(request.orderId()); }然后在 ChatClient 调用时通过withTools传入即可。这块功能比较深,这篇不展开,后续系列文章可以单独讲。
5. 常见问题排查与性能优化
5.1 下载慢、拉取失败类问题处理
这类问题出现的频率最高。除了换国内镜像源之外,还有几个细节值得关注。
一是模型中断后重新下载。Ollama 支持断点续传,中断后重新执行ollama run命令,一般会接着下。如果发现没有续传,可以试试先ollama pull,再ollama run,pull命令的进度条更直观。
二是磁盘空间不足。模型下载前要确认磁盘剩余空间。ollama list里显示的是最终解压后的体积,下载过程中还会有临时文件,建议至少留出模型体积 1.5 倍的空间。我遇到过最尴尬的情况是下载到 90% 时磁盘满了,然后模型文件损坏,只能删掉重新拉。
三是模型拉取成功但运行报错。这时候不要急着删,先看完整报错日志。如果是量化格式不支持,换个 Q4_K_M 这类常见量化版本基本能解决。
5.2 模型加载失败与 dtype 异常
前面提到的expected m1 and m2 to have the same dtype是 AMD 或集成显卡用户高频遇到的一个报错。这个报错本质上是模型的部分参数在 GPU 上加载,部分参数在 CPU 上加载,两者的数据类型没有对齐,导致矩阵运算失败。
最简单的处理方法就是前面说的,在环境变量里设置OLLAMA_NUM_GPU=0强制走 CPU,然后重启 Ollama。如果你还是希望用 GPU 加速,可以尝试更新显卡驱动,或者换一个量化等级更高的模型,比如用q4_K_M代替f16,但这块兼容性因显卡型号而异,不一定是 100% 能解决。
另外还有一个常见的 dtype 问题是跨平台复制模型文件。比如你把别人电脑上的模型目录直接拷贝到自己机器上,容易因为平台架构不同导致二进制文件不兼容。遇到这种情况,老老实实重新 pull 一次,别在模型文件上偷懒。
5.3 Spring AI 连接失败与不输出 content
这一节是 Java 侧最常踩的坑。
先说连接失败:如果 Spring AI 启动时报Connection refused: localhost/127.0.0.1:11434,第一步先确认 Ollama 是否真的在运行。很多人启动 Ollama 后又把它退出了,Spring Boot 起来当然连不上。第二步确认配置地址,如果你设置了OLLAMA_HOST为非默认端口,那么spring.ai.ollama.base-url要和它一致。第三步检查防火墙,局域网访问的场景下,防火墙可能会拦截 11434 端口。
再有就是“模型返回正常,但 Spring 调用拿不到 content”的问题。这个问题网上讨论热度很高,尤其在对接 OpenAI 兼容服务时频繁出现。其实核心原因通常是两个:一是响应 JSON 结构变化,模型返回的数据里content字段的路径变了,或者变成了空字符串;二是num-predict设置得太小,模型还没来得及输出完整答案就被截断,导致content从模型侧看是 null。
排查思路很简单:先用 curl 原文看一下完整响应,确认返回结构里有没有 text/content 字段。确认模型侧没问题后,再把 Spring AI 的日志级别调到 DEBUG,看一下它实际拿到的响应内容和解析路径。大多数能解。
5.4 硬件配置参考与模型选型建议
最后整理一份硬件参考表,帮大家判断自己的机器能跑到什么程度。我平时测试的机器配置是:CPU 为 AMD 5600G,内存 32GB,无独立显卡。这个配置属于“刚刚够用”的入门水平。
| 模型规模 | 量化等级 | 建议内存 | 是否建议在 32G 内存机器上运行 | 速度体验 |
|---|---|---|---|---|
| 7B / 8B | Q4_K_M | 8-12GB | 可以,推荐 | CPU 下 3-5 token/s,可开发测试 |
| 13B / 14B | Q4_K_M | 16-24GB | 勉强可以,但内存吃紧 | CPU 较慢,建议有 GPU |
| 32B+ | Q4_K_M | 32GB 以上 | 不推荐 | CPU 基本不可用,需要多卡 GPU |
如果是 N 卡且显存在 8GB 以上,跑 7B/8B 或 14B 的量化模型都很流畅,体验会好很多。最近很火的 Qwen2.5 系列本来就是多尺寸覆盖,从 0.5B 到 72B 都有,开发阶段可以先拉 0.5B 或 3B 的小模型验证代码,确认链路通了再换大模型,能省不少等待时间。
还有一个小建议:不要盲目追求模型尺寸。你在开发环境跑 7B 就好,真要上线高并发,再考虑部署到 GPU 服务器或迁移到云端 API。这套 Spring AI 抽象的好处就在这里,切换模型只是改配置,业务代码一行不用动。
6. 项目生态与后续扩展
6.1 与 Dify、ruoyi-ai 等开源项目对接
现在社区里已经有很多项目直接把 Ollama 作为默认的模型后端了。比如 Dify,你可以在模型供应商里选择 Ollama,填上http://localhost:11434和模型名,就能在可视化工作流里编排大模型应用。好处是 Dify 帮你做了知识库切片、Prompt 编排、日志审计,而你只需要保证 Ollama 在后台正常运行。
另一个典型项目是 ruoyi-ai 这类基于 Spring Boot 的管理系统脚手架,它把 AI 能力集成到后台管理界面里,底层同样可以对接 Ollama。通过 Spring AI 的抽象,你不用关心底层到底是 Ollama 还是 OpenAI,只管调ChatClient接口。
我自己的经验是:先在命令行把 Ollama 本身跑通,再用 Spring AI 写最小示例,最后放到业务系统里。这个顺序能帮你节约排查时间,因为每一层的边界都很清晰。
6.2 后续可以做的方向
如果你已经顺利把 Spring AI + Ollama 跑通,下一步值得尝试的方向有:
- 接入 Embedding 模型做本地知识库问答,把文档向量化后存储在向量数据库里,实现基于本地文档的智能问答。
- 使用
spring-ai-rag模块做 Retrieval-Augmented Generation,让模型引用你提供的私有资料来回答,减少幻觉。 - 把流式对话接入 WebSocket 或 SSE,做一个真正可用的聊天机器人。
- 在 Spring AI 中使用 Advisor 机制做日志记录、敏感信息过滤、多轮上下文记忆管理。
这些都是同一个架构下的自然延伸,底层模型和框架不变,业务代码的可复用度很高。
7. 实操心得与一个小技巧
最后讲一点我个人的体会。
这套方案最舒服的地方是“可控”。模型在本地,参数调错了随时改,上下文长度、量化等级、缓存机制都可以随手调整,不像调云端 API 那样要考虑成本、限流和数据合规。对于个人开发者和中小团队来说,用一个 7B 模型先把产品雏形跑起来,等验证了需求、攒了用户量,再逐步迁移到更大模型或 GPU 集群,这是最务实的一条路。
再分享一个小技巧:很多人用 Ollama 时会遇到 Windows 控制台输出乱码的问题,这跟模型本身没有关系,是控制台代码页不对。把终端切到 UTF-8 编码(chcp 65001)再启动 Ollama,或者直接用 Windows Terminal,基本就正常了。
最后提醒一句:遇到问题先别急着改代码,先用 curl 调一次 Ollama 的接口,确认模型服务本身没问题,再回头排查 Java 侧。这条排查顺序能帮你省掉一大半的纠结时间。如果你按这篇文章的顺序从安装走到 Spring AI 集成,大概一小时内就能跑通,祝你顺利。