聊天客户端(Chat Client) API
Fluent API 具有用于构建包含提示语组成部分的方法,这些提示语作为输入传递给 AI 模型。 包含用于指导 AI 模型的输出和行为的说明文本。从 API 的角度来看,提示语由消息集合组成。
AI 模型处理两种主要类型的消息:用户消息(来自用户的直接输入)和系统消息(由系统生成以引导对话)。
这些消息通常包含占位符,这些占位符在运行时根据用户输入进行替换,以自定义 AI 模型对用户输入的响应。
还可以指定提示选项,例如要使用的 AI 模型的名称以及控制生成输出的随机性或创造力的温度设置。
ChatClient源码分析请移步到此处Spring AI之ChatClient源码分析
创建 ChatClient
您可以获取任何 ChatModel Spring Boot 自动配置的自动配置实例,也可以以编程方式创建一个实例。
ChatClient
ChatClient.Builder
使用自动配置的 ChatClient.Builder
在最简单的用例中,Spring AI 提供 Spring Boot 自动配置,创建一个原型 Bean 供您注入类中。 下面是检索对简单用户请求的 String 响应。
ChatClient.Builder
@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()
.content();
}
}
在这个简单示例中,用户输入设置用户消息的内容。 call 方法向 AI 模型发送请求,context 方法以 String 形式返回 AI 模型的响应。
以编程方式创建 ChatClient
您可以通过设置属性来禁用自动配置。 如果同时使用多个聊天模型,这将非常有用。 然后以编程方式为每个创建一个实例:ChatClient.Builder
spring.ai.chat.client.enabled=false
ChatClient.Builder
ChatModel
ChatModel myChatModel = ... // usually autowired
ChatClient.Builder builder = ChatClient.builder(myChatModel);
// or create a ChatClient with the default builder settings:
ChatClient chatClient = ChatClient.create(myChatModel);
ChatClient 响应
ChatClient API 提供了多种方法来格式化来自 AI 模型的响应。
返回 ChatResponse
来自 AI 模型的响应是由 ChatResponse 类型定义的丰富结构。 它包括有关如何生成响应的元数据,还可以包含多个响应(称为第 s 代),每个响应都有自己的元数据。 元数据包括用于创建响应的标记数(每个标记大约为一个单词的 3/4)。 此信息很重要,因为托管 AI 模型根据每个请求使用的令牌数量收费。
下面通过调用该方法来返回包含元数据的对象的示例。ChatResponse
chatResponse()
call()
ChatResponse chatResponse = chatClient.prompt()
.user("Tell me a joke")
.call()
.chatResponse();
您通常希望返回从返回的实体类映射的实体类。 该方法提供此功能。String
entity
例如,给定 Java 记录:
record ActorFilms(String actor, List<String> movies) {
}
entity
ActorFilms actorFilms = chatClient.prompt()
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorFilms.class);
还有一个带有签名的重载方法,可用于指定泛型列表等类型:entity
entity(ParameterizedTypeReference<T> type)
List<ActorFilms> actorFilms = chatClient.prompt()
.user("Generate the filmography of 5 movies for Tom Hanks and Bill Murray.")
.call()
.entity(new ParameterizedTypeReference<List<ActorsFilms>>() {
});
流式响应
允许您获得如下所示的异步响应stream
Flux<String> output = chatClient.prompt()
.user("Tell me a joke")
.stream()
.content();
您还可以使用方法流式传输 。ChatResponse
Flux<ChatResponse> chatResponse()
在 1.0.0 M2 中,我们将提供一种方便的方法,让您使用响应式方法返回 Java 实体。 同时,应使用结构化输出转换器来转换聚合响应显式,如下所示。 这也演示了 Fluent API 中参数的使用,这将在本文档的后面部分中更详细地讨论。stream()
var converter = new BeanOutputConverter<>(new ParameterizedTypeReference<List<ActorsFilms>>() {
});
Flux<String> flux = this.chatClient.prompt()
.user(u -> u.text("""
Generate the filmography for a random actor.
{format}
""")
.param("format", converter.getFormat()))
.stream()
.content();
String content = flux.collectList().block().stream().collect(Collectors.joining());
List<ActorFilms> actorFilms = converter.convert(content);
call() 返回值
指定方法后,响应类型有几个不同的选项。call
ChatClient
-
String content()
:返回响应的 String 内容 -
ChatResponse chatResponse()
:返回包含多代的对象以及有关响应的元数据,例如,用于创建响应的令牌数。ChatResponse
-
entity
返回 Java 类型-
entity(ParameterizedTypeReference<T> type):用于返回实体类型的集合。
-
entity(Class<T> type):用于返回特定的实体类型。
-
entity(StructuredOutputConverter<T> structuredOutputConverter):用于指定 a 的实例以将 a 转换为实体类型。
StructuredOutputConverter
String
-
您还可以调用该方法,而不是 和stream
call
stream() 返回值
指定 on 的方法后,响应类型有几个选项:stream
ChatClient
-
Flux<String> content()
:返回 AI 模型生成的字符串的 Flux。 -
Flux<ChatResponse> chatResponse()
:返回对象的 Flux,其中包含有关响应的其他元数据。ChatResponse
使用默认值
在类中使用默认系统文本创建 ChatClient 可简化运行时代码。 通过设置默认值,您只需要在调用时指定用户文本,而无需为运行时代码路径中的每个请求设置系统文本。@Configuration
ChatClient
默认系统文本
在以下示例中,我们将系统文本配置为始终以盗版者的声音回复。 为了避免在运行时代码中重复系统文本,我们将在类中创建一个实例。ChatClient
@Configuration
@Configuration
class Config {
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a Pirate")
.build();
}
}
和 an 来调用它@RestController
@RestController
class AIController {
private final ChatClient chatClient;
AIController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/ai/simple")
public Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message) {
return Map.of("completion", chatClient.prompt().user(message).call().content());
}
}
通过 curl 调用它给出
❯ curl localhost:8080/ai/simple
{"generation":"Why did the pirate go to the comedy club? To hear some arrr-rated jokes! Arrr, matey!"}
带参数的默认系统文本
在下面的示例中,我们将在系统文本中使用占位符来指定运行时完成的语音,而不是设计时。
@Configuration
class Config {
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder.defaultSystem("You are a friendly chat bot that answers question in the voice of a {voice}")
.build();
}
}
@RestController
class AIController {
private final ChatClient chatClient
AIController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@GetMapping("/ai")
Map<String, String> completion(@RequestParam(value = "message", defaultValue = "Tell me a joke") String message, String voice) {
return Map.of(
"completion",
chatClient.prompt()
.system(sp -> sp.param("voice", voice))
.user(message)
.call()
.content());
}
}
答案是
http localhost:8080/ai voice=='Robert DeNiro'
{
"completion": "You talkin' to me? Okay, here's a joke for ya: Why couldn't the bicycle stand up by itself? Because it was two tired! Classic, right?"
}
其他默认值
在该级别,您可以指定默认提示。ChatClient.Builder
-
defaultOptions(ChatOptions chatOptions)
:传入类中定义的可移植选项或特定于模型的选项,例如 中的选项。有关特定于模型的实现的更多信息,请参阅 JavaDocs。ChatOptions
OpenAiChatOptions
ChatOptions
-
defaultFunction(String name, String description, java.util.function.Function<I, O> function)
:用于在用户文本中引用函数。它解释了函数的用途,并帮助 AI 模型选择正确的函数以获得准确的响应。该参数是模型在必要时执行的 Java 函数实例。name
description
function
-
defaultFunctions(String… functionNames)
:在应用程序上下文中定义的 'java.util.Function' 的 Bean 名称。 -
defaultUser(String text)
、 、 : 这些方法允许您定义用户文本。这允许您使用 lambda 来指定用户文本和任何默认参数。defaultUser(Resource text)
defaultUser(Consumer<UserSpec> userSpecConsumer)
Consumer<UserSpec>
-
defaultAdvisors(RequestResponseAdvisor… advisor)
:顾问程序允许修改用于创建 .该实现通过在提示后附加与用户文本相关的上下文信息来实现模式。Prompt
QuestionAnswerAdvisor
Retrieval Augmented Generation
-
defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer)
:此方法允许您定义一个以使用 .顾问可以修改用于创建最终 .它允许您指定一个 lambda 来添加顾问,例如 ,它通过根据用户文本在提示后附加相关上下文信息来支持。Consumer
AdvisorSpec
Prompt
Consumer<AdvisorSpec>
QuestionAnswerAdvisor
Retrieval Augmented Generation
您可以在运行时使用不带前缀的相应方法覆盖这些默认值。default
-
options(ChatOptions chatOptions)
-
function(String name, String description, java.util.function.Function<I, O> function)
-
'functions(字符串...functionNames)
-
user(String text)
, ,user(Resource text)
user(Consumer<UserSpec> userSpecConsumer)
-
advisors(RequestResponseAdvisor… advisor)
-
advisors(Consumer<AdvisorSpec> advisorSpecConsumer)
顾问
使用用户文本调用 AI 模型时,一种常见模式是使用上下文数据追加或扩充提示。
此上下文数据可以是不同的类型。常见类型包括:
-
您自己的数据:这是 AI 模型尚未训练的数据。即使模型看到了类似的数据,附加的上下文数据在生成响应时也会优先。
-
对话历史记录:聊天模型的 API 是无状态的。如果你告诉 AI 模型你的名字,它不会在随后的交互中记住它。必须随每个请求一起发送对话历史记录,以确保在生成响应时考虑以前的交互。
检索增强生成
向量数据库存储 AI 模型无法识别的数据。 当用户问题发送到 AI 模型时,会向向量数据库查询与用户问题相关的文档。QuestionAnswerAdvisor
来自向量数据库的响应将附加到用户文本中,以便为 AI 模型生成响应提供上下文。
假设您已经将数据加载到 ,您可以通过提供 的实例来执行检索增强生成 (RAG)。VectorStore
QuestionAnswerAdvisor
ChatClient
ChatResponse response = ChatClient.builder(chatModel)
.build().prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
.user(userText)
.call()
.chatResponse();
在此示例中,将对矢量数据库中的所有文档执行相似性搜索。 为了限制搜索的文档类型,需要一个类似 SQL 的过滤器表达式,该表达式可移植到所有 .SearchRequest.defaults()
SearchRequest
VectorStores
聊天记忆
该接口表示聊天对话历史记录的存储。它提供了将消息添加到 * 对话,从对话中检索消息,并清除对话历史记录。ChatMemory
有一种实现为聊天对话历史记录提供内存中存储。InMemoryChatMemory
两个顾问实现使用该接口向提示提供对话历史记录的建议,它们在如何将内存添加到提示的详细信息上有所不同ChatMemory
-
MessageChatMemoryAdvisor
:检索内存作为消息集合添加到提示符中 -
PromptChatMemoryAdvisor
:检索到的内存将添加到提示的系统文本中。 -
VectorStoreChatMemoryAdvisor
:构造函数 'VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize)' 允许您指定要从中检索聊天记录的 VectorStore、unqiue 会话 ID、要检索的聊天记录的大小(以令牌大小表示)。
下面显示了使用多个顾问程序的示例实现@Service
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;
import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY;
@Service
public class CustomerSupportAssistant {
private final ChatClient chatClient;
public CustomerSupportAssistant(ChatClient.Builder builder, VectorStore vectorStore, ChatMemory chatMemory) {
this.chatClient = builder
.defaultSystem("""
You are a customer chat support agent of an airline named "Funnair".", Respond in a friendly,
helpful, and joyful manner.
Before providing information about a booking or cancelling a booking, you MUST always
get the following information from the user: booking number, customer first name and last name.
Before changing a booking you MUST ensure it is permitted by the terms.
If there is a charge for the change, you MUST ask the user to consent before proceeding.
""")
.defaultAdvisors(
new PromptChatMemoryAdvisor(chatMemory),
// new MessageChatMemoryAdvisor(chatMemory), // CHAT MEMORY
new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()),
new LoggingAdvisor()) // RAG
.defaultFunctions("getBookingDetails", "changeBooking", "cancelBooking") // FUNCTION CALLING
.build();
}
public Flux<String> chat(String chatId, String userMessageContent) {
return this.chatClient.prompt()
.user(userMessageContent)
.advisors(a -> a
.param(CHAT_MEMORY_CONVERSATION_ID_KEY, chatId)
.param(CHAT_MEMORY_RETRIEVE_SIZE_KEY, 100))
.stream().content();
}
}