Spring AI源码解读(一):ChatClient

一、使用方法

ChatClient是通过ChatClient.Builder对象创建的。可以使用类似SpringBoot中的Bean注入的方式自动创建ChatClient.Builder实例对象。

// 方式1:使用构造器注入
@RestController
class MyController {

    private final ChatClient chatClient;

    public MyController(ChatClient.Builder chatClientBuilder) {
        this.chatClient = chatClientBuilder.build();
    }

    @GetMapping("/ai")
    String generation(String userInput) {
        return this.chatClient.prompt()
            .user(userInput)
            .call() // 向AI发送请求
            .content(); // 返回AI结果
    }
}

// 方式2:使用建造者模式
ChatClient chatClient = ChatClient.builder(chatModel)
	.build();

小弟比较推荐使用方式2来构建chatClient对象

public class APP{
    public App(ChatModel dashscopeChatModel) {
        // 初始化基于文件的对话记忆
        String fileDir = System.getProperty("user.dir") + "/tmp/chat-memory";
        ChatMemory chatMemory = new FileBasedChatMemory(fileDir);
    
        chatClient = ChatClient.builder(dashscopeChatModel)
                .defaultSystem(SYSTEM_PROMPT)
                .defaultAdvisors(
                        new MessageChatMemoryAdvisor(chatMemory),
                        // 自定义日志拦截器,可按需开启
                        new MyLoggerAdvisor()
                )
                .build();
    }
}

在构建ChatClient实例的时候,构造函数参数 ChatModel dashscopeChatModel被自动注入,Spring AI会自动根据 spring.ai.dashscope 配置项的存在,激活 DashScope 相关的自动配置,使用配置的 API Key 创建 DashScope 客户端,将 ChatModel 实现类注册为 Spring Bean。Spring 的依赖注入机制按名称匹配,自动注入到构造函数参数中。

spring:
  ai:
    dashscope:
      api-key: 模型的API key
      chat:
        options:
          model: qwen-plus

二、总体结构

ChatClient是一个接口,我们可以看到他大致有下面几类方法:

静态方法

1. create()方法

public interface ChatClient {
    // 调用第二个create方法
    static ChatClient create(ChatModel chatModel) {
        return create(chatModel, ObservationRegistry.NOOP);
    }
    
    // 调用第三个create方法
    static ChatClient create(ChatModel chatModel, ObservationRegistry observationRegistry) {
        return create(chatModel, observationRegistry, (ChatClientObservationConvention)null);
    }
    
    // 断言检测,然后采用builder.build()建造
    static ChatClient create(ChatModel chatModel, ObservationRegistry observationRegistry, @Nullable ChatClientObservationConvention observationConvention) {
        Assert.notNull(chatModel, "chatModel cannot be null");
        Assert.notNull(observationRegistry, "observationRegistry cannot be null");
        return builder(chatModel, observationRegistry, observationConvention).build();
    }
}

其实本质上.create()方法就是使用.builder().build()方法进行构造。

1.1. ObservationRegistry

其有3个实现类,DelegatingObservationRegistry是psf类型的

ObservationRegistry.NOOP对象的创建过程如下:

  1. ObservationRegistry中,采用多态的方式,创建一个实现类对象
  2. 调用NoopObservationRegistry类的构造方法,将INSTANCE传入ObservationConfig()
  3. INSTANCE只是一个NoopObservationConfig()的空对象而已。

所以ObservationRegistry.NOOP仅仅是一个空的NoopObservationConfig()的占位对象而已。NoopObservationConfig()ObservationRegistry类的一个嵌套类,负责申明配置的。

// ObservationRegistry.class
public interface ObservationRegistry {
    // 采用多态的方式,创建一个实现类对象
    ObservationRegistry NOOP = new NoopObservationRegistry();
    
    static ObservationRegistry create() {
        return new SimpleObservationRegistry();
    }
    ......

    public static class ObservationConfig {
        ......
        public ObservationConfig() {
        }

        ......

// NoopObservationRegistry.class,final类,不能被继承和扩展
final class NoopObservationRegistry implements ObservationRegistry {
	static final ObservationRegistry FOR_SCOPES = ObservationRegistry.create();
    private final ObservationRegistry.ObservationConfig observationConfig;
    NoopObservationRegistry() {
        this.observationConfig = NoopObservationConfig.INSTANCE;
    }

    ......

// NoopObservationConfig.class
final class NoopObservationConfig extends ObservationRegistry.ObservationConfig {
    static final NoopObservationConfig INSTANCE = new NoopObservationConfig();

    private NoopObservationConfig() {
    }
    
    ......
1.2. ChatClientObservationConvention:

传入对话的上下文

2. builder()方法

public interface ChatClient {
    // 调用第二个builder方法
    static Builder builder(ChatModel chatModel) {
        return builder(chatModel, ObservationRegistry.NOOP, (ChatClientObservationConvention)null);
    }
    
    // 
    static Builder builder(ChatModel chatModel, ObservationRegistry observationRegistry, @Nullable ChatClientObservationConvention customObservationConvention) {
        Assert.notNull(chatModel, "chatModel cannot be null");
        Assert.notNull(observationRegistry, "observationRegistry cannot be null");
        return new DefaultChatClientBuilder(chatModel, observationRegistry, customObservationConvention);
    }
    // 嵌套接口
    public interface Builder {
    }
}

// DefaultChatClientBuilder.class
public class DefaultChatClientBuilder implements ChatClient.Builder {
    protected final DefaultChatClient.DefaultChatClientRequestSpec defaultRequest;

    DefaultChatClientBuilder(ChatModel chatModel) {
        this(chatModel, ObservationRegistry.NOOP, (ChatClientObservationConvention)null);
    }

    public DefaultChatClientBuilder(ChatModel chatModel, ObservationRegistry observationRegistry, @Nullable ChatClientObservationConvention customObservationConvention) {
        Assert.notNull(chatModel, "the " + ChatModel.class.getName() + " must be non-null");
        Assert.notNull(observationRegistry, "the " + ObservationRegistry.class.getName() + " must be non-null");
        this.defaultRequest = new DefaultChatClient.DefaultChatClientRequestSpec(chatModel, (String)null, Map.of(), (String)null, Map.of(), List.of(), List.of(), List.of(), List.of(), (ChatOptions)null, List.of(), Map.of(), observationRegistry, customObservationConvention, Map.of());
    }

// DefaultChatClient.class
public class DefaultChatClient implements ChatClient {
	public DefaultChatClientRequestSpec(final ChatModel chatModel, @Nullable String userText, Map<String, Object> userParams, @Nullable String systemText, Map<String, Object> systemParams, List<FunctionCallback> functionCallbacks, List<Message> messages, List<String> functionNames, List<Media> media, @Nullable ChatOptions chatOptions, List<Advisor> advisors, Map<String, Object> advisorParams, ObservationRegistry observationRegistry, @Nullable ChatClientObservationConvention customObservationConvention, Map<String, Object> toolContext) {
        // 添加各种配置:model、advisors等
        ......
}

Builder()接口

    public interface Builder {
        Builder defaultAdvisors(Advisor... advisor);

        Builder defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer);

        Builder defaultAdvisors(List<Advisor> advisors);

        Builder defaultOptions(ChatOptions chatOptions);

        Builder defaultUser(String text);

        Builder defaultUser(Resource text, Charset charset);

        Builder defaultUser(Resource text);

        Builder defaultUser(Consumer<PromptUserSpec> userSpecConsumer);

        Builder defaultSystem(String text);

        Builder defaultSystem(Resource text, Charset charset);

        Builder defaultSystem(Resource text);

        Builder defaultSystem(Consumer<PromptSystemSpec> systemSpecConsumer);

        Builder defaultTools(String... toolNames);

        Builder defaultTools(FunctionCallback... toolCallbacks);

        Builder defaultTools(List<ToolCallback> toolCallbacks);

        Builder defaultTools(Object... toolObjects);

        Builder defaultTools(ToolCallbackProvider... toolCallbackProviders);

        /** @deprecated */
        @Deprecated
        Builder defaultFunctions(String... functionNames);

        /** @deprecated */
        @Deprecated
        Builder defaultFunctions(FunctionCallback... functionCallbacks);

        Builder defaultToolContext(Map<String, Object> toolContext);

        Builder clone();

        ChatClient build();
    }

Builder接口里面有几大类:

  1. defaultAdvisors:Advisor相关配置
  2. defaultOptions:会话选项配置
  3. defaultUser:用户prompt配置
  4. defaultSystem:系统prompt配置
  5. defaultTools:工具调用配置
  6. defaultToolContext:工具文本描述配置

三、链式调用

ChatClient.builder(dashscopeChatModel)  // 步骤①:返回 Builder 对象
         .defaultSystem(SYSTEM_PROMPT)  // 步骤②:Builder 类的方法
         .defaultAdvisors(...)          // 步骤③:Builder 类的方法
         .build();                      // 步骤④:Builder 类的 build() 方法
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小火汁猛猛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值