SpringAI调用第三方模型增加自定义请求参数

在使用SpringAI时,会有调用第三方模型的需求,然而框架本身不支持开发者传入自定义请求参数,模型响应也就做不到高度自定义化。

在使用SpringAI做DouBao的模型接入时就遇到了该问题,DouBao虽然支持OpenAI格式,但是要控制模型是否思考时却不能定义相关参数,针对此问题,提出了一个临时的解决办法。

在SpringAI向 API 发送请求时进行拦截,拦截后将自定义的请求参数加上

该文档飞书文档地址:gx6ax5529hd.feishu.cn/wiki/ZaOAwG...

SpringAI version : 1.0.1

一、目标请求参数:

vbnet 复制代码
curl --location "https://ark.cn-beijing.volces.com/api/v3/chat/completions" \
--header "Authorization: Bearer $ARK_API_KEY" \
--header "Content-Type: application/json" \
--data '{
 "model": "doubao-seed-1.6-250615",
     "messages": [
         {
             "role": "user",
             "content": [
                 {
                     "type":"text",
                     "text":"我要研究深度思考模型与非深度思考模型区别的课题,体现出我的专业性"
                 }
             ]
         }
     ],
     "thinking":{
         "type":"disabled"
     }
}'

由于SpringAI-OpenAI的限制,无法在请求中加上"thinking"字段,所以在调用时模型会默认为思考模式这也就导致我们等待的响应时间要比其他模型长。

二、拦截请求:

具体拦截请求代码如下:

java 复制代码
 /**
* 当 RestClient 写出 OpenAiApi.ChatCompletionRequest 时拦截并注入额外字段(例如 "thinking")。
*/
public class ChatCompletionRequestHttpMessageConverter extends AbstractHttpMessageConverter<Object> {

    private static final Logger log = LoggerFactory.getLogger(ChatCompletionRequestHttpMessageConverter.class);

    private final ObjectMapper mapper;
    private final Function<OpenAiApi.ChatCompletionRequest, Map<String, Object>> extraParameter;

    public ChatCompletionRequestHttpMessageConverter(ObjectMapper mapper,
                                                     Function<OpenAiApi.ChatCompletionRequest, Map<String, Object>> extraParameter) {
        super(MediaType.APPLICATION_JSON);
        this.mapper = mapper;
        this.extraParameter = extraParameter;
    }

    @Override
    protected boolean supports(Class<?> clazz) {
        // 只对 OpenAiApi.ChatCompletionRequest 生效(也可以根据需要扩展)
        return OpenAiApi.ChatCompletionRequest.class.isAssignableFrom(clazz);
    }

    @Override
    protected Object readInternal(Class<?> clazz, HttpInputMessage inputMessage) throws IOException {
        // 反序列化交给 Jackson 来做(默认行为)
        return mapper.readValue(inputMessage.getBody(), clazz);
    }

    @Override
    protected void writeInternal(Object object, HttpOutputMessage outputMessage) throws IOException {
        if (object instanceof OpenAiApi.ChatCompletionRequest request) {
            // 把请求对象转为 JSON 树
            ObjectNode node = mapper.valueToTree(request);

            // 计算要注入的额外参数
            Map<String, Object> extras = extraParameter.apply(request);
            if (extras != null && !extras.isEmpty()) {
                node.set("thinking", mapper.valueToTree(extras));
            }

            // DEBUG: 打印最终请求体(仅用于调试,生产可改为 log.debug)
            String finalJson = mapper.writeValueAsString(node);
            log.info("[ChatCompletionRequestHttpMessageConverter] final request JSON: {}", finalJson);

            // 写出最终 JSON 到输出流
            mapper.writeValue(outputMessage.getBody(), node);
            return;
        }

        // 不太可能走到这里(因为 supports 限制),但以防万一 fallback:
        mapper.writeValue(outputMessage.getBody(), object);
    }
}

三、注入RestClient.Builder Bean

同时需要将ChatCompletionRequestHttpMessageConverter类注入到RestClient.BuilderBean中,同时设置请求超时时间为3分钟

java 复制代码
@Bean
@Primary
public RestClient.Builder customRestClientBuilder(ObjectMapper objectMapper) {
       ClientHttpRequestFactorySettings settings = ClientHttpRequestFactorySettings.defaults()
               .withConnectTimeout(Duration.ofMinutes(3))
               .withReadTimeout(Duration.ofMinutes(5));

       // 自定义 converter:注入 thinking: { type: "disabled" }
       ChatCompletionRequestHttpMessageConverter customConverter =
               new ChatCompletionRequestHttpMessageConverter(objectMapper,
                       request -> Map.of("type", "disabled"));

       return RestClient.builder()
               .requestFactory(ClientHttpRequestFactoryBuilder.reactor().build(settings))
               .messageConverters(converters -> {
                   // converters 是默认 converters 的 List,往最前面插入,确保优先匹配
                   converters.add(0, customConverter);

//                     (可选)你也可以打印当前 converters 顺序帮助调试
                    System.out.println("Converters: " + converters);
               });
   }

四、模型调用

在OpenAiApi中Builder进一个.restClientBuilder参数,传入(restClientBuilder类)

java 复制代码
private ChatClient getChatClient() {
         OpenAiChatModel chatModel = openAiChatModel.mutate()
                    .openAiApi(OpenAiApi.builder()
                            .restClientBuilder(restClientBuilder) // 在此注入
                            .baseUrl(apiHost)
                            .completionsPath(completionPath)
                            .apiKey(apiKey)
                            .build())
                    .defaultOptions(OpenAiChatOptions.builder()
                            .model(modelName)
                            .build())
                    .build();

            this.promptText = promptTemplateService.queryByDeptId(1L);
            return ChatClient.builder(chatModel)
                    .build();
               }

总结

上述只是临时的解决办法,需要完全解决还需要等到官方进行适配支持

同时SpringAI的Github上也有类似的issue,地址如下,大家可以去看看

github.com/spring-proj...

相关推荐
言慢行善30 分钟前
sqlserver模糊查询问题
java·数据库·sqlserver
专吃海绵宝宝菠萝屋的派大星35 分钟前
使用Dify对接自己开发的mcp
java·服务器·前端
大数据新鸟1 小时前
操作系统之虚拟内存
java·服务器·网络
Tong Z1 小时前
常见的限流算法和实现原理
java·开发语言
凭君语未可1 小时前
Java 中的实现类是什么
java·开发语言
He少年1 小时前
【基础知识、Skill、Rules和MCP案例介绍】
java·前端·python
克里斯蒂亚诺更新1 小时前
myeclipse的pojie
java·ide·myeclipse
迷藏4941 小时前
**eBPF实战进阶:从零构建网络流量监控与过滤系统**在现代云原生架构中,**网络可观测性**和**安全隔离**已成为
java·网络·python·云原生·架构
迷藏4941 小时前
**发散创新:基于Solid协议的Web3.0去中心化身份认证系统实战解析**在Web3.
java·python·web3·去中心化·区块链