大模型专栏--Spring AI Alibaba Chat 源码分析

Chat 功能是 LLMs 应用的最基础功能,任何功能都要向 LLMs 应用给输入,之后将获得 LLMs 的返回。其底层是 NLP 的实现。这篇文章中不会分析 LLMs 的底层实现,会分析一波 Spring AI Albaba 的 Chat 功能实现和 Spring AI 提供的 Chat 接口实现。

Spring AI Chat 接口定义

从之前的 Spring AI Alibaba 使用示例中,我们可以看到,Spring AI 提供了两种 Chat Model 的实现,一种是比较低级的 ChatModel API,一种是高级的 ChatClient API。

其两者的区别是:ChatModel 是针对于各个模型的客户端,而 ChatClient 是屏蔽底层模型差异性的客户端接口,基于 ChatClient,Spring AI 提供了强大的 Advisors 机制来扩展功能。Advisors API 的实现类似于 Spring 的 AOP,应用了 AOP 的编程思想。

ChatModel API

下面以 Spring AI Alibaba 的 DashScope ChatModel 为例来了解一下 ChatModel 的源码设计。

在这里插入图片描述

从 IDEA 生成的关系图来看:DashScopeChatModel 继承了 AbstractToolCallSupport 实现了 ChatModel 接口。其中 AbstractToolCallSupport 实现了 工具函数调用的 抽象逻辑,ChatModel 提供了 call() 和 stream() 方法,用来和 LLMs 进行交互。从四个维度来理解一下 ChatModel 做的事情:

  1. Options,模型配置参数的处理;
  2. call 和 stream 的调用;
  3. Function 处理;
  4. 可观测处理。
Options 处理

在 Spring AI 中,将大模型的一些参数抽象成了 Options 的概念,其类结构如下图:

在这里插入图片描述

在 Spring AI Alibaba 中,对 ChatOptions 的参数支持在 application.yml 中指定,也支持在代码中通过 DashScopeChatOptions 提供的 Builder 方法进行构建:

在 Spring AI Alibaba DascopeChatModel#createRequest 方法中:

DashScopeChatOptions options = DashScopeChatOptions.builder().build();
if (prompt.getOptions() != null) {
   
   
    DashScopeChatOptions updatedRuntimeOptions = ModelOptionsUtils.copyToTarget(prompt.getOptions(),
          ChatOptions.class, DashScopeChatOptions.class);

    enabledToolsToUse.addAll(this.runtimeFunctionCallbackConfigurations(updatedRuntimeOptions));
    options = ModelOptionsUtils.merge(updatedRuntimeOptions, options, DashScopeChatOptions.class);
}

if (!CollectionUtils.isEmpty(this.defaultOptions.getFunctions())) {
   
   
    enabledToolsToUse.addAll(this.defaultOptions.getFunctions());
}

options = ModelOptionsUtils.merge(options, this.defaultOptions, DashScopeChatOptions.class);

if (!CollectionUtils.isEmpty(enabledToolsToUse)) {
   
   
    options.setTools(this.getFunctionTools(enabledToolsToUse));
}

对 模型调用参数进行了一系列的 merge 操作。以通过代码设置的 options 为准,其次是以 application.yml 中配置的 options 参数。也就是说,通过代码设置的参数优先级高于配置文件。

Call 和 Stream 方法

在之前的版本中,Spring AI Alibaba 通过 SDK 来适配通义大模型,现在都使用 openapi 规范的接口去调用大模型。

在 ChatModel 中,定义了 Call 和 Stream 方法,用来调用大模型,前者是朴素的调用方式,后者是流式调用。

在 Model 接口中定义了 Call 方法:

public interface Model<TReq extends ModelRequest<?>, TRes extends ModelResponse<?>> {
   
   

	TRes call(TReq request);
}

在 StreamingModel 中定义了 stream 方法:

public interface StreamingModel<TReq extends ModelRequest<?>, TResChunk extends ModelResponse<?>> {
   
   

	Flux<TResChunk> stream(TReq request);
}

通过代码可以看到在默认的 ChatModel 中组合了 Model 和 StreamingModel:

public interface ChatModel extends Model<Prompt, ChatResponse>, StreamingChatModel {
   
   

    @Override
    ChatResponse call(Prompt prompt);

    ChatOptions getDefaultOptions();

    default Flux<ChatResponse> stream(Prompt prompt) {
   
   
       throw new UnsupportedOperationException("streaming is not supported");
    }
}

有了接口,且适配了不同模型之后,通过 createRequest 构造一个 DashScopeApi#ChatCompletionRequest 的 request 对象,用于发起调用:()其中包含对 options 参数的处理和对 function 的处理)

# 构建请求参数
DashScopeApi.ChatCompletionRequest request = createRequest(prompt, false);

# 调用 LLMs API
ResponseEntity<ChatCompletion> completionEntity = this.retryTemplate
    .execute(ctx -> this.dashscopeApi.chatCompletionEntity(request));

# 准备接受 LLMs 响应
var chatCompletion = completionEntity.getBody();

在对 stream 方法的调用中,和 call 相同,细心的同学们,肯定发现了 createRequest 方法有两个参数,第一个是 prompt,第二个就是决定此次请求是否是流式调用:

ChatCompletionRequest request = createRequest(prompt, true);

继而,再去调用 dashScopeApi 的 chatCompletionStream 方法完成请求。最后接受响应。

至此我们已经完成了对 call 和 stream 请求方式的解析,从源码实现上非常简单。现在来分析一下 Spring AI 中定义的用来接受模型响应的类 ChatResponse:

在这里插入图片描述

其中元数据接口专注于提供 AI 模型生成结果的更多背景信息和见解。它可能包括计算时间、模型版本和 Usage 等其他相关详细信息,以增强对各种应用中的 AI 模型输出的理解和管理。

Generation 类是一个记录 AI 返回信息的实体类。有两个类成员:一个为 ChatGenerationMetadata,一个是 AssistantMessage。

其中 AssistantMessage 是让 LLMs 知晓此类型的消息类型是作为用户响应生成的。实现了 Message 和 Content 类型:类图如下:

在这里插入图片描述

我们可以看到有四种类型的实现,这里我们不讨论实现,后续文章会介绍。

至此便是整个 Spring AI 的 ChatReponse 的源码实现,可以看到 Spring AI 定义了一系列的接口来约束实现类的行为。使得整个 Spring AI 体系逻辑更加严密。

Function 处理

在 LLMs 中为了解决实时性的问题,提供了函数调用的方式,可以调用外部的一些工具函数来补充自身知识,给用户正确的响应。在 Spring AI 中也提供了对函数调用的集成。

下面以 DashScope 的 ChatModel 中的 Function 实现来分析一波具体的实现。在 IDEA 中关系图如下:

在这里插入图片描述

我们可以看到 Spring AI 主要通过 AbstractToolCallSupport 这个抽象类来完成整个函数调用的工具链抽象。通过构造组合相关的类,其中类说明如下:

  1. AbstractToolCallSupport:(此类已经经过好几轮迭代,不知道后续会不会在变动,之前的名字是 AbstractFunctionCallSupport)其中维护一组 Map 集合的 FunctionCallback 对象,提供用于处理函数回调和运行函数的功能;

  2. FunctionCallback:接口实现,内部定义了一个 Function 必须要的一些属性和方法,如 Name,Desc 等;

    public interface FunctionCallback {
         
         
        String getName();
        String getDescription();
        String getInputTypeSchema();
        String call(String functionInput);
    }
    
  3. FunctionCallbackContext:实现 Spring 的 ApplicationContextAware 接口,从 Spring context 中获取 Function Bean;

  4. AbstractFunctionCallback:承上启下,对大模型需要转换为大模型需要的函数调用协议,对于3rd,需要包装调用第三方服务或者函数。在使用上对该类是无感知的;

  5. FunctionCallbackWrapper:将输出转换为模型可以使用的格式,默认实现是将输出发送给模型之前将其转换为字符串。可以提供一个自定义的函数responseConverter实现来覆盖此操作。

此小节中,将从以下几个方面来分析:

  1. 函数是如何定义以及如何被 LLMs 感知到的?
  2. Spring AI 在调用 LLMs 时对 F
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值