文章目录
最新版官方版本
Spring AI 的1.0.0-M7 已解决 1.0.0-M6 版本及之前的 Spring AI 的 OpenAi Model 和阿里云百炼的部分接口存在兼容性问题,直接调用官方的OpenAiChatModel类就可以:
一、问题原因
SpringAI对接阿里云百炼平台时不支持stream流模式,function calling的stream模式返回的函数参数格式有问题,与open AI接口不兼容。
二、问题解析
在处理大模型返回的结果时,尝试从中解析function calling信息,但由于参数格式问题导致解析失败。
open AI官方要求buildGeneration()方法只返回一条完整的结果,通义千问模型返回多条残缺不全的message,参数被分成了多段返回,导致生成的toolCalls对象残缺不全,无法调用。
三、解决方案
思想:使用map reduce方法,将buildGeneration()方法中传入的多个残缺不全的toolCall合并成一个完整的toolCall对象。
具体实现:由于OpenAiChatModel类中buildGeneration()方法私有,无法重写,我们需要配置新建的 AlibabaOpenAiChatModel 类,替换原生的OpenAiChatModel类,再修改buildGeneration()方法。
1. 新建 AlibabaOpenAiChatModel 类
遵循阿里巴巴百炼平台接口规范的ChatModel,其中大部分代码来自SpringAI的OpenAiChatModel,只需要重写接口协议不匹配的地方。
package com.itheima.ai.model;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.MessageType;
import org.springframework.ai.chat.messages.ToolResponseMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.metadata.*;
import org.springframework.ai.chat.model.*;
import org.springframework.ai.chat.observation.ChatModelObservationContext;
import org.springframework.ai.chat.observation.ChatModelObservationConvention;
import org.springframework.ai.chat.observation.ChatModelObservationDocumentation;
import org.springframework.ai.chat.observation.DefaultChatModelObservationConvention;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.model.Media;
import org.springframework.ai.model.ModelOptionsUtils;
import org.springframework.ai.model.function.FunctionCallback;
import org.springframework.ai.model.function.FunctionCallbackResolver;
import org.springframework.ai.model.function.FunctionCallingOptions;
import org.springframework.ai.model.tool.LegacyToolCallingManager;
import org.springframework.ai.model.tool.ToolCallingChatOptions;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.tool.ToolExecutionResult;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.openai.api.OpenAiApi;
import org.springframework.ai.openai.api.common.OpenAiApiConstants;
import org.springframework.ai.openai.metadata.support.OpenAiResponseHeaderExtractor;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.tool.definition.ToolDefinition;
import org.springframework.core.io.ByteArrayResource;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.lang.Nullable;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.*;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
public class AlibabaOpenAiChatModel extends AbstractToolCallSupport implements ChatModel {
private static final Logger logger = LoggerFactory.getLogger(AlibabaOpenAiChatModel.class);
private static final ChatModelObservationConvention DEFAULT_OBSERVATION_CONVENTION = new DefaultChatModelObservationConvention();
private static final ToolCallingManager DEFAULT_TOOL_CALLING_MANAGER = ToolCallingManager.builder().build();
/**
* The default options used for the chat completion requests.
*/
private final OpenAiChatOptions defaultOptions;
/**
* The retry template used to retry the OpenAI API calls.
*/
private final RetryTemplate retryTemplate;
/**
* Low-level access to the OpenAI API.
*/
private final OpenAiApi openAiApi;
/**
* Observation registry used for instrumentation.
*/
private final ObservationRegistry observationRegistry;
private final ToolCallingManager toolCallingManager;
/**
* Conventions to use for generating observations.
*/
private ChatModelObservationConvention observationConvention = DEFAULT_OBSERVATION_CONVENTION;
/**
* Creates an instance of the AlibabaOpenAiChatModel.
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
* Chat API.
* @throws IllegalArgumentException if openAiApi is null
* @deprecated Use AlibabaOpenAiChatModel.Builder.
*/
@Deprecated
public AlibabaOpenAiChatModel(OpenAiApi openAiApi) {
this(openAiApi, OpenAiChatOptions.builder().model(OpenAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build());
}
/**
* Initializes an instance of the AlibabaOpenAiChatModel.
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
* Chat API.
* @param options The OpenAiChatOptions to configure the chat model.
* @deprecated Use AlibabaOpenAiChatModel.Builder.
*/
@Deprecated
public AlibabaOpenAiChatModel(OpenAiApi openAiApi, OpenAiChatOptions options) {
this(openAiApi, options, null, RetryUtils.DEFAULT_RETRY_TEMPLATE);
}
/**
* Initializes a new instance of the AlibabaOpenAiChatModel.
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
* Chat API.
* @param options The OpenAiChatOptions to configure the chat model.
* @param functionCallbackResolver The function callback resolver.
* @param retryTemplate The retry template.
* @deprecated Use AlibabaOpenAiChatModel.Builder.
*/
@Deprecated
public AlibabaOpenAiChatModel(OpenAiApi openAiApi, OpenAiChatOptions options,
@Nullable FunctionCallbackResolver functionCallbackResolver, RetryTemplate retryTemplate) {
this(openAiApi, options, functionCallbackResolver, List.of(), retryTemplate);
}
/**
* Initializes a new instance of the AlibabaOpenAiChatModel.
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
* Chat API.
* @param options The OpenAiChatOptions to configure the chat model.
* @param functionCallbackResolver The function callback resolver.
* @param toolFunctionCallbacks The tool function callbacks.
* @param retryTemplate The retry template.
* @deprecated Use AlibabaOpenAiChatModel.Builder.
*/
@Deprecated
public AlibabaOpenAiChatModel(OpenAiApi openAiApi, OpenAiChatOptions options,
@Nullable FunctionCallbackResolver functionCallbackResolver,
@Nullable List<FunctionCallback> toolFunctionCallbacks, RetryTemplate retryTemplate) {
this(openAiApi, options, functionCallbackResolver, toolFunctionCallbacks, retryTemplate,
ObservationRegistry.NOOP);
}
/**
* Initializes a new instance of the AlibabaOpenAiChatModel.
* @param openAiApi The OpenAiApi instance to be used for interacting with the OpenAI
* Chat API.
* @param options The OpenAiChatOptions to configure the chat model.
* @param functionCallbackResolver The function callback resolver.
* @param toolFunctionCallbacks The tool function callbacks.
* @param retryTemplate The retry template.
* @param observationRegistry The ObservationRegistry used for instrumentation.
* @deprecated Use AlibabaOpenAiChatModel.Builder or AlibabaOpenAiChatModel(OpenAiApi,
* OpenAiChatOptions, ToolCallingManager, RetryTemplate, ObservationRegistry).
*/
@Deprecated
public AlibabaOpenAiChatModel(OpenAiApi openAiApi, OpenAiChatOptions options,
@Nullable FunctionCallbackResolver functionCallbackResolver,
@Nullable List<FunctionCallback> toolFunctionCallbacks, RetryTemplate retryTemplate,
ObservationRegistry observationRegistry) {
this(openAiApi, options,
LegacyToolCallingManager.builder()
.functionCallbackResolver(functionCallbackResolver)
.functionCallbacks(toolFunctionCallbacks)
.build(),
retryTemplate, observationRegistry);
logger.warn("This constructor is deprecated and will be removed in the next milestone. "
+ "Please use the AlibabaOpenAiChatModel.Builder or the new constructor accepting ToolCallingManager instead.");
}
public AlibabaOpenAiChatModel(OpenAiApi openAiApi, OpenAiChatOptions defaultOptions, ToolCallingManager toolCallingManager,
RetryTemplate retryTemplate, ObservationRegistry observationRegistry) {
// We do not pass the 'defaultOptions' to the AbstractToolSupport,
// because it modifies them. We are using ToolCallingManager instead,
// so we just pass empty options here.
super(null, OpenAiChatOptions.builder().build(), List.of());
Assert.notNull(openAiApi, "openAiApi cannot be null");
Assert.notNull(defaultOptions, "defaultOptions cannot be null");
Assert.notNull(toolCallingManager, "toolCallingManager cannot be null");
Assert.notNull(retryTemplate, "retryTemplate cannot be null");
Assert.notNull(observationRegistry, "observationRegistry cannot be null");
this.openAiApi = openAiApi;
this.defaultOptions = defaultOptions;
this.toolCallingManager = toolCallingManager;
this.retryTemplate = retryTemplate;
this.observationRegistry = observationRegistry;
}
@Override
public ChatResponse call(Prompt prompt) {
// Before moving any further, build the final request Prompt,
// merging runtime and default options.
Prompt requestPrompt = buildRequestPrompt(prompt);
return this.internalCall(requestPrompt, null);
}
public ChatResponse internalCall(Prompt prompt, ChatResponse previousChatResponse) {
OpenAiApi.ChatCompletionRequest request = createRequest(prompt, false);
ChatModelObservationContext observationContext = ChatModelObservationContext.builder()
.prompt(prompt)
.provider(OpenAiApiConstants.PROVIDER_NAME)
.requestOptions(prompt.getOptions())
.build();
ChatResponse response = ChatModelObservationDocumentation.CHAT_MODEL_OPERATION
.observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
this.observationRegistry)
.observe(() -> {
ResponseEntity<OpenAiApi.ChatCompletion> completionEntity = this.retryTemplate
.execute(ctx -> this.openAiApi.chatCompletionEntity(request, getAdditionalHttpHeaders(prompt)));
var chatCompletion = completionEntity.getBody();
if (chatCompletion == null) {
logger.warn("No chat completion returned for prompt: {}", prompt);
return new ChatResponse(List.of());
}
List<OpenAiApi.ChatCompletion.Choice> choices = chatCompletion.choices();
if (choices == null) {
logger.warn("No choices returned for prompt: {}", prompt);
return new ChatResponse(List.of());
}
List<Generation> generations = choices.stream(

最低0.47元/天 解锁文章
2123

被折叠的 条评论
为什么被折叠?



