跳到主要内容

聊天客户端 API

Deepseek 3.2 中英对照 Chat Client API

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

备注

请参阅本文档底部的 实现说明,其中涉及 ChatClient 中命令式与响应式编程模型结合使用的相关内容。

流畅API提供了一系列方法,用于构建提示的各个组成部分,这些提示将作为输入传递给AI模型。Prompt包含指导AI模型输出和行为的指令文本。从API的角度来看,提示由一系列消息组成。

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

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

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

创建聊天客户端

ChatClient 是通过 ChatClient.Builder 对象创建的。你可以为任何 ChatModel Spring Boot 自动配置获取一个自动配置的 ChatClient.Builder 实例,或者以编程方式创建一个。

使用自动配置的 ChatClient.Builder

在最简单的使用场景中,Spring AI 提供了 Spring Boot 自动配置功能,它会创建一个原型 ChatClient.Builder Bean,供你注入到自己的类中。以下是一个简单的示例,展示如何获取对用户简单请求的 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();
}
}

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

使用多个聊天模型

在单个应用程序中,有几种情况可能需要同时使用多个聊天模型:

  • 针对不同类型的任务使用不同的模型(例如,用强大模型处理复杂推理,用更快、更便宜的模型处理简单任务)

  • 当某个模型服务不可用时,实施备用机制

  • 对不同的模型或配置进行 A/B 测试

  • 基于用户偏好为其提供模型选择

  • 结合使用专用模型(一个用于代码生成,另一个用于创意内容等)

默认情况下,Spring AI 会自动配置一个 ChatClient.Builder Bean。然而,在您的应用中可能需要处理多个聊天模型。以下是处理这种情况的方法:

在所有情况下,你都需要通过设置属性 spring.ai.chat.client.enabled=false 来禁用 ChatClient.Builder 的自动配置。

这使您可以手动创建多个 ChatClient 实例。

单个模型类型支持多个 ChatClient

本节涵盖一个常见用例:您需要创建多个 ChatClient 实例,这些实例都使用相同的基础模型类型,但配置不同。

// Create ChatClient instances programmatically
ChatModel myChatModel = ... // already autoconfigured by Spring Boot
ChatClient chatClient = ChatClient.create(myChatModel);

// Or use the builder for more control
ChatClient.Builder builder = ChatClient.builder(myChatModel);
ChatClient customChatClient = builder
.defaultSystemPrompt("You are a helpful assistant.")
.build();

适用于不同模型类型的ChatClients

当使用多个AI模型时,您可以为每个模型定义独立的ChatClient bean:

import org.springframework.ai.chat.ChatClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatClientConfig {

@Bean
public ChatClient openAiChatClient(OpenAiChatModel chatModel) {
return ChatClient.create(chatModel);
}

@Bean
public ChatClient anthropicChatClient(AnthropicChatModel chatModel) {
return ChatClient.create(chatModel);
}
}

随后,您便可以通过 @Qualifier 注解将这些 bean 注入到您的应用程序组件中:

@Configuration
public class ChatClientExample {

@Bean
CommandLineRunner cli(
@Qualifier("openAiChatClient") ChatClient openAiChatClient,
@Qualifier("anthropicChatClient") ChatClient anthropicChatClient) {

return args -> {
var scanner = new Scanner(System.in);
ChatClient chat;

// Model selection
System.out.println("\nSelect your AI model:");
System.out.println("1. OpenAI");
System.out.println("2. Anthropic");
System.out.print("Enter your choice (1 or 2): ");

String choice = scanner.nextLine().trim();

if (choice.equals("1")) {
chat = openAiChatClient;
System.out.println("Using OpenAI model");
} else {
chat = anthropicChatClient;
System.out.println("Using Anthropic model");
}

// Use the selected chat client
System.out.print("\nEnter your question: ");
String input = scanner.nextLine();
String response = chat.prompt(input).call().content();
System.out.println("ASSISTANT: " + response);

scanner.close();
};
}
}

多个 OpenAI 兼容 API 端点

OpenAiApiOpenAiChatModel 类提供了一个 mutate() 方法,允许你使用不同的属性创建现有实例的变体。这在需要处理多个与 OpenAI 兼容的 API 时特别有用。

@Service
public class MultiModelService {

private static final Logger logger = LoggerFactory.getLogger(MultiModelService.class);

@Autowired
private OpenAiChatModel baseChatModel;

@Autowired
private OpenAiApi baseOpenAiApi;

public void multiClientFlow() {
try {
// Derive a new OpenAiApi for Groq (Llama3)
OpenAiApi groqApi = baseOpenAiApi.mutate()
.baseUrl("https://api.groq.com/openai")
.apiKey(System.getenv("GROQ_API_KEY"))
.build();

// Derive a new OpenAiApi for OpenAI GPT-4
OpenAiApi gpt4Api = baseOpenAiApi.mutate()
.baseUrl("https://api.openai.com")
.apiKey(System.getenv("OPENAI_API_KEY"))
.build();

// Derive a new OpenAiChatModel for Groq
OpenAiChatModel groqModel = baseChatModel.mutate()
.openAiApi(groqApi)
.defaultOptions(OpenAiChatOptions.builder().model("llama3-70b-8192").temperature(0.5).build())
.build();

// Derive a new OpenAiChatModel for GPT-4
OpenAiChatModel gpt4Model = baseChatModel.mutate()
.openAiApi(gpt4Api)
.defaultOptions(OpenAiChatOptions.builder().model("gpt-4").temperature(0.7).build())
.build();

// Simple prompt for both models
String prompt = "What is the capital of France?";

String groqResponse = ChatClient.builder(groqModel).build().prompt(prompt).call().content();
String gpt4Response = ChatClient.builder(gpt4Model).build().prompt(prompt).call().content();

logger.info("Groq (Llama3) response: {}", groqResponse);
logger.info("OpenAI GPT-4 response: {}", gpt4Response);
}
catch (Exception e) {
logger.error("Error in multi-client flow", e);
}
}
}

ChatClient Fluent API

ChatClient 的流畅 API 允许你通过重载的 prompt 方法以三种不同的方式创建提示,从而启动流畅 API:

  • prompt():此无参数方法允许您开始使用流畅 API,从而构建提示中的用户、系统及其他组成部分。

  • prompt(Prompt prompt):此方法接受一个 Prompt 参数,允许您传入使用 Prompt 的非流畅 API 创建的 Prompt 实例。

  • prompt(String content):这是一个便利方法,与上一个重载方法类似。它接受用户的文本内容。

ChatClient 响应

ChatClient API 提供了多种方式,可以通过流畅式 API 来格式化 AI 模型的响应。

返回 ChatResponse

AI模型的响应是一个由类型 [ChatResponse](../chatmodel/index.md#ChatResponse) 定义的丰富结构。它包含了关于响应生成方式的元数据,并且可以包含多个响应,每个响应被称为 Generation,各自拥有其元数据。这些元数据包括用于创建响应的令牌数量(每个令牌大约相当于 3/4 个单词)。这些信息非常重要,因为托管的 AI 模型根据每个请求使用的令牌数量进行计费。

下面是一个返回包含元数据的 ChatResponse 对象的示例,即在 call() 方法之后调用 chatResponse()

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

返回实体

您通常希望返回一个从返回的 String 映射而来的实体类。entity() 方法提供了这一功能。

例如,给定Java记录:

record ActorFilms(String actor, List<String> movies) {}

您可以使用 entity() 方法轻松地将AI模型的输出映射到此记录,如下所示:

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<ActorFilms>>() {});

原生结构化输出

随着越来越多的AI模型原生支持结构化输出,您可以在调用ChatClient时,通过使用AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT顾问参数来利用此功能。您可以在ChatClient.Builder上使用defaultAdvisors()方法为所有调用全局设置此参数,或如下所示,为每个调用单独设置:

ActorFilms actorFilms = chatClient.prompt()
.advisors(AdvisorParams.ENABLE_NATIVE_STRUCTURED_OUTPUT)
.user("Generate the filmography for a random actor.")
.call()
.entity(ActorFilms.class);
备注

某些AI模型(如OpenAI)本身不支持对象数组。在这种情况下,您可以使用Spring AI默认的结构化输出转换功能。

流式响应

stream() 方法允许您获取异步响应,如下所示:

Flux<String> output = chatClient.prompt()
.user("Tell me a joke")
.stream()
.content();

你也可以使用 Flux<ChatResponse> chatResponse() 方法来流式传输 ChatResponse

未来,我们将提供一个便捷方法,让您可以通过响应式 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", this.converter.getFormat()))
.stream()
.content();

String content = this.flux.collectList().block().stream().collect(Collectors.joining());

List<ActorsFilms> actorFilms = this.converter.convert(this.content);

提示模板

ChatClient的流式API允许您以模板形式提供用户和系统文本,其中的变量会在运行时被替换。

String answer = ChatClient.create(chatModel).prompt()
.user(u -> u
.text("Tell me the names of 5 movies whose soundtrack was composed by {composer}")
.param("composer", "John Williams"))
.call()
.content();

在内部,ChatClient 使用 PromptTemplate 类来处理用户和系统文本,并在运行时根据给定的 TemplateRenderer 实现,用提供的值替换变量。默认情况下,Spring AI 使用 StTemplateRenderer 实现,该实现基于由 Terence Parr 开发的开源 StringTemplate 引擎。

Spring AI 还提供了 NoOpTemplateRenderer,适用于不需要模板处理的情况。

备注

直接在 ChatClient 上配置的 TemplateRenderer(通过 .templateRenderer())仅适用于直接在 ChatClient 构建器链中定义的提示内容(例如通过 .user().system())。它不会影响 Advisors(如 QuestionAnswerAdvisor)内部使用的模板,这些模板有它们自己的模板定制机制(参见 自定义 Advisor 模板)。

如果你想使用不同的模板引擎,可以直接向 ChatClient 提供 TemplateRenderer 接口的自定义实现。你也可以继续使用默认的 StTemplateRenderer,但采用自定义配置。

例如,默认情况下,模板变量由 {} 语法标识。如果你计划在提示中包含 JSON,可能希望使用不同的语法以避免与 JSON 语法冲突。例如,你可以使用 <> 作为分隔符。

String answer = ChatClient.create(chatModel).prompt()
.user(u -> u
.text("Tell me the names of 5 movies whose soundtrack was composed by <composer>")
.param("composer", "John Williams"))
.templateRenderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
.call()
.content();

call() 的返回值

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

  • String content(): 返回响应的字符串内容。

  • ChatResponse chatResponse(): 返回包含多个生成结果以及响应元数据(例如创建响应所使用的 token 数量)的 ChatResponse 对象。

  • ChatClientResponse chatClientResponse(): 返回包含 ChatResponse 对象和 ChatClient 执行上下文的 ChatClientResponse 对象,让你可以访问在执行顾问(Advisor)期间使用的额外数据(例如,在 RAG 流程中检索到的相关文档)。

  • entity() 用于返回一个 Java 类型。

    • entity(ParameterizedTypeReference<T> type): 用于返回一个实体类型的 Collection
    • entity(Class<T> type): 用于返回一个特定的实体类型。
    • entity(StructuredOutputConverter<T> structuredOutputConverter): 用于指定一个 StructuredOutputConverter 的实例,以将 String 转换为实体类型。
  • responseEntity() 用于同时返回 ChatResponse 和一个 Java 类型。当你在一次调用中需要同时访问完整的 AI 模型响应(包含元数据和生成结果)和结构化的输出实体时,这非常有用。

    • responseEntity(Class<T> type): 用于返回一个包含完整 ChatResponse 对象和特定实体类型的 ResponseEntity
    • responseEntity(ParameterizedTypeReference<T> type): 用于返回一个包含完整 ChatResponse 对象和一个实体类型 CollectionResponseEntity
    • responseEntity(StructuredOutputConverter<T> structuredOutputConverter): 用于返回一个包含完整 ChatResponse 对象以及使用指定 StructuredOutputConverter 转换的实体的 ResponseEntity

你也可以调用 stream() 方法而不是 call()

备注

调用 call() 方法实际上并不会触发 AI 模型的执行。它只是指示 Spring AI 使用同步调用还是流式调用。真正的 AI 模型调用发生在诸如 content()chatResponse()responseEntity() 等方法被调用时。

stream() 返回值

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

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

  • Flux<ChatResponse> chatResponse(): 返回 ChatResponse 对象的 Flux,该对象包含有关响应的额外元数据。

  • Flux<ChatClientResponse> chatClientResponse(): 返回 ChatClientResponse 对象的 Flux,该对象包含 ChatResponse 对象和 ChatClient 执行上下文,使您能够访问在执行顾问期间使用的额外数据(例如,在 RAG 流程中检索到的相关文档)。

消息元数据

ChatClient支持在用户消息和系统消息中添加元数据。元数据提供了关于消息的额外上下文和信息,可供AI模型或下游处理使用。

为用户消息添加元数据

您可以使用 metadata() 方法向用户消息添加元数据:

// Adding individual metadata key-value pairs
String response = chatClient.prompt()
.user(u -> u.text("What's the weather like?")
.metadata("messageId", "msg-123")
.metadata("userId", "user-456")
.metadata("priority", "high"))
.call()
.content();

// Adding multiple metadata entries at once
Map<String, Object> userMetadata = Map.of(
"messageId", "msg-123",
"userId", "user-456",
"timestamp", System.currentTimeMillis()
);

String response = chatClient.prompt()
.user(u -> u.text("What's the weather like?")
.metadata(userMetadata))
.call()
.content();

为系统消息添加元数据

同样地,您可以为系统消息添加元数据:

// Adding metadata to system messages
String response = chatClient.prompt()
.system(s -> s.text("You are a helpful assistant.")
.metadata("version", "1.0")
.metadata("model", "gpt-4"))
.user("Tell me a joke")
.call()
.content();

默认元数据支持

你也可以在ChatClient构建器级别配置默认元数据:

@Configuration
class Config {
@Bean
ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem(s -> s.text("You are a helpful assistant")
.metadata("assistantType", "general")
.metadata("version", "1.0"))
.defaultUser(u -> u.text("Default user context")
.metadata("sessionId", "default-session"))
.build();
}
}

元数据验证

ChatClient验证元数据以确保数据完整性:

  • 元数据键不能为空或空字符串

  • 元数据值不能为空

  • 当传递 Map 时,键和值都不能包含空元素

// This will throw an IllegalArgumentException
chatClient.prompt()
.user(u -> u.text("Hello")
.metadata(null, "value")) // Invalid: null key
.call()
.content();

// This will also throw an IllegalArgumentException
chatClient.prompt()
.user(u -> u.text("Hello")
.metadata("key", null)) // Invalid: null value
.call()
.content();

访问元数据

元数据包含在生成的UserMessage和SystemMessage对象中,可通过消息的getMetadata()方法访问。这在处理顾问中的消息或检查对话历史时尤其有用。

使用默认值

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

默认系统文本

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

@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", 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):传入 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):Advisor 允许修改用于创建 Prompt 的数据。QuestionAnswerAdvisor 实现通过向提示中添加与用户文本相关的上下文信息,支持 Retrieval Augmented Generation 模式。

  • defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer):此方法允许您定义一个 Consumer,使用 AdvisorSpec 配置多个 advisor。Advisor 可以修改用于创建最终 Prompt 的数据。Consumer<AdvisorSpec> 允许您指定一个 lambda 表达式来添加 advisor,例如 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 流式 API 提供了用于配置建议器的 AdvisorSpec 接口。该接口提供了一系列方法,用于添加参数、一次性设置多个参数,以及向链中添加一个或多个建议器。

interface AdvisorSpec {
AdvisorSpec param(String k, Object v);
AdvisorSpec params(Map<String, Object> p);
AdvisorSpec advisors(Advisor... advisors);
AdvisorSpec advisors(List<Advisor> advisors);
}

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

ChatClient.builder(chatModel)
.build()
.prompt()
.advisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.user(userText)
.call()
.content();

在这种配置下,MessageChatMemoryAdvisor 将首先执行,把对话历史添加到提示中。随后,QuestionAnswerAdvisor 将基于用户的问题和添加的对话历史进行搜索,从而可能提供更相关的结果。

检索增强生成

请参考检索增强生成指南。

日志记录

SimpleLoggerAdvisor 是一个用于记录 ChatClientrequestresponse 数据的通知器。这对于调试和监控您的 AI 交互非常有用。

提示

Spring AI 支持对大型语言模型(LLM)和向量存储交互的可观测性。更多信息,请参阅 可观测性 指南。

要启用日志记录功能,请在创建ChatClient时向顾问链添加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

将此内容添加到你的 application.propertiesapplication.yaml 文件中。

你可以通过以下构造函数自定义记录 AdvisedRequestChatResponse 中的哪些数据:

SimpleLoggerAdvisor(
Function<ChatClientRequest, String> requestToString,
Function<ChatResponse, String> responseToString,
int order
)

示例用法:

SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
request -> "Custom request: " + request.prompt().getUserMessage(),
response -> "Custom response: " + response.getResult(),
0
);

这使您能够根据特定需求定制日志信息。

:::提示
在生产环境中记录敏感信息时需谨慎。
:::

聊天记忆

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

目前有一个内置实现:MessageWindowChatMemory

MessageWindowChatMemory 是一种聊天记忆实现,它会维护一个达到指定最大容量(默认:20条消息)的消息窗口。当消息数量超过这个限制时,较早的消息会被移除,但系统消息会被保留。如果添加了一条新的系统消息,所有之前的系统消息都会从记忆中删除。这确保了对话中最新的上下文始终可用,同时将内存使用控制在有限范围内。

MessageWindowChatMemoryChatMemoryRepository 抽象支持,该抽象为聊天对话内存提供了存储实现。现有多种实现可用,包括 InMemoryChatMemoryRepositoryJdbcChatMemoryRepositoryCassandraChatMemoryRepositoryNeo4jChatMemoryRepository

有关更多详情和使用示例,请参阅 Chat Memory 文档。

实现说明

ChatClient中结合使用命令式和响应式编程模型是该API的一个独特方面。通常,应用程序要么采用响应式编程,要么采用命令式编程,而不会两者兼用。

  • 当定制模型实现的HTTP客户端交互时,必须同时配置RestClient和WebClient。
important

由于 Spring Boot 3.4 中存在一个 bug,必须设置属性 "spring.http.client.factory=jdk"。否则,默认会设置为 "reactor",这会破坏某些 AI 工作流程,例如 ImageModel。

  • 流式处理仅支持通过响应式栈实现。因此,命令式应用程序必须包含响应式栈(例如 spring-boot-starter-webflux)。

  • 非流式处理仅支持通过 Servlet 栈实现。因此,响应式应用程序必须包含 Servlet 栈(例如 spring-boot-starter-web),并预期某些调用是阻塞的。

  • 工具调用是命令式的,会导致阻塞的工作流程。这也导致了部分/中断的 Micrometer 观测(例如,ChatClient 的追踪和工具调用的追踪没有连接,因此第一个追踪会保持未完成状态)。

  • 内置的顾问器对标准调用执行阻塞操作,对流式调用执行非阻塞操作。用于顾问器流式调用的 Reactor Scheduler 可以通过每个 Advisor 类上的 Builder 进行配置。

章节总结