聊天客户端(Chat Client) API

文章正文
发布时间:2025-05-11 17:34

上一课:Spring AI API介绍 

ChatClient,它提供了一个流畅的 API,用于与 AI 模型进行通信。 它支持同步编程模型和响应式编程模型。

Fluent API 具有用于构建包含提示语组成部分的方法,这些提示语作为输入传递给 AI 模型。 包含用于指导 AI 模型的输出和行为的说明文本。从 API 的角度来看,提示语由消息集合组成。

AI 模型处理两种主要类型的消息:用户消息(来自用户的直接输入)和系统消息(由系统生成以引导对话)。

这些消息通常包含占位符,这些占位符在运行时根据用户输入进行替换,以自定义 AI 模型对用户输入的响应。

还可以指定提示选项,例如要使用的 AI 模型的名称以及控制生成输出的随机性或创造力的温度设置。

ChatClient源码分析请移步到此处Spring AI之ChatClient源码分析 

创建 ChatClient

您可以获取任何 ChatModel Spring Boot 自动配置的自动配置实例,也可以以编程方式创建一个实例。

ChatClientChatClient.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.Builderspring.ai.chat.client.enabled=falseChatClient.BuilderChatModel

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 模型根据每个请求使用的令牌数量收费。

下面通过调用该方法来返回包含元数据的对象的示例。ChatResponsechatResponse()call()

ChatResponse chatResponse = chatClient.prompt() .user("Tell me a joke") .call() .chatResponse();

返回实体

您通常希望返回从返回的实体类映射的实体类。 该方法提供此功能。Stringentity

例如,给定 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);

还有一个带有签名的重载方法,可用于指定泛型列表等类型:entityentity(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();

您还可以使用方法流式传输 。ChatResponseFlux<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() 返回值

指定方法后,响应类型有几个不同的选项。callChatClient

String content():返回响应的 String 内容

ChatResponse chatResponse():返回包含多代的对象以及有关响应的元数据,例如,用于创建响应的令牌数。ChatResponse

entity返回 Java 类型

entity(ParameterizedTypeReference<T> type):用于返回实体类型的集合。

entity(Class<T> type):用于返回特定的实体类型。

entity(StructuredOutputConverter<T> structuredOutputConverter):用于指定 a 的实例以将 a 转换为实体类型。StructuredOutputConverterString

您还可以调用该方法,而不是 和streamcall

stream() 返回值

指定 on 的方法后,响应类型有几个选项:streamChatClient

Flux<String> content():返回 AI 模型生成的字符串的 Flux。

Flux<ChatResponse> chatResponse():返回对象的 Flux,其中包含有关响应的其他元数据。ChatResponse

使用默认值

在类中使用默认系统文本创建 ChatClient 可简化运行时代码。 通过设置默认值,您只需要在调用时指定用户文本,而无需为运行时代码路径中的每个请求设置系统文本。@ConfigurationChatClient

默认系统文本

在以下示例中,我们将系统文本配置为始终以盗版者的声音回复。 为了避免在运行时代码中重复系统文本,我们将在类中创建一个实例。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。ChatOptionsOpenAiChatOptionsChatOptions

defaultFunction(String name, String description, java.util.function.Function<I, O> function):用于在用户文本中引用函数。它解释了函数的用途,并帮助 AI 模型选择正确的函数以获得准确的响应。该参数是模型在必要时执行的 Java 函数实例。namedescriptionfunction

defaultFunctions(String…​ functionNames):在应用程序上下文中定义的 'java.util.Function' 的 Bean 名称。

defaultUser(String text)、 、 : 这些方法允许您定义用户文本。这允许您使用 lambda 来指定用户文本和任何默认参数。defaultUser(Resource text)defaultUser(Consumer<UserSpec> userSpecConsumer)Consumer<UserSpec>

defaultAdvisors(RequestResponseAdvisor…​ advisor):顾问程序允许修改用于创建 .该实现通过在提示后附加与用户文本相关的上下文信息来实现模式。PromptQuestionAnswerAdvisorRetrieval Augmented Generation

defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer):此方法允许您定义一个以使用 .顾问可以修改用于创建最终 .它允许您指定一个 lambda 来添加顾问,例如 ,它通过根据用户文本在提示后附加相关上下文信息来支持。ConsumerAdvisorSpecPromptConsumer<AdvisorSpec>QuestionAnswerAdvisorRetrieval 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)。VectorStoreQuestionAnswerAdvisorChatClient

ChatResponse response = ChatClient.builder(chatModel) .build().prompt() .advisors(new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults())) .user(userText) .call() .chatResponse();

在此示例中,将对矢量数据库中的所有文档执行相似性搜索。 为了限制搜索的文档类型,需要一个类似 SQL 的过滤器表达式,该表达式可移植到所有 .SearchRequest.defaults()SearchRequestVectorStores

聊天记忆

该接口表示聊天对话历史记录的存储。它提供了将消息添加到 * 对话,从对话中检索消息,并清除对话历史记录。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(); } }

首页
评论
分享
Top