本地vLLM部署大模型使用Langchain4J调用问题(非流式)

报错信息如下

java 复制代码
Caused by: dev.langchain4j.exception.HttpException: {"error":{"message":"1 validation error:\n  {'type': 'missing', 'loc': ('body',), 'msg': 'Field required', 'input': None}\n\n  File \"/Users/wangzhe112/workspace/workspace_my_python/ai-test/.venv/lib/python3.13/site-packages/vllm/entrypoints/utils.py\", line 478, in create_chat_completion\n    POST /v1/chat/completions [{'type': 'missing', 'loc': ('body',), 'msg': 'Field required', 'input': None}]","type":"Bad Request","param":null,"code":400}}
	at dev.langchain4j.http.client.jdk.JdkHttpClient.execute(JdkHttpClient.java:53)
	at dev.langchain4j.model.openai.internal.SyncRequestExecutor.execute(SyncRequestExecutor.java:20)
	at dev.langchain4j.model.openai.internal.RequestExecutor.executeRaw(RequestExecutor.java:44)
	at dev.langchain4j.model.openai.OpenAiChatModel.lambda$doChat$0(OpenAiChatModel.java:150)
	at dev.langchain4j.internal.ExceptionMapper.withExceptionMapper(ExceptionMapper.java:29)

版本

java=jdk17

langchain4j=1.8.0

python=3.13.0

vLLM=0.14.1

大模型=Qwen/Qwen3-4B

maven依赖

xml 复制代码
	<dependencyManagement>
		<dependencies>
			<!-- LangChain4j BOM (Bill of Materials) -->
			<!-- BOM只负责版本管理,不会实际引入依赖 -->
			<!-- 子模块需要显式声明所需的依赖,但可以省略版本号 -->
			<dependency>
				<groupId>dev.langchain4j</groupId>
				<artifactId>langchain4j-bom</artifactId>
				<version>1.8.0</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<!-- LangChain4j 核心库 -->
		<!-- 官方 langchain4j(包含 AiServices 等服务类) -->
		<dependency>
			<groupId>dev.langchain4j</groupId>
			<artifactId>langchain4j</artifactId>
		</dependency>
		<dependency>
			<groupId>dev.langchain4j</groupId>
			<artifactId>langchain4j-core</artifactId>
		</dependency>
		<!-- 通义千问模型支持 - 在langchain4j 1.x版本中使用 -->
		<dependency>
			<groupId>dev.langchain4j</groupId>
			<artifactId>langchain4j-open-ai</artifactId>
		</dependency>
		<dependency>
			<groupId>dev.langchain4j</groupId>
			<artifactId>langchain4j-community-dashscope</artifactId>
			<version>1.9.0-beta16</version>
		</dependency>
	</dependencies>

导致错误(未找到原因)

本地vLLM启动的qwen3-4B的大模型,使用python的langchain可以成功调用,但是用java的langchain4j的OpenAiChatModel就调用报错,同时使用postman调用也成功,经过多次尝试,发现请求体为空对象时,postman也会报同样的错误,那么问题就出在请求上。

经过多方搜索,奈何始终找不到对应的解答,AI也回答的不对,都是langchain4j 0.x版本的,1.x版本几乎没有,苦苦寻求无果。

解题思路

变换思路,langchain4j调用vLLM的openAI接口,使用的是langchain4j-http-client-jdk包中的类,其发送请求使用的是java原生的net包下的HttpClient,由于知识浅薄,很可能是兼容性问题吧,此包就3个类,那么我们自己替换这3个类,不用原生的了,换成apache的httpcomponents,OpenAiChatModel也开放了httpclient的替换,开干

代码奉上

仿照langchain4j-http-client-jdk,我手撸了HttpClient和HttpClientBuilder类,同时在调用时显示执行了httpclient类,请求就成功了,可喜可贺,虽然原因没找到,但问题解决了,有大神知道是啥原因不?希望大神指点迷津

代码如下,实验性的代码,如用在生产还需完善,见谅

java 复制代码
import dev.langchain4j.exception.HttpException;
import dev.langchain4j.exception.TimeoutException;
import dev.langchain4j.http.client.HttpClient;
import dev.langchain4j.http.client.HttpRequest;
import dev.langchain4j.http.client.SuccessfulHttpResponse;
import dev.langchain4j.http.client.sse.ServerSentEventListener;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.net.http.HttpTimeoutException;
import java.nio.charset.Charset;
import java.time.temporal.ChronoUnit;
import java.util.List;
import java.util.Map;

public class ComponentsHttpClient implements HttpClient {
    private final CloseableHttpClient delegate;
    private final Integer readTimeout;

    public ComponentsHttpClient(ComponentsHttpClientBuilder builder) {
        this.readTimeout = (int) builder.readTimeout().get(ChronoUnit.MILLIS);
        this.delegate = HttpClients.custom().build();
    }

    public static ComponentsHttpClientBuilder builder() {
        return new ComponentsHttpClientBuilder();
    }

    public SuccessfulHttpResponse execute(HttpRequest request) throws HttpException {
        try {
            HttpUriRequest httpUriRequest = this.toRequest(request);
            CloseableHttpResponse response = this.delegate.execute(httpUriRequest);
            // 判断返回状态是否为200
            int statusCode = response.getStatusLine().getStatusCode();
            String body = EntityUtils.toString(response.getEntity(), "UTF-8");
            if (!isSuccessful(response)) {
                throw new HttpException(statusCode, body);
            } else {
                return fromResponse(statusCode, body);
            }
        } catch (HttpTimeoutException var4) {
            throw new TimeoutException(var4);
        } catch (IOException var5) {
            throw new RuntimeException(var5);
        }
    }

    public void execute(HttpRequest request, dev.langchain4j.http.client.sse.ServerSentEventParser parser, ServerSentEventListener listener) {
        // 此方法为异步调用模型,由于我使用的是HttpComponents 4.x版本,本身不具备异步调用的能力,需要额外实现,故此处省略
        // 有此需求的则自行实现该方法
    }

    private HttpUriRequest toRequest(HttpRequest request) {
        Map<String, List<String>> headers = request.headers();
        if ("POST".equals(request.method().name())) {
            HttpPost httpPost = new HttpPost(request.url());
            if (!ObjectUtils.isEmpty(headers)) {
                for (String headerName : headers.keySet()) {
                    httpPost.setHeader(headerName, String.join(";",headers.get(headerName)));
                }
            }
            if (StringUtils.hasLength(request.body())){
                StringEntity entity = new StringEntity(request.body(), Charset.forName("UTF-8"));
                entity.setContentType("application/json");
                httpPost.setEntity(entity);
            }
            RequestConfig requestConfig =  RequestConfig.custom().setSocketTimeout(readTimeout).setConnectTimeout(readTimeout).build();
            httpPost.setConfig(requestConfig);
            return httpPost;
        }
        HttpGet httpGet = new HttpGet(request.url());
        if (!ObjectUtils.isEmpty(headers)) {
            for (String headerName : headers.keySet()) {
                httpGet.setHeader(headerName, String.join(";",headers.get(headerName)));
            }
        }
        RequestConfig requestConfig =  RequestConfig.custom().setSocketTimeout(readTimeout).setConnectTimeout(readTimeout).build();
        httpGet.setConfig(requestConfig);
        return httpGet;
    }

    private static SuccessfulHttpResponse fromResponse(Integer statusCode, String body) {
        return SuccessfulHttpResponse.builder().statusCode(statusCode).body(body).build();
    }

    private static boolean isSuccessful(CloseableHttpResponse response) {
        int statusCode = response.getStatusLine().getStatusCode();
        return statusCode >= 200 && statusCode < 300;
    }

}
java 复制代码
import dev.langchain4j.http.client.HttpClientBuilder;

import java.net.http.HttpClient;
import java.time.Duration;

public class ComponentsHttpClientBuilder implements HttpClientBuilder {
    private HttpClient.Builder httpClientBuilder;
    private Duration connectTimeout;
    private Duration readTimeout;

    public ComponentsHttpClientBuilder() {
    }

    public HttpClient.Builder httpClientBuilder() {
        return this.httpClientBuilder;
    }

    public ComponentsHttpClientBuilder httpClientBuilder(HttpClient.Builder httpClientBuilder) {
        this.httpClientBuilder = httpClientBuilder;
        return this;
    }

    public Duration connectTimeout() {
        return this.connectTimeout;
    }

    public ComponentsHttpClientBuilder connectTimeout(Duration connectTimeout) {
        this.connectTimeout = connectTimeout;
        return this;
    }

    public Duration readTimeout() {
        return this.readTimeout;
    }

    public ComponentsHttpClientBuilder readTimeout(Duration readTimeout) {
        this.readTimeout = readTimeout;
        return this;
    }

    public ComponentsHttpClient build() {
        return new ComponentsHttpClient(this);
    }
}
相关推荐
DongQiShanRen1 天前
裁决台账双向互校(中):五向一致性链——②向解读与③向实现
人工智能·深度学习·自然语言处理·集成学习·vllm
智码看视界1 天前
开源大模型每日追踪:小米 MiMo-V2.6-Pro,登顶开放权重第一(超过 GLM-5.3 、Kimi K3)
agent·vllm·moe·全模态·小米大模型·mimo-v2.6
云和数据.ChenGuang2 天前
JAVAEE AI工程师路线图
java·人工智能·java-ee·fastapi·springai·langchain4j
零基础1233 天前
DeepSeek V4.1 Flash (Batch) 的性能测试与应用
经验分享·python·语言模型·vllm
ZealSinger3 天前
LangChain4j用AiServices拼Tool
java·rag·ai智能体·langchain4j
梦帮科技4 天前
vLLM / TensorRT-LLM 极限推理:PagedAttention 细粒度物理页表管理与连续批处理(Continuous Batching)实战
数据结构·人工智能·分布式·python·深度学习·算法·vllm
可乐ea5 天前
vLLM 分离式推理实战指南:把 prefill 和 decode 拆开跑
ai智能体·vllm·kv缓存·大模型推理优化·分离式推理
小马9265 天前
【无标题】
vllm
小毕超7 天前
没有NVIDIA显卡,基于 OpenVINO_GenAI 框架在Intel集显GPU上也能流畅本地运行14B大模型
openvino·qwen3·openvino-genai
智码看视界8 天前
开源大模型追踪:腾讯混元Hy4preview,FP8 770GB 落地企业私有化,Apache 2.0 无字段限制
开源·vllm·开源大模型·apache2.0·fp8量化·腾讯混元hy4·agent推理