Spring AI 与 vLLM 交互踩坑记:HTTP/2 协议引发的“消失”的请求体
摘要: 在将Spring AI从云端API切换至本地vLLM服务时,出现请求体丢失的400错误{"object":"error","message":"[{'type': 'missing', 'loc': ('body',), 'msg': 'Field required', 'input': None}]","type":"Bad Request","param":。排查发现Spring AI
Spring AI 与 vLLM 交互踩坑记:HTTP/2 协议引发的“消失”的请求体
背景
最近在开发一个基于 Spring AI 的 Agent 功能。在开发阶段,我用的是 硅基流动的云端 API 进行调试,一切顺风顺水。
功能验证完毕后,我按计划将模型切换为本地部署的 vLLM。本以为只是改个 base-url 和 api-key 的事,结果却遇到了一个奇怪的bug。
问题初现
配置切换完成后,启动服务发起对话,控制台直接甩出了一行刺眼的 400 错误日志:
{
"object": "error",
"message": "[{'type': 'missing', 'loc': ('body',), 'msg': 'Field required', 'input': None}]",
"type": "Bad Request",
"param": null,
"code": 400
}
错误信息非常直白:Field required,位置在 body。换句话说,vLLM 认为我发过去的请求是空的,没有 Body!
这让我百思不得其解。我的代码仅仅是改了配置文件的地址,逻辑并未变动,怎么 Body 就凭空消失了?
排查之路:谁动了我的请求体?
为了找出真凶,我开始了一场类似“控制变量法”的排查过程:
1. 怀疑代码逻辑
首先,切回 硅基流动 的 API 进行测试。
- 结果:正常对话。
- 结论:Spring AI 的代码逻辑构建请求体(Prompt、Message 等)没有问题。
2. 怀疑模型服务端
既然代码没问题,难道是本地 vLLM 服务挂了或者接口不一样?我直接在服务器使用 curl 命令,手动构造 JSON 请求访问本地 vLLM。
- 结果:正常响应。
- 结论:vLLM 服务端本身工作正常,能正确解析标准 HTTP 请求。
3. 怀疑传输丢失
会不会是 Spring AI 发出的请求在传输过程中 Body 丢了?
为了验证这一点,我用 VibeCoding 快速生成了一个兼容 OpenAI 协议的 Mock 接口。让 Spring AI 调用这个 Mock 接口,并打印接收到的所有参数。
- 结果:Mock 接口完整打印出了 Body 内容。
- 结论:Spring AI 确实发送了 Body,数据并没有在客户端构建阶段丢失。但奇怪的是,Mock 服务能收到,vLLM 却收不到。
4. 怀疑框架兼容性
此时我怀疑是否是 Spring AI 底层封装的问题。
- 尝试方案 A:抛弃 Spring AI,换成 LangChain4j。
- 结果:依然报错,Body 丢失。
- 尝试方案 B:抛弃所有 AI 框架,直接用 Java 原生的
HttpClient手写请求。- 结果:成功了
这一步锁定了问题的范围:Spring AI 和 LangChain4j 的底层网络客户端与 vLLM 之间存在某种“沟通障碍”,而原生 HttpClient 在某种默认配置下避开了这个问题。
真相大白:HTTP/2 的锅
通过查阅 Spring AI 和 LangChain4j 的文档,发现它们在 Spring Boot 环境下通常默认使用 WebClient(基于 Reactor Netty 或 JDK Client 适配器)。
经过深入分析和向 AI 助手求证,线索指向了 HTTP/2。
- Spring 的 WebClient(尤其是配置了 JDK Connector 时)会默认尝试协商使用 HTTP/2 协议。
- 原生
HttpClient虽然也支持 HTTP/2,但如果不显式配置,有时在握手阶段的行为可能不同。 - 最关键的是,我去 vLLM 和 Spring AI 的 GitHub Issues 里搜了一圈,果然发现了类似的反馈 https://github.com/spring-projects/spring-ai/issues/2042 :vLLM 的服务端(基于 Uvicorn/FastAPI)在处理某些 HTTP/2 的
Upgrade请求或者 Frame 传输时,存在兼容性问题,导致无法正确读取 Request Body,从而被 Pydantic 校验拦截,报出body missing的错误。
解决方案
解决方案就很简单:强制客户端使用 HTTP/1.1。
在 Spring AI 中,我们可以通过自定义 WebClient.Builder 并配置 ClientHttpConnector 来实现这一点。
以下是修复后的配置代码:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.reactive.ClientHttpConnector;
import org.springframework.http.client.reactive.JdkClientHttpConnector;
import org.springframework.web.reactive.function.client.WebClient;
import java.net.http.HttpClient;
import java.time.Duration;
@Configuration
public class WebClientConfig {
@Bean
public WebClient.Builder webClientBuilder() {
// 强制指定 HTTP 版本为 HTTP_1_1
HttpClient httpClient = HttpClient.newBuilder()
.version(HttpClient.Version.HTTP_1_1)
.connectTimeout(Duration.ofSeconds(10))
.build();
// 使用 JDK 原生 HttpClient 作为底层连接器
ClientHttpConnector connector =
new JdkClientHttpConnector(httpClient);
return WebClient.builder()
.clientConnector(connector);
}
}
加上这个配置后,重启项目,再次调用本地 vLLM,久违的 AI 回复终于出现了
更多推荐


所有评论(0)