Spring AI之ChatClient源码分析
简介
ChatClient 是一个接口,它定义了一个与聊天服务交互的客户端。这个接口主要用于创建聊天客户端对象,设置请求规范,以及发起聊天请求。以下是接口中各个方法的详细解释:
静态方法:
create(ChatModel chatModel):创建一个 ChatClient 实例,使用给定的 ChatModel 对象作为基础。
builder(ChatModel chatModel):返回一个 ChatClient.Builder,允许用户构建自定义的 ChatClient 实例,基于给定的 ChatModel。
非静态方法:
prompt():返回一个 ChatClientRequestSpec,用于设置聊天请求的规范,如提示信息等。
prompt(Prompt prompt):设置聊天请求规范中的提示信息,参数 prompt 不能为 null。
内部接口:
PromptUserSpec 和 PromptSystemSpec:分别用于配置用户提示和系统提示的详细信息,如文本、参数、媒体资源等。
AdvisorSpec:用于设置请求响应的顾问对象,可以添加参数和顾问列表。
CallResponseSpec 和 StreamResponseSpec:分别表示调用响应和流式响应的规格,提供了获取聊天响应内容的方法。
Builder 接口:
Builder:提供了一种构建 ChatClient 对象的方式,允许设置默认的顾问、选项、用户和系统提示等。
默认方法(已过时):
call(String message) 和 call(Message... messages):这些方法已过时,用于发起基于单个消息或多个消息的聊天请求,返回第一代的输出内容。
call(Prompt prompt):过时的方法,用于根据 Prompt 对象发起聊天请求,返回 ChatResponse 对象。
总的来说,ChatClient 接口提供了构建和配置聊天客户端对象的灵活性,以及发起和处理聊天请求的能力。用户可以通过 ChatClient.Builder 来定制客户端的行为,然后使用 prompt() 和 prompt(Prompt prompt) 方法设置请求规范,最后通过 call() 方法发起聊天请求。
源码详解
package org.springframework.ai.chat.client;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import reactor.core.publisher.Flux;
import org.springframework.ai.chat.messages.Media;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.converter.StructuredOutputConverter;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.core.io.Resource;
import org.springframework.util.MimeType;
/**
* Client to perform stateless requests to an AI Model, using a fluent API.
*
* Use {@link ChatClient#builder(ChatModel)} to prepare an instance.
*
* @author Mark Pollack
* @author Christian Tzolov
* @author Josh Long
* @author Arjen Poutsma
* @since 1.0.0
*/
public interface ChatClient {
/**
* 根据传入的ChatModel创建一个ChatClient对象
*
* @param chatModel ChatModel对象,用于创建ChatClient
* @return ChatClient对象
*/
static ChatClient create(ChatModel chatModel) {
return builder(chatModel).build();
}
/**
* 返回一个根据ChatModel对象创建的ChatClient.Builder对象
*
* @param chatModel ChatModel对象,用于构建ChatClient
* @return ChatClient.Builder对象
*/
static Builder builder(ChatModel chatModel) {
return new DefaultChatClientBuilder(chatModel);
}
/**
* 提示ChatClientRequestSpec。
*
* @return 返回ChatClientRequestSpec对象,用于设置请求规范。
*/
ChatClientRequestSpec prompt();
/**
* 设置聊天客户端请求规范中的提示信息。
*
* @param prompt 聊天提示信息对象,不能为null。
* @return 返回聊天客户端请求规范对象,用于设置聊天请求规范。
* @throws NullPointerException 如果传入的prompt为null,则抛出NullPointerException异常。
*/
ChatClientPromptRequestSpec prompt(Prompt prompt);
/**
* 返回一个用于创建新的 {@link ChatClient} 的 {@link ChatClient.Builder},其设置将复制自当前客户端的默认 {@link ChatClientRequestSpec}。
*
* @return 返回一个用于创建新的 {@link ChatClient} 的 {@link ChatClient.Builder}
*/
Builder mutate();
interface PromptUserSpec {
/**
* 设置文本内容并返回 PromptUserSpec 对象,用于继续配置 PromptUser。
*
* @param text 文本内容
* @return PromptUserSpec 对象,用于继续配置 PromptUser
*/
PromptUserSpec text(String text);
/**
* 设置提示用户时的文本内容,并返回PromptUserSpec对象以继续配置。
*
* @param text 文本资源
* @param charset 文本内容的字符集
* @return PromptUserSpec对象,用于继续配置提示用户
*/
PromptUserSpec text(Resource text, Charset charset);
/**
* 设置提示用户时的文本内容,并返回PromptUserSpec对象以继续配置。
*
* @param text 文本资源,包含提示用户时需要显示的文本内容
* @return PromptUserSpec对象,用于继续配置提示用户
*/
PromptUserSpec text(Resource text);
/**
* 设置提示用户时的参数,并返回PromptUserSpec对象以继续配置。
*
* @param p 包含参数的Map对象,其中key为参数名,value为参数值
* @return PromptUserSpec对象,用于继续配置提示用户
*/
PromptUserSpec params(Map<String, Object> p);
/**
* 设置提示用户时的参数。
*
* @param k 参数名
* @param v 参数值
* @return PromptUserSpec对象,用于继续配置提示用户
*/
PromptUserSpec param(String k, Object v);
/**
* 设置提示用户时显示的媒体资源,并返回PromptUserSpec对象以继续配置。
*
* @param media 一个或多个媒体资源对象,用于在提示用户时显示
* @return PromptUserSpec对象,用于继续配置提示用户
*/
PromptUserSpec media(Media... media);
/**
* 设置提示用户时显示的媒体资源的MIME类型和URL地址,并返回PromptUserSpec对象以继续配置。
*
* @param mimeType 媒体资源的MIME类型
* @param url 媒体资源的URL地址
* @return PromptUserSpec对象,用于继续配置提示用户
*/
PromptUserSpec media(MimeType mimeType, URL url);
/**
* 设置提示用户时显示的媒体资源的MIME类型和资源对象,并返回PromptUserSpec对象以继续配置。
*
* @param mimeType 媒体资源的MIME类型
* @param resource 媒体资源的Resource对象
* @return PromptUserSpec对象,用于继续配置提示用户
*/
PromptUserSpec media(MimeType mimeType, Resource resource);
/**
* 返回一个包含所有媒体对象的列表。
*
* @return 包含所有媒体对象的列表
*/
List<Media> media();
}
interface PromptSystemSpec {
/**
* 设置提示系统的文本内容。
*
* @param text 提示系统的文本内容
* @return 返回PromptSystemSpec对象,用于继续配置提示系统
*/
PromptSystemSpec text(String text);
/**
* 设置提示系统的文本内容。
*
* @param text 文本资源
* @param charset 字符集
* @return 返回PromptSystemSpec对象,用于继续配置提示系统
*/
PromptSystemSpec text(Resource text, Charset charset);
/**
* 设置提示系统的文本内容。
*
* @param text 文本资源,包含提示系统需要显示的文本内容
* @return 返回PromptSystemSpec对象,用于继续配置提示系统
*/
PromptSystemSpec text(Resource text);
/**
* 设置提示系统的参数。
*
* @param p 包含参数的Map对象,其中key为参数名,value为参数值
* @return 返回PromptSystemSpec对象,用于继续配置提示系统
*/
PromptSystemSpec params(Map<String, Object> p);
/**
* 设置提示系统的参数。
*
* @param k 参数名
* @param v 参数值
* @return 返回PromptSystemSpec对象,用于继续配置提示系统
*/
PromptSystemSpec param(String k, Object v);
}
interface AdvisorSpec {
/**
* 设置参数,并返回AdvisorSpec对象,用于继续配置Advisor。
*
* @param k 参数名
* @param v 参数值
* @return 返回AdvisorSpec对象,用于继续配置Advisor
*/
AdvisorSpec param(String k, Object v);
/**
* 设置Advisor的参数。
*
* @param p 包含参数的Map对象,其中key为参数名,value为参数值
* @return 返回配置好的AdvisorSpec对象
*/
AdvisorSpec params(Map<String, Object> p);
/**
* 设置请求响应的顾问对象列表。
*
* @param advisors 请求响应的顾问对象列表
* @return 返回配置好的AdvisorSpec对象
*/
AdvisorSpec advisors(RequestResponseAdvisor... advisors);
/**
* 设置顾问对象列表。
*
* @param advisors 请求响应顾问对象列表
* @return 返回配置好的AdvisorSpec对象
*/
AdvisorSpec advisors(List<RequestResponseAdvisor> advisors);
}
/**
* 回调的响应规格
*/
interface CallResponseSpec {
/**
* 根据提供的泛型类型引用将请求结果转换为指定的实体类型
*
* @param type 泛型类型引用,用于指定转换后的实体类型
* @param <T> 转换后的实体类型
* @return 转换后的实体对象
*/
<T> T entity(ParameterizedTypeReference<T> type);
/**
* 将请求结果映射为指定类型的实体对象
*
* @param structuredOutputConverter 结构化输出转换器,用于将请求结果转换为指定类型的实体对象
* @param <T> 实体对象泛型类型
* @return 映射后的实体对象
*/
<T> T entity(StructuredOutputConverter<T> structuredOutputConverter);
/**
* 将请求结果映射为指定类型的实体对象
*
* @param type 实体对象类型
* @param <T> 实体对象泛型类型
* @return 映射后的实体对象
*/
<T> T entity(Class<T> type);
/**
* 发起聊天请求,获取聊天响应。
*
* @return 聊天响应对象,包含聊天结果信息。
*/
ChatResponse chatResponse();
/**
* 获取内容字符串。
*
* @return 返回内容字符串。
*/
String content();
/**
* 返回一个响应实体,包含聊天响应和泛型类型T的实体。
*
* @param type 泛型类型T的类对象,用于解析响应中的body内容。
* @param <T> 泛型类型T,表示响应实体中body的内容类型。
* @return 响应实体,包含聊天响应和泛型类型T的实体。
*/
<T> ResponseEntity<ChatResponse, T> responseEntity(Class<T> type);
/**
* 返回一个包含聊天响应和泛型类型T的实体的响应实体。
*
* @param type 用于指定泛型类型T的ParameterizedTypeReference引用。
* @param <T> 泛型类型T,表示响应实体中body的内容类型。
* @return 包含聊天响应和泛型类型T的实体的响应实体。
*/
<T> ResponseEntity<ChatResponse, T> responseEntity(ParameterizedTypeReference<T> type);
/**
* 返回一个包含聊天响应和泛型类型T的实体的响应实体。
*
* @param structuredOutputConverter 用于转换响应实体的结构化输出转换器。
* @param <T> 泛型类型T,表示响应实体中body的内容类型。
* @return 包含聊天响应和泛型类型T的实体的响应实体。
*/
<T> ResponseEntity<ChatResponse, T> responseEntity(StructuredOutputConverter<T> structuredOutputConverter);
}
/**
* 流式响应的规格
*/
interface StreamResponseSpec {
/**
* 返回一个Flux对象,其中包含ChatResponse类型的数据流。
*
* @return 包含ChatResponse类型数据的Flux对象
*/
Flux<ChatResponse> chatResponse();
/**
* 返回一个包含字符串的Flux对象。
*
* @return 包含字符串的Flux对象
*/
Flux<String> content();
}
/**
* 聊天客户端的请求规格
*/
interface ChatClientPromptRequestSpec {
/**
* 发起呼叫提示响应的调用请求。
*
* @return 返回 CallPromptResponseSpec 类型的对象,用于配置和获取呼叫提示响应的相关信息。
*/
CallPromptResponseSpec call();
/**
* 发起流提示响应的调用请求。
*
* @return 返回StreamPromptResponseSpec类型的对象,用于配置和获取流提示响应的相关信息。
*/
StreamPromptResponseSpec stream();
}
interface CallPromptResponseSpec {
/**
* 返回内容字符串。
*
* @return 内容字符串
*/
String content();
/**
* 获取内容列表。
*
* @return List<String> 返回一个字符串列表,包含特定的内容。
*/
List<String> contents();
/**
* 获得聊天响应的方法。
*
* 该方法用于发起一个聊天请求,并返回相应的聊天内容。
* 设计该方法的目的是为了提供一种与聊天机器人交互的方式,
* 使得用户可以通过调用这个方法来获取机器人生成的聊天回复。
*
* @return ChatResponse 返回一个ChatResponse对象,该对象包含了聊天机器人的响应内容。
*/
ChatResponse chatResponse();
}
/**
* StreamPromptResponseSpec 接口定义了流式响应规范,用于处理聊天响应的流。
* 该接口主要面向异步编程模型,利用Reactor框架的Flux类型处理响应的序列。
*/
interface StreamPromptResponseSpec {
/**
* 提供一个Flux实例,用于发布聊天响应。
* <p>
* 聊天响应(ChatResponse)是一个包含聊天具体内容和元数据的实体。
* 通过这个方法,调用者可以以异步流的形式获取聊天响应。
*
* @return Flux<ChatResponse>,一个异步序列,发布聊天响应。
*/
Flux<ChatResponse> chatResponse();
/**
* 提供一个Flux实例,用于发布聊天内容。
* <p>
* 这个方法专注于发布聊天内容的字符串表示,而不包括其他元数据。
* 调用者可以通过这个流专注于聊天的文本内容。
*
* @return Flux<String>,一个异步序列,发布聊天内容的字符串形式。
*/
public Flux<String> content();
}
/**
* 聊天客户端请求规格接口。
* 该接口定义了构建聊天客户端请求的各种方法,允许逐步构建请求规格,
* 包括但不限于设置顾问、消息、选项、功能和系统及用户提示。
*/
interface ChatClientRequestSpec {
/**
* 获取构建器,用于修改请求规格。
*
* @return 构建器实例,用于进一步定制请求规格。
*/
Builder mutate();
/**
* 使用消费者回调来配置顾问规格。
*
* @param consumer 一个消费AdvisorSpec的Lambda表达式,用于定制顾问规格。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec advisors(Consumer<AdvisorSpec> consumer);
/**
* 添加一个或多个请求响应顾问到请求规格中。
*
* @param advisors 一个RequestResponseAdvisor数组,用于定制请求的处理。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec advisors(RequestResponseAdvisor... advisors);
/**
* 添加一个顾问列表到请求规格中。
*
* @param advisors 一个包含RequestResponseAdvisor的列表。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec advisors(List<RequestResponseAdvisor> advisors);
/**
* 添加一个或多个消息到请求规格中。
*
* @param messages 一个Message数组,用于在请求中发送的消息。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec messages(Message... messages);
/**
* 添加一个消息列表到请求规格中。
*
* @param messages 一个包含Message的列表。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec messages(List<Message> messages);
/**
* 为请求添加特定选项。
*
* @param options 一个实现ChatOptions接口的实例,用于定制请求选项。
* @return 当前请求规格实例,允许链式调用。
*/
<T extends ChatOptions> ChatClientRequestSpec options(T options);
/**
* 为请求添加一个功能,包括名称、描述和功能本身。
*
* @param name 功能名称。
* @param description 功能描述。
* @param function 功能的具体实现,作为一个函数接口。
* @param <I> 输入类型。
* @param <O> 输出类型。
* @return 当前请求规格实例,允许链式调用。
*/
<I, O> ChatClientRequestSpec function(String name, String description,
java.util.function.Function<I, O> function);
/**
* 通过功能 bean 名称添加多个功能到请求规格中。
*
* @param functionBeanNames 功能 bean 的名称数组。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec functions(String... functionBeanNames);
/**
* 为请求添加系统文本。
*
* @param text 系统文本内容。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec system(String text);
/**
* 为请求添加系统文本资源,指定字符集。
*
* @param textResource 系统文本资源。
* @param charset 指定的字符集。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec system(Resource textResource, Charset charset);
/**
* 为请求添加系统文本资源。
*
* @param textResource 系统文本资源。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec system(Resource text);
/**
* 使用消费者回调来配置系统规格。
*
* @param consumer 一个消费PromptSystemSpec的Lambda表达式,用于定制系统规格。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec system(Consumer<PromptSystemSpec> consumer);
/**
* 为请求添加用户文本。
*
* @param text 用户文本内容。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec user(String text);
/**
* 为请求添加用户文本资源,指定字符集。
*
* @param textResource 用户文本资源。
* @param charset 指定的字符集。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec user(Resource text, Charset charset);
/**
* 为请求添加用户文本资源。
*
* @param textResource 用户文本资源。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec user(Resource text);
/**
* 使用消费者回调来配置用户规格。
*
* @param consumer 一个消费PromptUserSpec的Lambda表达式,用于定制用户规格。
* @return 当前请求规格实例,允许链式调用。
*/
ChatClientRequestSpec user(Consumer<PromptUserSpec> consumer);
/**
* 构建一个调用响应规格。
*
* @return 调用响应规格实例。
*/
CallResponseSpec call();
/**
* 构建一个流式响应规格。
*
* @return 流式响应规格实例。
*/
StreamResponseSpec stream();
}
/**
* A mutable builder for creating a {@link ChatClient}.
*/
interface Builder {
Builder defaultAdvisors(RequestResponseAdvisor... advisor);
Builder defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer);
Builder defaultAdvisors(List<RequestResponseAdvisor> advisors);
ChatClient build();
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);
<I, O> Builder defaultFunction(String name, String description, java.util.function.Function<I, O> function);
Builder defaultFunctions(String... functionNames);
}
/**
* Calls the underlying chat model with a prompt message and returns the output
* content of the first generation.
* @param message The message to be used as a prompt for the chat model.
* @return The output content of the first generation.
* @deprecated This method is deprecated as of version 1.0.0 M1 and will be removed in
* a future release. Use the method
* builder(chatModel).build().prompt().user(message).call().content() instead
*
*/
@Deprecated(since = "1.0.0 M1", forRemoval = true)
default String call(String message) {
var prompt = new Prompt(new UserMessage(message));
var generation = call(prompt).getResult();
return (generation != null) ? generation.getOutput().getContent() : "";
}
/**
* Calls the underlying chat model with a prompt message and returns the output
* content of the first generation.
* @param messages The messages to be used as a prompt for the chat model.
* @return The output content of the first generation.
* @deprecated This method is deprecated as of version 1.0.0 M1 and will be removed in
* a future release. Use the method
* builder(chatModel).build().prompt().messages(messages).call().content() instead.
*/
@Deprecated(since = "1.0.0 M1", forRemoval = true)
default String call(Message... messages) {
var prompt = new Prompt(Arrays.asList(messages));
var generation = call(prompt).getResult();
return (generation != null) ? generation.getOutput().getContent() : "";
}
/**
* Calls the underlying chat model with a prompt and returns the corresponding chat
* response.
* @param prompt The prompt to be used for the chat model.
* @return The chat response containing the generated messages.
* @deprecated This method is deprecated as of version 1.0.0 M1 and will be removed in
* a future release. Use the method builder(chatModel).build().prompt(prompt).call()
* instead.
*/
@Deprecated(since = "1.0.0 M1", forRemoval = true)
ChatResponse call(Prompt prompt);
}