此版本仍在开发中,尚未被视为稳定版本。最新的快照版本请使用 Spring AI 1.0.0-SNAPSHOT! |
聊天客户端 API
它提供了一个 Fluent API,用于与 AI 模型通信。
它支持同步和流式编程模型。ChatClient
Fluent API 具有构建 Prompt 的组成部分的方法,这些部分作为输入传递给 AI 模型。
该 包含指导 AI 模型的输出和行为的说明文本。从 API 的角度来看,提示由一组消息组成。Prompt
AI 模型处理两种主要类型的消息:用户消息(来自用户的直接输入)和系统消息(由系统生成以指导对话)。
这些消息通常包含占位符,这些占位符在运行时根据用户输入进行替换,以自定义 AI 模型对用户输入的响应。
还有一些可以指定的 Prompt 选项,例如要使用的 AI 模型的名称以及控制生成输出的随机性或创造性的温度设置。
创建 ChatClient
这是使用对象创建的。
您可以为任何 ChatModel Spring Boot 自动配置获取自动配置的实例,也可以以编程方式创建一个。ChatClient
ChatClient.Builder
ChatClient.Builder
使用自动配置的 ChatClient.Builder
在最简单的用例中, Spring AI 提供 Spring Boot 自动配置,创建一个原型 bean 供你注入到你的类中。
下面是检索对简单用户请求的响应的简单示例。ChatClient.Builder
String
@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();
}
}
在这个简单的示例中,用户输入设置用户消息的内容。
该方法向 AI 模型发送请求,该方法将 AI 模型的响应作为 .call()
content()
String
以编程方式创建 ChatClient
您可以通过设置属性 来禁用自动配置 。
如果同时使用多个聊天模型,这非常有用。
然后,以编程方式为每个需要创建一个实例:ChatClient.Builder
spring.ai.chat.client.enabled=false
ChatClient.Builder
ChatModel
ChatModel myChatModel = ... // usually autowired
ChatClient.Builder builder = ChatClient.builder(this.myChatModel);
// or create a ChatClient with the default builder settings:
ChatClient chatClient = ChatClient.create(this.myChatModel);
ChatClient Fluent API
Fluent API 允许您使用重载方法以三种不同的方式创建提示,以启动 Fluent API:ChatClient
prompt
-
prompt()
:这种不带参数的方法允许您开始使用 Fluent API,从而允许您构建 user、system 和 prompt的其他部分。 -
prompt(Prompt prompt)
:此方法接受一个参数,允许您传入使用 Prompt 的非 Fluent API 创建的实例。Prompt
Prompt
-
prompt(String content)
:这是一种类似于前面的重载的便捷方法。它获取用户的文本内容。
ChatClient 响应
API 提供了多种使用 Fluent API 格式化 AI 模型的响应的方法。ChatClient
返回 ChatResponse
来自 AI 模型的响应是由类型定义的丰富结构。
它包括有关响应生成方式的元数据,还可以包含多个响应,称为 Generations,每个响应都有自己的元数据。
元数据包括用于创建响应的标记数(每个标记大约是一个单词的 3/4)。
此信息非常重要,因为托管 AI 模型根据每个请求使用的令牌数量收费。ChatResponse
通过在方法后调用,如下所示返回包含元数据的对象的示例。ChatResponse
chatResponse()
call()
ChatResponse chatResponse = chatClient.prompt()
.user("Tell me a joke")
.call()
.chatResponse();
返回实体
您通常需要返回从返回的 .
该方法提供此功能。String
entity()
例如,给定 Java 记录:
record ActorFilms(String actor, List<String> movies) {}
您可以使用该方法轻松地将 AI 模型的输出映射到此记录,如下所示:entity()
ActorFilms actorFilms = chatClient.prompt()
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorFilms.class);
还有一个带有签名的重载方法,允许您指定类型,例如泛型 List: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<ActorFilms>>() {});
流式响应
该方法允许您获得异步响应,如下所示:stream()
Flux<String> output = chatClient.prompt()
.user("Tell me a joke")
.stream()
.content();
您还可以使用方法 .ChatResponse
Flux<ChatResponse> chatResponse()
将来,我们将提供一种便捷的方法,让您使用 reactive 方法返回 Java 实体。
同时,您应该使用 Structured Output Converter 转换聚合响应显式,如下所示。
这也演示了 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", this.converter.getFormat()))
.stream()
.content();
String content = this.flux.collectList().block().stream().collect(Collectors.joining());
List<ActorFilms> actorFilms = this.converter.convert(this.content);
call() 返回值
指定方法 on 后,响应类型有几种不同的选项。call()
ChatClient
-
String content()
:返回响应的 String 内容 -
ChatResponse chatResponse()
:返回包含多个代的对象以及有关响应的元数据,例如用于创建响应的令牌数。ChatResponse
-
entity()
返回 Java 类型-
entity(ParameterizedTypeReference<T> type)
:用于返回 OF 实体类型。Collection
-
entity(Class<T> type)
:用于返回特定实体类型。 -
entity(StructuredOutputConverter<T> structuredOutputConverter)
:用于指定 a 的实例以将 a 转换为实体类型。StructuredOutputConverter
String
-
您还可以调用 method 而不是 .stream()
call()
stream() 返回值
指定方法 on 后,响应类型有几个选项:stream()
ChatClient
-
Flux<String> content()
:返回 AI 模型生成的字符串的 a。Flux
-
Flux<ChatResponse> chatResponse()
:返回对象的 a,其中包含有关响应的其他元数据。Flux
ChatResponse
使用默认值
在类中创建具有默认系统文本的 a 可简化运行时代码。
通过设置默认值,您只需在调用 时指定用户文本,无需为运行时代码路径中的每个请求设置系统文本。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();
}
}
和 a 来调用它:@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", this.chatClient.prompt().user(message).call().content());
}
}
通过 curl 调用应用程序端点时,结果为:
❯ curl localhost:8080/ai/simple
{"completion":"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",
this.chatClient.prompt()
.system(sp -> sp.param("voice", voice))
.user(message)
.call()
.content());
}
}
通过 httpie 调用应用程序终端节点时,结果为:
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(Advisor… advisor)
:顾问程序允许修改用于创建 .该实现通过在 prompt 后附加与用户文本相关的上下文信息来启用模式。Prompt
QuestionAnswerAdvisor
Retrieval Augmented Generation
-
defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer)
:此方法允许您定义 a 以使用 配置多个 advisor。顾问可以修改用于创建最终 .允许您指定一个 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(String… functionNames)
-
user(String text)
, ,user(Resource text)
user(Consumer<UserSpec> userSpecConsumer)
-
advisors(Advisor… advisor)
-
advisors(Consumer<AdvisorSpec> advisorSpecConsumer)
顾问
Advisors API 提供了一种灵活而强大的方法来拦截、修改和增强 Spring 应用程序中的 AI 驱动的交互。
使用用户文本调用 AI 模型时,一种常见模式是使用上下文数据附加或增强提示。
此上下文数据可以是不同的类型。常见类型包括:
-
您自己的数据:这是 AI 模型尚未训练的数据。即使模型看到了类似的数据,附加的上下文数据在生成响应时也优先。
-
对话历史记录:聊天模型的 API 是无状态的。如果您告诉 AI 模型您的名字,它将在后续交互中不会记住它。必须随每个请求发送对话历史记录,以确保在生成响应时考虑以前的交互。
ChatClient 中的 Advisor 配置
ChatClient Fluent API 提供了用于配置 advisor 的接口。此接口提供了添加参数、一次设置多个参数以及将一个或多个 advisor 添加到链的方法。AdvisorSpec
interface AdvisorSpec {
AdvisorSpec param(String k, Object v);
AdvisorSpec params(Map<String, Object> p);
AdvisorSpec advisors(Advisor... advisors);
AdvisorSpec advisors(List<Advisor> advisors);
}
将 advisor 添加到链中的顺序至关重要,因为它决定了它们的执行顺序。每个 advisor 都以某种方式修改 prompt 或 context,并且一个 advisor 所做的更改将传递给链中的下一个 advisor。 |
ChatClient.builder(chatModel)
.build()
.prompt()
.advisors(
new MessageChatMemoryAdvisor(chatMemory),
new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults())
)
.user(userText)
.call()
.content();
在此配置中,将首先执行 ,并将对话历史记录添加到提示符中。然后,将根据用户的问题和添加的对话历史记录执行搜索,从而可能提供更相关的结果。MessageChatMemoryAdvisor
QuestionAnswerAdvisor
检索增强一代
矢量数据库存储 AI 模型不知道的数据。
将用户问题发送到 AI 模型时,a 会在向量数据库中查询与用户问题相关的文档。QuestionAnswerAdvisor
来自向量数据库的响应将附加到用户文本中,以便为 AI 模型生成响应提供上下文。
假设您已将数据加载到 中,则可以通过向 提供 来执行检索增强生成 (RAG)。VectorStore
QuestionAnswerAdvisor
ChatClient
ChatResponse response = ChatClient.builder(chatModel)
.build().prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
.user(userText)
.call()
.chatResponse();
在此示例中,将对 Vector Database 中的所有文档执行相似性搜索。
为了限制搜索的文档类型,它采用一个类似 SQL 的过滤器表达式,该表达式可在所有 .SearchRequest.defaults()
SearchRequest
VectorStores
动态筛选表达式
在运行时使用 advisor context 参数更新筛选条件表达式:SearchRequest
FILTER_EXPRESSION
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
.build();
// Update filter expression at runtime
String content = this.chatClient.prompt()
.user("Please answer my question XYZ")
.advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, "type == 'Spring'"))
.call()
.content();
该参数允许您根据提供的表达式动态筛选搜索结果。FILTER_EXPRESSION
聊天记忆
该接口表示聊天对话历史记录的存储。它提供了向对话添加消息、从对话中检索消息以及清除对话历史记录的方法。ChatMemory
当前有两个实现 和 ,它们分别为聊天对话历史记录提供内存中的存储,并相应地使用 持久化。InMemoryChatMemory
CassandraChatMemory
time-to-live
要创建 with :CassandraChatMemory
time-to-live
CassandraChatMemory.create(CassandraChatMemoryConfig.builder().withTimeToLive(Duration.ofDays(1)).build());
以下 advisor 实现使用接口向 Prompt 提供 conversation history 的通知,这些 conversation history 在如何将内存添加到提示的细节上有所不同ChatMemory
-
MessageChatMemoryAdvisor
:检索内存并将其作为消息集合添加到提示符中 -
PromptChatMemoryAdvisor
:检索内存并将其添加到提示的系统文本中。 -
VectorStoreChatMemoryAdvisor
:构造函数此构造函数允许您:VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize, int order)
-
指定用于管理和查询文档的 VectorStore 实例。
-
设置在上下文中未提供任何内容时使用的默认对话 ID。
-
根据令牌大小定义聊天历史记录检索的窗口大小。
-
提供用于 Chat Advisor 系统的系统文本建议。
-
设置此 advisor 在链中的优先顺序。
-
该方法允许您指定默认对话 ID、聊天历史记录窗口大小以及要检索的聊天历史记录的顺序。VectorStoreChatMemoryAdvisor.builder()
使用多个 advisor 的示例实现如下所示。@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 MessageChatMemoryAdvisor(chatMemory), // CHAT MEMORY
new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()), // RAG
new SimpleLoggerAdvisor())
.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();
}
}
Logging
它是一个 advisor,用于记录 和 的数据。
这对于调试和监控 AI 交互非常有用。SimpleLoggerAdvisor
request
response
ChatClient
Spring AI 支持 LLM 和向量存储交互的可观察性。有关更多信息,请参阅 可观测性指南 。 |
要启用日志记录,请在创建 ChatClient 时将 添加到 advisor 链中。
建议将其添加到链的末尾:SimpleLoggerAdvisor
ChatResponse response = ChatClient.create(chatModel).prompt()
.advisors(new SimpleLoggerAdvisor())
.user("Tell me a joke?")
.call()
.chatResponse();
要查看日志,请将 advisor 包的日志记录级别设置为:DEBUG
logging.level.org.springframework.ai.chat.client.advisor=DEBUG
将此文件添加到您的 or 文件中。application.properties
application.yaml
您可以使用以下构造函数自定义 from 和 what data is logged (来自和记录的数据):AdvisedRequest
ChatResponse
SimpleLoggerAdvisor(
Function<AdvisedRequest, String> requestToString,
Function<ChatResponse, String> responseToString
)
用法示例:
SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
request -> "Custom request: " + request.userText,
response -> "Custom response: " + response.getResult()
);
这允许您根据自己的特定需求定制记录的信息。
在生产环境中记录敏感信息时要小心。 |