背景:Spring Boot 4.1.0 + Spring AI 2.0.0-RC2 + Ollama(deepseek-r1:1.5b),实现一个简单的流式聊天接口。搭建过程中连续踩了两个坑,在此记录,希望帮到有同样遭遇的同学。

环境

组件版本
Java21
Spring Boot4.1.0
Spring AI2.0.0-RC2
Ollamadeepseek-r1:1.5b(部署在局域网 172.16.29.3:11434)

坑一:Spring AI 版本与 Spring Boot 4 不兼容

报错信息

BeanDefinitionStoreException: Failed to process import candidates for configuration class
[org.springframework.ai.autoconfigure.ollama.OllamaAutoConfiguration]:
Type org.springframework.boot.autoconfigure.web.client.RestClientAutoConfiguration not present

原因

Spring AI 的 1.x 版本(如 1.0.0-M61.1.x)是基于 Spring Boot 3.x 构建的,而 Spring Boot 4.x 对模块进行了大幅重构,RestClientAutoConfiguration 的包路径和所属模块都发生了变化。

更关键的是,从 Spring AI 2.0 开始,Ollama 的 starter artifact 名字也改了

Spring AI 版本Artifact 名
1.x(Spring Boot 3)spring-ai-ollama-spring-boot-starter
2.x(Spring Boot 4)spring-ai-starter-model-ollama

解决方案

如果你的 Spring Boot 版本是 4.x,必须使用 Spring AI 2.0 系列的依赖:

<properties>
    <spring-ai.version>2.0.0-RC2</spring-ai.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-ollama</artifactId>
        <version>${spring-ai.version}</version>
    </dependency>
</dependencies>

版本兼容对照

Spring BootSpring AIArtifact
3.x1.0.x / 1.1.xspring-ai-ollama-spring-boot-starter
4.x2.0.xspring-ai-starter-model-ollama
提示:Spring AI 2.0.0 GA 版已于 2026 年 5 月底发布,建议优先使用 GA 版本。截至 2026 年 6 月,最新的预发布版本为 RC2。

坑二:系统 HTTP 代理导致 Ollama 流式响应解析失败

报错信息

WebClientResponseException: 200 OK from POST http://172.16.29.3:11434/api/chat,
but response failed with cause: java.lang.NumberFormatException: Empty chunk size

Caused by: java.lang.NumberFormatException: Empty chunk size
    at io.netty.handler.codec.http.HttpObjectDecoder.getChunkSize(HttpObjectDecoder.java:1047)

应用启动完全正常,Tomcat 监听 8080 端口也没问题,但只要一调用 Ollama 的聊天接口就报错。

排查过程

仔细看堆栈信息,发现一个关键线索:

at io.netty.handler.proxy.HttpProxyHandler$HttpClientCodecWrapper.channelRead(HttpProxyHandler.java:304)

HttpProxyHandler 出现在 Netty 的 pipeline 中!这说明 Reactor Netty 的 WebClient 检测到了系统代理,并通过代理转发了对 Ollama 的请求。

Ollama 的流式接口(/api/chat?stream=true)使用 chunked transfer encoding 返回 NDJSON 格式的数据。HTTP 代理在转发这类流式响应时,可能会重新编码 chunked 数据,导致 Netty 的 HttpObjectDecoder 在解析 chunk size 时遇到了空行(\r\n),从而抛出 NumberFormatException: Empty chunk size

为什么会被代理?

Reactor Netty 的 HttpClient 会自动读取 Java 系统属性或操作系统级别的代理配置(如 macOS 的系统代理、VPN、Charles/mitmproxy 等),然后自动添加 HttpProxyHandler。这在你访问外网时是好事,但访问局域网地址时就成了干扰。

解决方案

方法一:JVM 启动参数排除代理(推荐)

在启动参数中添加 nonProxyHosts,让局域网地址绕过代理:

# IntelliJ IDEA: Run Configuration → VM options
-Dhttp.nonProxyHosts=172.16.29.3
-Dhttps.nonProxyHosts=172.16.29.3

也可以使用通配符:

-Dhttp.nonProxyHosts=172.16.29.*|192.168.*|localhost|127.0.0.1
-Dhttps.nonProxyHosts=172.16.29.*|192.168.*|localhost|127.0.0.1

方法二:代码中配置(灵活但侵入性强)

如果你不想改 JVM 参数,也可以在 Spring 配置类中自定义 WebClient.Builder,让 Reactor Netty 跳过代理:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ReactorClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import reactor.netty.http.client.HttpClient;

import java.net.InetSocketAddress;
import java.net.ProxySelector;

@Configuration
public class ProxyBypassConfig {

    @Bean
    public WebClient.Builder webClientBuilder() {
        // 创建一个不使用代理的 HttpClient
        HttpClient httpClient = HttpClient.create()
                .proxy(proxy -> proxy
                        .type(reactor.netty.transport.ProxyProvider.Proxy.HTTP)
                        .nonProxyHosts("172.16.29.*")
                );

        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(httpClient));
    }
}

方法三:临时关闭系统代理

如果你只是在本地开发测试,最快的方式是临时关闭 VPN / 系统代理 / 抓包工具。

验证 Ollama 服务本身是否正常

排查时可以用 curl 直接测试 Ollama(curl 默认不走系统代理):

# 非流式测试
curl http://172.16.29.3:11434/api/chat \
  -d '{"model":"deepseek-r1:1.5b","messages":[{"role":"user","content":"你好"}],"stream":false}'

# 流式测试
curl http://172.16.29.3:11434/api/chat \
  -d '{"model":"deepseek-r1:1.5b","messages":[{"role":"user","content":"你好"}],"stream":true}'

如果 curl 能正常返回,说明 Ollama 没问题,问题出在代理。


最终配置参考

pom.xml(关键依赖)

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

<properties>
    <java.version>21</java.version>
    <spring-ai.version>2.0.0-RC2</spring-ai.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-starter-model-ollama</artifactId>
        <version>${spring-ai.version}</version>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <scope>provided</scope>
    </dependency>
</dependencies>

application.yaml

spring:
  application:
    name: ai-demo
  ai:
    ollama:
      base-url: http://172.16.29.3:11434
      chat:
        model: deepseek-r1:1.5b

ChatController(流式聊天接口)

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

    private final ChatClient chatClient;

    @RequestMapping(value = "/chat", produces = "text/html;charset=UTF-8")
    public Flux<String> chat(@RequestParam(defaultValue = "讲个笑话") String prompt) {
        return chatClient
                .prompt(prompt)
                .stream()
                .content();
    }
}

IntelliJ VM Options

-Dhttp.nonProxyHosts=172.16.29.3
-Dhttps.nonProxyHosts=172.16.29.3

总结

症状根因解法
版本不兼容启动失败,RestClientAutoConfiguration not presentSpring AI 1.x 不兼容 Spring Boot 4.x,且 artifact 改名使用 Spring AI 2.0 + spring-ai-starter-model-ollama
代理干扰运行时报 Empty chunk size系统 HTTP 代理破坏了 Ollama 流式响应的 chunked 编码JVM 参数 -Dhttp.nonProxyHosts 排除局域网地址

一句话总结:Spring Boot 4 必须配 Spring AI 2.0,局域网 Ollama 别让代理劫了。


Written on 2026-06-12