SpringAI 02 – Chat Client API

ChatClient 提供了与AI模型交互的fluent API。它同时支持同步和流式编程模型。

ChatClient fluent API拥有构建传递给AI模型的提示(Prompt)的组成部分的方法。提示包含指导AI模型输出和行为的指令文本。从API的角度来看,提示由一系列消息组成。

AI模型处理两种主要类型的消息:

  • 用户消息,即用户的直接输入。
  • 系统消息,由系统生成以指导对话。

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

还可以指定一些提示选项,例如:

  • AI模型的名称,即要使用的AI模型的名称。
  • 温度设置,控制生成输出的随机性或创造性。

这些功能使得ChatClient成为一个强大的工具,允许开发者以灵活的方式与AI模型进行交互,并通过定制化的提示和消息来优化AI模型的响应。

创建 ChatClient

使用 ChatClient.Builder 对象创建 ChatClient。你可以为任何 ChatModel SpringBoot 自动配置获取自动配置的 ChatClient.Builder 实例,或者手动创建一个。

使用自动配置的 ChatClient.Builder

在最简单的用例中,Spring AI 通过 SpringBoot 自动配置创建了一个 ChatClient.Builder 实例原型,使用时可以将其注入到类中。以下是一个简单的示例,用于检索对简单用户请求的字符串响应。

在这个简单的例子中,用户输入设置了用户消息的内容。call() 方法向 AI 模型发送请求,content() 方法将 AI 模型的响应作为字符串返回。

手动创建 ChatClient

可以通过设置属性 spring.ai.chat.client.enabled=false 来禁用 ChatClient.Builder 自动配置。当同时使用多个聊天模型时,这个配置会很有用。然后,为每个需要的 ChatModel 手动创建一个 ChatClient.Builder 实例:

ChatClient Fluent API

ChatClient fluent API 允许使用重载的 prompt() 方法以三种不同的方式创建提示(Prompt),以启动fluent API:

  • prompt(): 此方法不接受任何参数就可以开始使用fluent API,允许您构建用户、系统和其它提示(prompt)部分。
  • prompt(Prompt prompt): 此方法接受一个 Prompt 实例作为参数,这个参数可以是一个非fluent API 创建的 Prompt 实例。
  • prompt(String content): 这是一个快捷方法,类似于之前重载的方法,它接受用户文本内容作为参数。

ChatClient 响应

ChatClient API 提供了几种格式化 AI 模型响应内容的方法。

返回 ChatResponse

AI 模型的响应是一个由类型 ChatResponse 定义的复杂结构。ChatResponse中包含关于生成响应的元数据,并且还可以包含多个响应,称为 Generations,每个Generation都有自己的元数据。元数据还包括用于创建响应的token数量(每个token大概是 3/4 个单词)。这个信息很重要,因为托管的 AI 模型会根据每个请求中使用的token数量收费。

以下示例展示了如何获取包含元数据的 ChatResponse 对象的过程,ChatResponse实例是在 call() 方法后执行 chatResponse() 方法获得:

返回Entity

有时会希望将返回的字符串映射为某个特定的实体类的对象。entity() 方法提供了这个功能。

比如下面的 Java record类:

可以使用 entity() 方法轻松地将 AI 模型的输出结果映射为这个record类,如下所示:

还有一个重载的 entity() 方法,方法签名为 entity(ParameterizedTypeReference type),让您可以指定更复杂的类型,如List泛型:

流式响应

使用 stream() 方法可以像下面这样获得异步响应:

也可以使用 Flux<ChatResponse> chatResponse() 方法流式传输 ChatResponse 对象结果。

Spring AI将提供一个更便捷的方法,让开发者能够使用反应式 stream() 方法返回 Java Entity结果。与此同时,还可以使用结构化输出转换器( Structured Output Converter )来显式转换聚合的响应结果,就跟下面的例子一样。这个例子里也展示了fluent API 中参数的使用,具体将在文档的后续部分详细讨论。

call() 方法返回值

在 ChatClient 上指定 call() 方法后,有如下几种不同的响应类型选项。

  • String content(): 返回字符串格式的响应结果
  • ChatResponse chatResponse(): 返回包含多个Generation和响应元数据的 ChatResponse 对象,例如用于创建响应的token数量。
  • entity() 返回指定 Java 类型的返回结果
    • entity(ParameterizedTypeReference<T> type): 用于返回集合类型的结果(支持泛型)。
    • entity(Class type): 用于返回特定类型的结果。
    • entity(StructuredOutputConverter structuredOutputConverter): 可以指定 StructuredOutputConverter 实例,将字符串转换为需要的类型。

也可以使用 stream() 方法来替换 call()方法。

stream() 方法返回值

在 ChatClient 上指定 stream() 方法后,有几种响应类型选项:

  • Flux<String> content(): 返回 AI 模型生成的字符串Flux对象。
  • Flux chatResponse(): 返回包含响应的额外元数据的 ChatResponse 对象的 Flux对象。

使用默认值

使用默认系统文本在 @Configuration 类中创建ChatClient可以 简化运行时代码。通过设置默认值,在调用 ChatClient 时只需要指定用户文本,这样在运行时代码路径中就不需要再为每个请求设置系统文本了。

默认系统文本

以下面的例子中,我们将系统文本配置为始终以海盗的声音回复。为了避免在运行时代码中重复系统文本,我们将在@Configuration类中创建一个ChatClient实例:

以及一个调用这个ChatClient实例的 @RestController接口:

通过 curl 命令来调用这个接口,收到的返回结果是:

带有参数的默认系统文本

以下示例中,我们将在系统文本中通过占位符在运行时指定声音类型而不是设计时指定声音类型。

通过 httpie 调用应用程序接口的结果如下:

 

其他默认值

ChatClient.Builder 级别,可以指定默认的提示(prompt)配置:

  • 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<UserSpec> userSpecConsumer): 这些方法用于定义用户文本。Consumer<UserSpec> 用于使用 lambda 来指定用户文本和任何默认参数。
  • defaultAdvisors(Advisor… advisor)Advisors 允许修改用于创建**提示(Prompt)**的数据。QuestionAnswerAdvisor 通过将提示与用户文本相关的上下文信息附加在一起实现了启用检索增强生成(RAG)模式。
  • defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer): 此方法支持使用 AdvisorSpec 配置多个AdvisorAdvisor可以修改用于创建最终**提示(Prompt)**的数据。Consumer<AdvisorSpec> 通过指定一个 lambda 来添加顾问,如 QuestionAnswerAdvisor,它通过将提示与基于用户文本的相关上下文信息附加在一起来支持检索增强生成(RAG)。

可以使用没有 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 userSpecConsumer)
  • advisors(Advisor… advisor)
  • advisors(Consumer advisorSpecConsumer)

Advisor

Advisor API 提供了一种灵活而强大的方式,用于拦截、修改和增强 Spring 应用程序中的AI驱动交互。

使用用户文本调用 AI 模型时的通用模式是用上下文数据追加或增强提示。

这些上下文数据可以是不同类型的。常见类型包括:

  • 您自己的数据: 这是 AI 模型未经训练的数据。即使模型已经看到过类似的数据,追加的上下文数据在生成响应时仍会被优先考虑。
  • 对话历史记录: 聊天模型的 API 是无状态的。如果你告诉 AI 模型你的名字,它在后续交互中也不会记住它。必须将对话历史记录与每个请求一起发送,以确保在生成响应时考虑之前的交互。

ChatClient 中的 Advisor 配置

ChatClient 的 fluent API 提供了一个AdvisorSpec 接口来配置Advisor。这个接口提供了添加参数、一次设置多个参数和向调用链中添加一个或多个Advisor的方法。

向链中添加Advisor的顺序至关重要,因为它决定了它们的执行顺序。每个Advisor以某种方式修改提示或上下文,一个Advisor所做的更改会传递给链中的下一个。

在此配置中,MessageChatMemoryAdvisor 将首先执行,将对话历史记录添加到提示中。然后QuestionAnswerAdvisor 将根据用户的问题和添加的对话历史记录执行搜索,这样就可能会提供更相关的结果。

点此了解更多关于问题回答Advisor相关的内容

检索增强生成(RAG)

向量数据库存储了 AI 模型不知道的数据。当用户问题发送到 AI 模型时,QuestionAnswerAdvisor 会为与用户问题相关的文档查询向量数据库。

向量数据库的响应被追加到用户文本中,为 AI 模型生成响应提供上下文。

假设已经将数据加载到 VectorStore 中,就可以通过向 ChatClient 提供 QuestionAnswerAdvisor实例来执行检索增强生成(RAG)。

在此示例中,SearchRequest.defaults() 将在向量数据库中对所有文档执行相似性搜索。要限制搜索的文档类型,SearchRequest 接受一个 SQL 样式的过滤表达式,该表达式在所有 VectorStores 中都是可移植的。

动态过滤表达式

使用 FILTER_EXPRESSION Advisor上下文参数在运行时更新 SearchRequest 过滤表达式:

FILTER_EXPRESSION 参数允许根据提供的表达式动态过滤搜索结果。

聊天记忆

ChatMemory 接口表示对聊天对话历史的存储。它提供将消息添加到对话、从对话中检索消息和清除对话历史记录的方法。

目前有两种实现,InMemoryChatMemory 和 CassandraChatMemory,分别提供基于内存和TTL持久存储的聊天对话历史记录存储支持。

创建TTL的 CassandraChatMemory

以下Advisor的实现使用 ChatMemory 接口与对话历史记录来建议提示,它们在如何将内存添加到提示的细节上有所不同:

  • MessageChatMemoryAdvisor:内存被检索并作为一系列消息添加到提示中
  • PromptChatMemoryAdvisor:内存被检索并添加到提示的系统文本中。
  • VectorStoreChatMemoryAdvisor:构造函数 VectorStoreChatMemoryAdvisor(VectorStore vectorStore, String defaultConversationId, int chatHistoryWindowSize, int order) 允许您:
    1. 指定用于管理和查询文档的 VectorStore 实例。
    2. 设置如果在上下文中未提供,则使用的默认对话 ID。
    3. 定义以token数量为单位的聊天历史记录检索窗口规格。
    4. 提供用于聊天Advisor系统的系统文本建议。
    5. 设置Advisor在链中的优先级顺序。

VectorStoreChatMemoryAdvisor.builder() 方法用于指定默认对话 ID、聊天历史记录窗口大小和要检索的聊天历史的顺序。

以下是一个使用多个Advisor的 @Service 实现示例:

日志记录

SimpleLoggerAdvisor 是一个记录 ChatClient 请求和响应数据的Advisor。这对于调试和监控 AI 交互很有用。

Spring AI 支持对 LLM 和向量存储交互的可观察性。有关更多信息,请参考可观测性指南。

要启用日志记录,在创建 ChatClient 实例时需要将 SimpleLoggerAdvisor 添加到Advisor链中。建议将其添加到链的末尾:

要查看日志,将顾问包的日志级别设置为 DEBUG

将这行配置添加到 application.properties 或 application.yam 文件中。

可以使用以下构造函数自定义记录的 AdvisedRequest 和 ChatResponse 数据:

示例用法:

这允许根据特定需求定制日志信息。

在生产环境中记录敏感信息时要小心。

END!!

发表评论

这个站点使用 Akismet 来减少垃圾评论。了解你的评论数据如何被处理