SpringAI与Ollama:本地LLM开发实战指南
1. 为什么你需要SpringAI和Ollama?
如果你是一名Java开发者,最近肯定被各种AI新闻刷屏了。从ChatGPT到各种国产大模型,感觉全世界都在搞AI,但自己一上手就懵了:动辄几十GB的模型文件、复杂的Python环境、还有那让人头疼的API调用费用。难道我们Java开发者就只能做个旁观者吗?
当然不是。SpringAI和Ollama的出现,就是为了解决我们这些痛点。简单来说,SpringAI 是Spring官方推出的、专门为Java生态设计的AI应用框架。它让你能用写Spring Boot应用的方式,轻松地调用各种大模型的能力,就像你平时用@RestController写接口一样自然。而 Ollama 则是一个能在你本地电脑上(甚至是性能不错的笔记本)运行各种开源大模型的工具,它把复杂的模型部署和管理过程,简化成了几条简单的命令。
把它们俩结合起来,意味着什么?意味着你可以在不依赖任何外部API、不支付任何费用、完全离线的情况下,用你最熟悉的Java和Spring技术栈,开发出具备智能对话、文本生成、内容总结等能力的应用。无论是想给内部系统加个智能客服,还是做个自动生成周报的小工具,或者只是想自己捣鼓点AI应用玩玩,这个组合都能让你快速上手。
我自己的体验是,从零开始到跑通第一个AI对话,只用了不到半小时。整个过程几乎没有遇到什么“玄学”问题,这对于习惯了Spring那种“约定大于配置”的开发者来说,简直太友好了。接下来,我就带你一步步走通这个流程。
2. 手把手搭建你的本地AI环境
2.1 第一步:请来你的“模型管家”——Ollama
Ollama的安装简单到超乎想象,它就像一个专门管理大模型的“管家”。你不需要懂深度学习,也不需要配CUDA环境(当然,有GPU会更快),Ollama帮你把一切都打包好了。
对于Mac用户: 直接去官网下载那个.dmg安装包,双击、拖拽安装,和装一个普通软件没区别。安装完成后,你会在应用程序里找到它。我更推荐用命令行,打开终端(Terminal),输入 ollama run llama2 试试。这条命令会自动下载并启动Meta的Llama 2模型(约3.8GB)。第一次运行会下载模型,需要一点时间,泡杯咖啡回来就好了。
对于Windows用户: 前往Ollama官网下载Windows版的安装程序(.exe文件)。安装过程一路“下一步”就行。安装完成后,你可以在开始菜单找到“Ollama”并运行它,它会以一个后台服务的形式启动。同样,打开PowerShell或CMD,输入 ollama run gemma:2b。Gemma是Google推出的轻量级模型,对中文支持不错,而且只有20亿参数,在普通电脑上运行毫无压力。
对于Linux用户: 打开终端,一行命令搞定:
curl -fsSL https://ollama.com/install.sh | sh
安装完成后,同样用 ollama run mistral 命令测试。Mistral是一个性能很强的7B参数模型,如果你的电脑内存有16GB以上,跑起来会很流畅。
验证安装是否成功: 安装并运行一个模型后,打开你的浏览器,访问 http://localhost:11434。如果看到Ollama的API文档页面,恭喜你,你的本地大模型服务器已经跑起来了!这个11434端口就是Ollama对外提供服务的端口,后续SpringAI就会连接到这里。
这里有个小建议:第一次玩,别贪心去下载那些动辄70B、100B+参数的巨无霸模型。从 gemma:2b、llama2:7b 或 mistral:7b 开始。它们体积小(几GB),响应速度快,在CPU上也能获得不错的体验,足够你完成大部分概念验证和学习了。
2.2 第二步:创建你的SpringAI项目骨架
环境搭好了,现在该写代码了。创建Spring Boot项目,你现在有了更AI专属的方式。
方法一:使用Spring Initializr(最传统) 访问 start.spring.io,在“Dependencies”那里,点击“Add Dependencies”,搜索“Spring AI”。你会发现一个叫 “Ollama” 的依赖,勾选它。同时,确保你添加了 “Spring Web” 依赖,因为我们之后要写接口。然后生成项目,用IDE打开。
方法二:使用Spring Boot CLI(最快捷,官方推荐) 如果你已经安装了Spring Boot CLI(没有的话可以搜一下安装方法,很简单),那么创建项目就是一行命令的事:
spring boot new --from ai --name my-ai-app
这条命令会直接创建一个预配置了SpringAI基础依赖的项目模板,特别方便。生成的项目里会有一个README,引导你进行下一步。
无论用哪种方法,打开项目后的pom.xml文件,你应该能看到类似的依赖:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这个 spring-ai-ollama-spring-boot-starter 就是连接SpringAI和Ollama的桥梁,它封装了所有通信细节。
2.3 第三步:关键的连接配置
项目有了,模型服务也跑起来了,现在要让它们俩“握手”。配置非常简单,只需要在 application.properties 或 application.yml 里加两行。
如果你用的是 .properties 文件:
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.chat.model=gemma:2b
如果你更喜欢 .yml 格式:
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
model: gemma:2b
这里的配置一目了然:base-url 告诉SpringAI你的Ollama服务在哪里;chat.model 指定你要使用Ollama中的哪个模型。请确保这里的模型名和你用 ollama run 启动的模型名称一致。
配置完,启动你的Spring Boot应用。如果控制台没有报错,并且能看到SpringAI相关的自动配置日志,那么连接就成功了!
3. 从“Hello World”到流式对话:编写你的第一个AI交互
环境配置只是热身,写代码才是我们开发者的主场。SpringAI的核心抽象是 ChatClient,通过它,你可以用几乎相同的方式去调用任何后端模型(Ollama、OpenAI、Azure等),这种可移植性设计真是太棒了。
3.1 基础同步调用:让AI打个招呼
我们先来写一个最简单的同步调用。创建一个 @RestController 和一个简单的服务。
首先,写一个Service类来封装AI调用逻辑:
import org.springframework.ai.chat.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class SimpleAIService {
@Autowired
private ChatClient chatClient; // SpringAI会自动注入配置好的ChatClient
public String getAIResponse(String userMessage) {
// 构建一个简单的提示词(Prompt)
String prompt = "请用一句简短、友好、热情的话回答用户。用户说:" + userMessage;
// 调用ChatClient,获取AI的回复
String aiResponse = chatClient.call(prompt);
return aiResponse;
}
}
然后,创建一个控制器来暴露接口:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class AIController {
@Autowired
private SimpleAIService aiService;
@GetMapping("/chat")
public String chat(@RequestParam String message) {
return aiService.getAIResponse(message);
}
}
启动应用,用浏览器或curl访问 http://localhost:8080/chat?message=你好,世界!。稍等片刻,你就会看到模型返回的问候,比如“你好!很高兴见到你!”之类的。虽然简单,但这标志着你的Java程序已经成功调用了本地大模型!
3.2 进阶:体验“打字机效果”的流式响应
同步调用虽然简单,但体验上少了点“AI感”。想象一下ChatGPT那种一个字一个字蹦出来的效果,是不是更有趣?SpringAI同样支持流式(Streaming)响应,实现起来也不难。
我们来改造一下Service,让它支持流式输出:
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.StreamingChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
@Service
public class StreamingAIService {
@Autowired
private StreamingChatClient streamingChatClient; // 注意这里注入的是StreamingChatClient
public Flux<String> getStreamingAIResponse(String userMessage, String role) {
// 构建一个更复杂的、带系统指令(System Message)的提示词
String systemPrompt = "你是一个" + role + ",请用专业且生动的语言回答用户问题。";
String fullPrompt = systemPrompt + "\n用户问题:" + userMessage;
// 调用stream()方法,返回一个Flux(响应式流)
return streamingChatClient.stream(fullPrompt)
.map(response -> response.getResult().getOutput().getContent()); // 从响应中提取文本内容
}
}
再创建一个新的控制器来处理流式请求:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;
@RestController
public class StreamingAIController {
@Autowired
private StreamingAIService streamingAiService;
@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) // 关键:设置返回内容为SSE流
public Flux<String> streamChat(@RequestParam String message,
@RequestParam(defaultValue = "高级运营专家") String role) {
return streamingAiService.getStreamingAIResponse(message, role);
}
}
这里的关键是 produces = MediaType.TEXT_EVENT_STREAM_VALUE,它告诉Spring这是一个服务器发送事件(Server-Sent Events)流。现在,你可以用一些支持SSE的客户端来测试了。最简单的方法是使用 curl:
curl -N http://localhost:8080/chat/stream?message=写一段关于春季旅行的文案&role=文案策划
你会看到回复内容像流水一样,一段一段地实时输出到命令行,那种“AI正在思考并生成”的临场感立刻就来了。在前端,你可以用 EventSource API 轻松地接收并渲染这个流,做出一个类ChatGPT的对话界面。
4. 玩点真的:构建一个智能周报助手
光会问好还不够,我们来做点实用的。很多开发者每周都要写工作周报,这个过程既枯燥又耗时。不如用我们刚学的技术,做一个智能周报生成器。
4.1 设计提示词(Prompt)工程
AI的表现很大程度上取决于你如何“提问”或“下达指令”。这就是提示词工程。对于周报生成,我们需要给模型一个清晰的结构和上下文。
我们可以创建一个更专业的Prompt模板:
import org.springframework.stereotype.Component;
@Component
public class WeeklyReportPromptTemplate {
public String generatePrompt(String thisWeekWork, String nextWeekPlan, String style) {
String styleInstruction = "";
switch (style) {
case "简洁":
styleInstruction = "请用非常简洁、条理清晰的要点形式总结,避免任何修饰性词语。";
break;
case "详细":
styleInstruction = "请生成一份详细、专业的周报,包含背景、具体行动、成果数据和后续思考。";
break;
case "乐观":
styleInstruction = "请用积极、向上、充满干劲的语气来撰写这份周报,突出进展和团队协作。";
break;
default:
styleInstruction = "请用专业、平实的语言撰写。";
}
return String.format("""
你是一个资深的软件开发工程师,请根据我提供的工作内容,生成一份专业的工作周报。
%s
**本周已完成工作:**
%s
**下周工作计划:**
%s
请严格按照以下Markdown格式输出周报,不要添加任何额外的解释或开场白:
## 一、本周工作摘要
[此处生成一段概括性总结]
## 二、已完成工作详情
[将‘本周已完成工作’的内容,分条目整理并适当扩充,每条以‘-’开头]
## 三、遇到的问题与解决方案
[根据已完成工作的内容,合理推断可能遇到的1-2个技术难点,并给出简要解决方案]
## 四、下周工作计划
[将‘下周工作计划’的内容,转化为具体、可执行的任务条目]
## 五、所需支持与建议
[生成1-2条合理的、需要团队或上级支持的建议]
""", styleInstruction, thisWeekWork, nextWeekPlan);
}
}
这个模板做了几件事:1. 定义了AI的角色(资深工程师);2. 允许用户选择周报风格;3. 给出了非常具体的输出格式要求(Markdown标题)。这样能极大提高生成内容的可用性和规范性。
4.2 实现周报生成服务与接口
有了模板,服务层的实现就水到渠成了:
import org.springframework.ai.chat.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class WeeklyReportService {
@Autowired
private ChatClient chatClient;
@Autowired
private WeeklyReportPromptTemplate promptTemplate;
public String generateReport(String thisWeekWork, String nextWeekPlan, String style) {
// 1. 生成精心设计的提示词
String prompt = promptTemplate.generatePrompt(thisWeekWork, nextWeekPlan, style);
// 2. 调用AI模型
String report = chatClient.call(prompt);
// 3. (可选)后处理,比如确保格式正确
return report;
}
}
最后,创建一个REST接口:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ReportController {
@Autowired
private WeeklyReportService reportService;
@PostMapping("/api/report/generate")
public String generateWeeklyReport(@RequestBody ReportRequest request) {
return reportService.generateReport(
request.getThisWeekWork(),
request.getNextWeekPlan(),
request.getStyle()
);
}
// 简单的请求体封装
public static class ReportRequest {
private String thisWeekWork;
private String nextWeekPlan;
private String style = "详细"; // 默认风格
// getters and setters ...
}
}
现在,你可以用Postman或前端页面,输入“本周修复了登录接口的BUG,完成了用户模块开发”、“下周计划进行代码评审和压力测试”,并选择“简洁”风格。点击发送,一份结构清晰、语言得体的周报草稿瞬间就生成了。你只需要稍作修改和补充,就能直接使用,效率提升肉眼可见。
5. 深入探索与避坑指南
项目跑起来只是开始,要想用得顺手、用得放心,还得了解一些进阶知识和常见问题。
5.1 性能调优与模型选择
在本地运行模型,性能是我们最关心的问题。这里有几个关键点:
模型选择是重中之重。Ollama支持很多模型,你可以用 ollama list 查看已安装的,用 ollama pull <model-name> 下载新的。对于Java后端集成,我推荐以下策略:
- 追求速度与轻量:选择参数量在7B(70亿)以下的模型,如
gemma:2b,llama2:7b,mistral:7b。它们在CPU上也能有秒级的响应速度,适合交互式应用。 - 追求质量与能力:如果你的服务器内存足够(32GB+),可以考虑
llama2:13b,mixtral:8x7b(混合专家模型,效果很棒)。这些模型能处理更复杂的逻辑和更长的文本。 - 中文任务优先:
qwen:7b(通义千问)、yi:34b等国产模型在中文理解和生成上通常有更好表现。可以用ollama pull qwen:7b来获取。
调整Ollama运行参数。运行模型时,可以指定参数来控制资源占用和生成效果:
ollama run llama2:7b --num-predict 512 --temperature 0.7
--num-predict 512:限制模型最大生成512个token(约300-400汉字),防止它“滔滔不绝”。--temperature 0.7:控制生成随机性。值越高(接近1.0)越有创意但也可能胡言乱语;值越低(接近0.0)越稳定和可预测。0.7是个不错的平衡点。
SpringAI客户端配置。在 application.yml 中,你可以对SpringAI的客户端进行更细致的配置:
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
model: mistral:7b
options:
temperature: 0.8
num-predict: 256
top-k: 40
top-p: 0.9
这些选项会传递给Ollama,影响生成效果。top-k 和 top-p 是另一种控制随机性的采样方式,多数情况下用默认值即可。
5.2 你可能遇到的“坑”及解决方案
在实际操作中,我踩过几个坑,这里分享给你,希望能帮你节省时间:
-
“Connection refused” 错误:SpringAI启动时报错,无法连接
localhost:11434。- 原因:Ollama服务没有启动。
- 解决:确保你先在终端运行了
ollama run <模型名>。在Windows上,检查Ollama后台服务是否运行(可以在任务管理器的“服务”选项卡里找“Ollama”)。
-
模型响应速度极慢或内存溢出:
- 原因:模型太大,或系统可用内存不足。
- 解决:首先,换一个更小的模型(如从13B换到7B)。其次,检查任务管理器,确保模型运行时没有挤占所有内存。对于Windows/Mac,Ollama通常能自动管理。在Linux服务器上,可能需要通过环境变量
OLLAMA_NUM_PARALLEL等限制其并发数。
-
生成的内容不符合预期或胡言乱语:
- 原因:提示词(Prompt)不够清晰,或者模型的“温度”(temperature)设置过高。
- 解决:这是提示词工程的问题。参考我们周报助手的例子,给你的AI设定明确的“角色”、清晰的“任务”和严格的“输出格式”。把
temperature调低到0.3-0.5试试,输出会更稳定。
-
如何更新或切换模型?
- 在Ollama端:用
ollama pull <新模型名>下载新模型。运行时指定新模型名即可。 - 在SpringAI端:只需修改配置文件中的
spring.ai.ollama.chat.model属性,重启应用,所有代码无需任何改动!这充分体现了SpringAI API可移植性的优势。
- 在Ollama端:用
5.3 还能做什么?更多应用场景启发
本地AI+Java的组合,想象力远不止于聊天和写周报。这里有几个我实践过或认为很有潜力的方向:
- 智能代码助手:利用SpringAI的“函数调用”功能,你可以让模型理解你的代码库(通过向量数据库检索),然后回答诸如“这个支付接口在哪被调用?”、“给我写一个符合我们项目规范的JPA Repository”之类的问题。
- 内部知识库问答:将公司内部的文档、Wiki、会议纪要通过ETL框架灌入到如
Chroma、PGVector这样的向量数据库(SpringAI都支持),然后做一个基于自然语言的智能搜索系统。新员工不用再翻几百页手册,直接提问就行。 - 自动化测试数据生成:让AI根据你的数据模型(Entity类),生成大量符合业务逻辑、多样化的测试数据,比如生成1000个包含合理姓名、地址、消费记录的用户档案。
- 日志分析与异常摘要:将生产环境的海量日志错误信息实时喂给模型,让它帮你总结出“过去一小时最主要的三个错误类型及其可能原因”,这比人眼看日志高效得多。
本地部署最大的好处就是数据隐私和安全。所有数据都在你自己的机器上流转,完全不用担心敏感信息泄露到第三方API。这对于处理企业内部数据、开发涉密应用来说,是至关重要的前提。
走到这一步,你已经成功地将最前沿的大模型能力,无缝集成到了你最熟悉的Java生态中。从环境搭建到核心代码编写,再到性能调优和场景拓展,这条路径已经打通。剩下的,就是发挥你的创意,去解决那些真实世界中有趣或棘手的问题了。我自己的感受是,技术门槛比想象中低得多,真正的挑战和乐趣,在于如何设计出巧妙、实用的AI增强型应用。
更多推荐




所有评论(0)