本地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);
    }
}
相关推荐
Shacoray1 天前
OpenClaw 接入阿里云百炼 Coding Plan 指南
阿里云·ai·云计算·qwen3·openclaw·coding plan
~kiss~2 天前
高性能大语言模型推理与服务框架(推理引擎)vLLM
人工智能·语言模型·vllm
hit56实验室2 天前
明明已经把vllm被强制kill了,但是仍然占用GPU显存
vllm
长路 ㅤ   2 天前
05、LangChain4j快速对接生图模型(百炼平台、智谱)
java开发·通义万相·智谱glm·langchain4j·ai生图
陈 洪 伟3 天前
大模型推理引擎vLLM(14): 什么是MLA多头潜在注意力
vllm·mla
陈 洪 伟4 天前
大模型推理引擎vLLM(12): vLLM Prefix Caching以及eviction的相关问题和代码
vllm·prefix caching
深刻如此4 天前
Qwen2.5-7B-Instruct实战教程:Chainlit集成WebSocket实时通信增强
大语言模型·文本生成·vllm·chainlit
长路 ㅤ   5 天前
快速了解VLLM推理引擎
模型部署·vllm·xinference·推理引擎·ai框架
长路 ㅤ   5 天前
Milvus系列之02、Spring+Milvus实现RAG检索增强
向量数据库·apache tika·langchain4j·知识库构建·hanlp分词
长路 ㅤ   5 天前
适配AI平台的HTTP插件系统设计
langchain4j·ai插件系统·http工具调用·语雀知识库·coze插件设计