Chat Client API
它ChatClient提供了与 AI 模型进行通信的流畅 API。它支持同步和反应式编程模型。
Fluent API 具有构建Prompt组成部分的方法,Prompt 会作为输入传递给 AI 模型。其中Prompt包含指导 AI 模型输出和行为的说明性文本。从 API 的角度来看,提示由一组消息组成。
AI 模型处理两种主要类型的消息:用户消息(来自用户的直接输入)和系统消息(由系统生成以引导对话)。
这些消息通常包含占位符,这些占位符在运行时根据用户输入进行替换,以定制 AI 模型对用户输入的响应。
还可以指定提示选项,例如要使用的 AI 模型的名称和控制生成输出的随机性或创造性的温度设置。
创建 ChatClient
使用 ChatClient.Builder 创建 ChatClient 对象。您可以通过 ChatClient.Builder 为任何 ChatModel 的 Spring Boot 自动配置获取一个自动配置实例,也可以通过编程创建一个。
使用自动配置的 ChatClient.Builder
在最简单的用例中,Spring AI 提供 Spring Boot 自动配置,ChatClient.Builder 为您创建一个原型 bean 以注入到您的类中。这是一个检索对简单用户请求的字符串响应的简单示例。
@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 模型发送请求,content 方法以字符串形式返回 AI 模型的响应。
以编程方式创建 ChatClient
您可以通过设置属性 spring.ai.chat.client.enabled=false
来禁用 ChatClient.Builder
自动配置。如果多个聊天模型一起使用,这很有用。然后以编程方式为每个聊天模型创建一个 ChatClient.Builder
实例:
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 类型定义的丰富结构。它包含有关如何生成响应的元数据,还可以包含多个响应(称为 Generation),每个响应都有自己的元数据。元数据包括用于创建响应的令牌数量(每个令牌大约为一个单词的 3/4)。此信息很重要,因为托管 AI 模型根据每个请求使用的令牌数量收费。
ChatResponse下面显示了通过调用chatResponse()该方法返回包含元数据的对象的示例call()。
ChatResponse chatResponse = chatClient.prompt()
.user("Tell me a joke")
.call()
.chatResponse();
返回 Entity
您如果希望返回从返回的映射而来的实体类String。该entity方法提供了此功能。
例如,给定 Java Record:
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);
还有一种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>>() {
});
Streaming Responses
让stream您获得异步响应,如下所示
Flux<String> output = chatClient.prompt()
.user("Tell me a joke")
.stream()
.content();
您还可以ChatResponse使用该方法进行流式传输Flux<ChatResponse> chatResponse()
。
在 1.0.0 M2 中,我们将提供一种便捷方法,让您使用反应式stream()
方法返回 Java 实体。同时,您应该使用结构化输出转换器明确转换聚合响应,如下所示。这也演示了流畅 API 中参数的使用,本文档后面的部分将对此进行更详细的讨论。
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()
:返回响应的字符串内容ChatResponse chatResponse()
:返回ChatResponse包含多个代以及有关响应的元数据的对象,例如使用了多少个令牌来创建响应。entity
返回 Java 类型entity(ParameterizedTypeReference<T> type)
:用于返回实体类型的集合。entity(Class<T> type)
: 用于返回特定的实体类型。entity(StructuredOutputConverter<T> structuredOutputConverter)
: 用于指定一个实例,StructuredOutputConverter将一个实例转换String为实体类型。
您还可以调用该stream方法而call不是
stream() 返回值
在指定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();
}
}
并@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):传入ChatOptions类中定义的可移植选项或特定于模型的选项(例如 中的选项)OpenAiChatOptions。有关特定于模型的ChatOptions实现的更多信息,请参阅 JavaDocs。
defaultFunction(String name, String description, java.util.function.Function<I, O> function):name用于在用户文本中引用该函数。description解释该函数的用途并帮助 AI 模型选择正确的函数以获得准确的响应。参数function是模型将在必要时执行的 Java 函数实例。
defaultFunctions(String… functionNames):应用程序上下文中定义的 java.util.Function 的 bean 名称。
defaultUser(String text)、、defaultUser(Resource text):defaultUser(Consumer
defaultAdvisors(RequestResponseAdvisor… advisor):顾问允许修改用于创建的数据Prompt。该实现通过在提示中附加与用户文本相关的上下文信息来QuestionAnswerAdvisor实现模式。Retrieval Augmented Generation
defaultAdvisors(Consumer
您可以在运行时使用不带前缀的相应方法覆盖这些默认值default。
options(ChatOptions chatOptions)
function(String name, String description, java.util.function.Function<I, O> function)
`函数(字符串… 函数名称)
user(String text),,user(Resource text)user(Consumer
advisors(RequestResponseAdvisor… advisor)
advisors(Consumer
顾问
使用用户文本调用 AI 模型时的一个常见模式是使用上下文数据附加或扩充提示。
这些上下文数据可以是不同类型的。常见类型包括:
您自己的数据:这是 AI 模型尚未训练过的数据。即使模型已经看到过类似的数据,附加的上下文数据也会优先生成响应。
对话历史记录:聊天模型的 API 是无状态的。如果您告诉 AI 模型您的姓名,它不会在后续交互中记住它。每次请求都必须发送对话历史记录,以确保在生成响应时考虑到之前的交互。
检索增强生成
向量数据库存储的是 AI 模型不知道的数据。当用户问题被发送到 AI 模型时,它会在QuestionAnswerAdvisor向量数据库中查询与用户问题相关的文档。
来自向量数据库的响应被附加到用户文本中,为 AI 模型生成响应提供上下文。
假设您已将数据加载到中VectorStore,则可以通过向提供实例来执行检索增强生成 (RAG QuestionAnswerAdvisor) ChatClient。
ChatResponse response = ChatClient.builder(chatModel)
.build().prompt()
.advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
.user(userText)
.call()
.chatResponse();
已复制!
在这个例子中,SearchRequest.defaults()将对 Vector 数据库中的所有文档执行相似性搜索。为了限制要搜索的文档类型,采用SearchRequest了可移植到所有数据库中的类似 SQL 的筛选表达式VectorStores。
动态过滤表达式
SearchRequest使用FILTER_EXPRESSION顾问上下文参数在运行时更新过滤表达式:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()))
.build();
// Update filter expression at runtime
String content = chatClient.prompt()
.user(“Please answer my question XYZ”)
.advisors(a -> a.param(QuestionAnswerAdvisor.FILTER_EXPRESSION, “type == ‘Spring’”))
.call()
.content();
已复制!
该FILTER_EXPRESSION参数允许您根据提供的表达式动态过滤搜索结果。
聊天记忆
该接口ChatMemory表示聊天对话历史记录的存储。它提供向对话添加消息、从对话中检索消息以及清除对话历史记录的方法。
有一种实现InMemoryChatMemory可以为聊天对话历史记录提供内存存储。
两个顾问实现都使用ChatMemory接口来向提示提供对话历史记录,它们在如何将记忆添加到提示的细节上有所不同
MessageChatMemoryAdvisor:内存被检索作为消息集合添加到提示中
PromptChatMemoryAdvisor:内存被检索并添加到提示的系统文本中。
VectorStoreChatMemoryAdvisor :构造函数“VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize)”允许您指定要从中检索聊天历史记录的 VectorStore、唯一的对话 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
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();
}
}
已复制!
日志记录
这是一个用于记录ChatClient 的和数据的SimpleLoggerAdvisor顾问。这对于调试和监控您的 AI 交互非常有用。requestresponse
要启用日志记录,请SimpleLoggerAdvisor在创建 ChatClient 时将其添加到顾问链中。建议将其添加到链的末尾:
ChatResponse response = ChatClient.create(chatModel).prompt()
.advisors(new SimpleLoggerAdvisor())
.user(“Tell me a joke?”)
.call()
.chatResponse();
已复制!
要查看日志,请将顾问包的日志记录级别设置为DEBUG:
日志记录.级别.org.springframework.ai.chat.客户端.顾问=DEBUG
将其添加到您的application.properties或application.yaml文件中。
您可以使用以下构造函数自定义记录来自 AdvisedRequest 和 ChatResponse 的数据:
SimpleLoggerAdvisor(
Function<AdvisedRequest, String> requestToString,
Function<ChatResponse, String> responseToString
)
已复制!
使用示例:
javaCopySimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
request -> “Custom request: “ + request.userText,
response -> “Custom response: “ + response.getResult()
);
已复制!
这使得您可以根据您的特定需要定制记录的信息。
最后编辑:Jeebiz 更新时间:2024-07-06 19:00