在这里插入图片描述

Spring AI正式稳定版发布,结合本地Ollama,本地部署数据完全闭环,成本几乎为零。

今天就以企业内部智能客服问答模块为例,讲一讲Java Spring AI接入本地Ollama的全流程,从环境搭建、核心配置、代码开发,到生产级优化、常见踩坑,每一步都是我亲手操作过的,没有空洞的理论,只有可直接复用的实战经验。

一、前置准备:本地Ollama部署与Spring Boot项目搭建

1. 本地Ollama部署

Ollama是目前最简洁的本地大模型部署工具,支持Windows、Linux、MacOS,一行命令就能完成安装和模型启动。我用的是AlmaLinux 9服务器 ,部署流程如下:

# 卸载旧版本(如果有)
sudo dnf remove docker*  # 哦不对,卸载旧版Ollama
sudo rm -rf /usr/bin/ollama /usr/share/ollama ~/.ollama

# 安装Ollama
curl https://ollama.com/install.sh | sh

# 启动Ollama服务并设置开机自启
sudo systemctl start ollama
sudo systemctl enable ollama

# 验证安装
ollama --version

国内开发者一定要配置Ollama的国内镜像源,不然拉取模型特别慢,有时候甚至超时:

# 编辑Ollama的systemd服务配置
sudo systemctl edit ollama.service

# 在打开的编辑器里添加环境变量
[Service]
Environment="OLLAMA_HOST=0.0.0.0:11434"
Environment="OLLAMA_MODELS=/data/ollama/models"  # 可选,把模型存到数据盘
Environment="OLLAMA_KEEP_ALIVE=300"  # 模型在内存里保留5分钟,不用每次重新加载
Environment="OLLAMA_NUM_PARALLEL=4"  # 支持4个并发请求,根据服务器CPU调整

# 重载配置并重启Ollama
sudo systemctl daemon-reload
sudo systemctl restart ollama

然后拉取一个适合企业内部问答的轻量化模型,比如Llama 3 8B量化版,推理速度快,精度也够用:

ollama run llama3.1:8b-instruct-q4_0

启动成功后终端会显示 >>> ,可以直接输入问题测试,比如“介绍一下Spring AI的核心功能”,确认模型能正常响应。

2. Spring Boot项目搭建

新建一个Spring Boot 3.2.5项目,Spring Boot 3.x是Spring AI的最低要求,JDK用17或21都可以,我用的是JDK 17。核心依赖如下:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring AI Ollama -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-ollama-spring-boot-starter</artifactId>
    </dependency>

    <!-- Spring Boot Actuator(可选,用于健康检查) -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>

    <!-- Lombok(可选,简化代码) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

<!-- Spring AI依赖管理 -->
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0-M4</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

注意Spring AI的版本,我用的是1.0.0-M4,是目前比较稳定的里程碑版本,不要用太新的快照版,容易有兼容性问题。

二、核心配置:application.yml的关键参数

在src/main/resources目录下创建application.yml,配置Ollama的连接信息和大模型参数:

spring:
  application:
    name: spring-ai-ollama-demo
  ai:
    ollama:
      # Ollama服务地址,本地部署用localhost,远程部署用服务器IP
      base-url: http://localhost:11434
      # 模型名称,必须和Ollama启动的模型名称完全一致
      model: llama3.1:8b-instruct-q4_0
      # 大模型推理参数
      chat:
        options:
          # 温度值,0-1,值越小输出越精准,值越大输出越灵活
          temperature: 0.7
          # 最大生成token数,控制输出内容长度
          max-tokens: 1024
          # 推理超时时间,单位毫秒
          timeout: 5000
          # 上下文窗口大小,根据模型调整,Llama 3.1 8B是128k
          num-ctx: 4096

# Actuator配置(可选)
management:
  endpoints:
    web:
      exposure:
        include: health,info
  endpoint:
    health:
      show-details: always

这里有几个关键参数要注意:

  • base-url:如果Ollama部署在远程服务器,要把localhost换成服务器IP,还要确保远程服务器的防火墙开放了11434端口;
  • model:必须和Ollama启动的模型名称完全一致,比如启动的是llama3.1:8b-instruct-q4_0,就不能写成llama3.1;
  • temperature:企业内部问答建议设为0.5-0.7,兼顾精准度和灵活性;
  • num-ctx:根据模型的最大上下文窗口调整,不要超过模型的限制,不然会报错。

三、代码开发:单轮对话、多轮对话、流式输出

Spring AI已经封装好了大模型调用的核心逻辑,我们只需注入ChatClient对象,就能一键调用本地Ollama,无需编写复杂的HTTP请求和数据解析代码。

1. 单轮对话

单轮对话是最简单的场景,比如用户问一个问题,大模型直接回答,不需要记住之前的对话历史:

package com.example.springaiollamademo.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/ai")
public class ChatController {

    private final ChatClient chatClient;

    // 构造方法注入ChatClient
    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    /**
     * 单轮对话接口
     * @param question 用户问题
     * @return 大模型回答
     */
    @PostMapping("/single-chat")
    public String singleChat(@RequestBody String question) {
        return chatClient.prompt()
                .user(question)
                .call()
                .content();
    }
}

启动项目,用Postman测试接口,请求地址是http://localhost:8080/api/ai/single-chat,请求体是纯文本的问题,比如“如何用Spring AI接入本地Ollama”,就能看到大模型的回答了。

2. 多轮对话

多轮对话需要记住之前的对话历史,比如用户先问“什么是Spring Boot”,再问“它和Spring AI有什么关系”,大模型要能结合第一个问题的回答来回答第二个问题。Spring AI提供了ChatMemory来管理对话历史:

package com.example.springaiollamademo.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.memory.InMemoryChatMemory;
import org.springframework.web.bind.annotation.*;

import java.util.Map;

@RestController
@RequestMapping("/api/ai")
public class ChatController {

    private final ChatClient chatClient;
    // 用InMemoryChatMemory存储对话历史,生产环境建议用Redis
    private final ChatMemory chatMemory = new InMemoryChatMemory();

    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    /**
     * 多轮对话接口
     * @param request 包含对话ID和用户问题
     * @return 大模型回答
     */
    @PostMapping("/multi-chat")
    public String multiChat(@RequestBody Map<String, String> request) {
        String conversationId = request.get("conversationId");
        String question = request.get("question");

        return chatClient.prompt()
                .user(question)
                .chatMemory(chatMemory)
                .conversationId(conversationId)
                .call()
                .content();
    }
}

这里用的是InMemoryChatMemory,对话历史存在内存里,服务器重启后就会丢失,生产环境建议用RedisChatMemory,需要引入spring-ai-redis依赖:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-redis</artifactId>
</dependency>

然后在application.yml里配置Redis连接信息,把InMemoryChatMemory换成RedisChatMemory即可。

3. 流式输出

流式输出可以让大模型的回答逐字显示,提升用户体验,比如ChatGPT的效果:

package com.example.springaiollamademo.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/api/ai")
public class ChatController {

    private final ChatClient chatClient;

    public ChatController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    /**
     * 流式输出接口
     * @param question 用户问题
     * @return 逐字返回的大模型回答
     */
    @GetMapping(value = "/stream-chat", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<String> streamChat(@RequestParam String question) {
        return chatClient.prompt()
                .user(question)
                .stream()
                .content();
    }
}

注意流式输出接口的produces必须是MediaType.TEXT_EVENT_STREAM_VALUE,前端用EventSource来接收数据。

四、生产级优化:让服务更稳、更快、更安全

1. 模型优化

  • 用量化版模型:比如Llama 3.1 8B的q4_0或q8_0量化版,在牺牲少量精度的前提下,大幅提升推理速度,减少内存占用;
  • 调整OLLAMA_KEEP_ALIVE:把模型在内存里保留的时间设长一点,比如300秒,不用每次请求都重新加载模型;
  • 调整OLLAMA_NUM_PARALLEL:根据服务器的CPU核心数调整并发请求数,比如4核CPU设为4,8核设为8。

2. 接口优化

  • 限流防刷:用Spring Cloud Gateway或Guava RateLimiter对接口进行限流,防止大模型被恶意调用;
  • 权限控制:用Spring Security对接口进行权限控制,只有内部员工才能访问;
  • 缓存优化:对高频重复的问题,比如“公司的请假流程是什么”,用Redis缓存结果,下次请求直接返回缓存,减少大模型的调用次数。

3. 安全加固

  • 不要把敏感信息(比如Ollama的API密钥,如果有的话)直接写在配置文件里,用Spring Cloud Config或Vault管理;
  • 限制Ollama的访问IP:在Ollama的systemd服务配置里添加OLLAMA_ORIGINS=*(开发环境),生产环境只允许Spring Boot服务的IP访问;
  • 用HTTPS:生产环境用Nginx反向代理Spring Boot服务和Ollama服务,开启HTTPS,防止数据被窃听。

五、常见踩坑总结

1. Spring Boot启动失败,提示“Could not find bean of type ‘ChatClient.Builder’”

原因:Spring AI的依赖版本和Spring Boot的版本不兼容;

解决:确保Spring Boot的版本是3.2.x或3.3.x,Spring AI的版本是1.0.0-M4或更高的稳定里程碑版本。

2. 调用接口时提示“Timeout waiting for response from Ollama”

原因:大模型推理耗时过长,超过了配置的timeout时间;

解决:

  • 调整application.yml里的timeout参数,比如从5000改成10000;
  • 用更轻量的量化版模型;
  • 升级服务器的CPU或GPU。

3. 多轮对话时,大模型记不住之前的对话历史

原因:没有正确使用ChatMemory和conversationId;

解决:

  • 确保每次多轮对话请求都传了同一个conversationId;
  • 生产环境不要用InMemoryChatMemory,用RedisChatMemory。
4. 远程部署Ollama时,Spring Boot连接失败

原因:远程服务器的防火墙没有开放11434端口,或者Ollama的OLLAMA_HOST没有设为0.0.0.0:11434;

解决:

  • 检查Ollama的OLLAMA_HOST配置;
  • 开放远程服务器的11434端口:
sudo firewall-cmd --zone=public --add-port=11434/tcp --permanent
sudo firewall-cmd --reload

六、总结

Java Spring AI接入本地Ollama,是目前Java开发者对接大模型的最优解——不用懂Python,不用搭复杂的中间层,本地部署数据完全闭环,成本几乎为零。

本文的流程和优化方案,都是我在企业内部智能客服项目中验证过的,没有照搬官方文档,而是加入了很多踩坑的细节。希望这篇文章能帮你少走弯路,快速把大模型集成到你的Java项目里。

🎁 福利时间

如果你正在备战面试或者想要学习其他知识,给大家推荐一个宝藏知识库,作者整理了一些列 Java 程序员需要掌握的核心知识,有需要的自取不谢。

知识库地址:https://farerboy.com/


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐