一、使用方法
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
对象的创建过程如下:
- 在
ObservationRegistry
中,采用多态的方式,创建一个实现类对象 - 调用
NoopObservationRegistry
类的构造方法,将INSTANCE
传入ObservationConfig()
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
接口里面有几大类:
- defaultAdvisors:Advisor相关配置
- defaultOptions:会话选项配置
- defaultUser:用户prompt配置
- defaultSystem:系统prompt配置
- defaultTools:工具调用配置
- defaultToolContext:工具文本描述配置
三、链式调用
ChatClient.builder(dashscopeChatModel) // 步骤①:返回 Builder 对象
.defaultSystem(SYSTEM_PROMPT) // 步骤②:Builder 类的方法
.defaultAdvisors(...) // 步骤③:Builder 类的方法
.build(); // 步骤④:Builder 类的 build() 方法