Spring AI中如何自定义ChatClient所使用的HTTP客户端?
我之前刚好踩过这个坑!一开始跟你一样,以为只要定义个CloseableHttpClient的Bean,Spring AI就会自动用上,结果完全没效果。后来研究了下才搞明白:Spring AI的ChatClient本身不直接处理HTTP请求,它是依赖底层的模型客户端(比如OpenAiClient、AzureOpenAiClient这类)来和AI服务通信的,而这些模型客户端默认用RestClient或者WebClient来发起请求。文档里说的“必须配置RestClient和WebClient”,其实就是要给这些模型客户端绑定你自定义的RestClient/WebClient Builder。
一、通过RestClient自定义HTTP客户端(以Apache HttpClient 5为例)
大部分AI模型默认用RestClient来通信,我们先讲这个场景的配置:
1. 配置自定义的Apache HttpClient和RestClient.Builder
首先创建一个RestClient.Builder的Bean,把自定义的Apache HttpClient绑定进去,同时可以加拦截器设置自定义请求头、超时时间这些:
@Configuration public class SpringAiHttpConfig { @Bean public RestClient.Builder restClientBuilder() { // 1. 配置Apache HttpClient 5,添加自定义拦截器、超时等规则 CloseableHttpClient customHttpClient = HttpClientBuilder.create() // 给所有AI请求加自定义头 .addInterceptorFirst((HttpRequestInterceptor) (request, context) -> { request.addHeader("X-Custom-Header", "my-custom-value"); request.addHeader("X-App-Id", "spring-ai-demo"); }) // 设置连接超时时间 .setConnectTimeout(Timeout.ofSeconds(15)) // 设置响应超时时间 .setResponseTimeout(Timeout.ofSeconds(45)) .build(); // 2. 把自定义HttpClient绑定到RestClient.Builder return RestClient.builder() .requestFactory(new HttpComponentsClientHttpRequestFactory(customHttpClient)); } }
2. 让模型客户端使用这个自定义的RestClient.Builder
接下来要确保你的AI模型客户端(比如OpenAiClient)用上这个自定义的Builder,比如OpenAI场景的配置:
@Bean public OpenAiClient openAiClient(RestClient.Builder restClientBuilder, OpenAiApi openAiApi) { // 用我们自定义的RestClient.Builder创建模型客户端 return new OpenAiClient(openAiApi, restClientBuilder); }
这样ChatClient依赖的OpenAiClient就会用你配置的HTTP客户端发请求,自定义的头和超时设置自然就生效了。
二、如果模型用的是WebClient(流式请求场景常用)
有些模型或者流式对话场景会用WebClient,这时候就需要配置WebClient.Builder的Bean:
@Configuration public class SpringAiHttpConfig { @Bean public WebClient.Builder webClientBuilder() { // 1. 配置Reactor HttpClient,设置超时规则 HttpClient reactorHttpClient = HttpClient.create() .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 15000) .doOnConnected(conn -> conn .addHandlerLast(new ReadTimeoutHandler(45)) .addHandlerLast(new WriteTimeoutHandler(45))); // 2. 绑定到WebClient.Builder,同时加拦截器设置自定义头 return WebClient.builder() .clientConnector(new ReactorClientHttpConnector(reactorHttpClient)) .filter((clientRequest, exchangeFunction) -> { ClientRequest modifiedRequest = ClientRequest.from(clientRequest) .header("X-Custom-Header", "webclient-custom-value") .build(); return exchangeFunction.exchange(modifiedRequest); }); } // 对应模型客户端的配置,比如Azure OpenAI用WebClient的场景: @Bean public AzureOpenAiClient azureOpenAiClient(WebClient.Builder webClientBuilder, AzureOpenAiApi azureOpenAiApi) { return new AzureOpenAiClient(azureOpenAiApi, webClientBuilder); } }
三、为什么直接定义CloseableHttpClient Bean没用?
这个我之前也疑惑了很久,后来才搞懂:Spring AI的模型客户端默认是自己构建RestClient/WebClient的,不会自动识别容器里单独的CloseableHttpClient。必须把你的自定义HttpClient绑定到RestClient.Builder或WebClient.Builder的Bean里,再让模型客户端使用这个Builder,才能把自定义配置传进去。
四、验证自定义是否生效
你可以把org.springframework.web.client.RestTemplate(用RestClient的场景)或者org.springframework.web.reactive.function.client.WebClient(用WebClient的场景)的日志级别设为DEBUG,就能看到请求里已经带上你加的自定义头,而且用的是你配置的Apache HttpClient了。




