|
此版本仍在开发中,尚未被视为稳定版。如需最新的快照版本,请使用 Spring AI 1.1.3! |
聊天客户端API
ChatClient 提供了一种流畅的API来与AI模型通信。它支持同步和流式编程模型。
|
见本文件底部有关在 |
AI模型处理两种主要类型的消息:用户消息,这些是直接来自用户的输入;系统消息,这些是由系统生成的以引导对话的消息。
这些消息通常包含占位符,在运行时根据用户输入进行替换,以自定义AI模型对用户输入的响应。
也有可以指定的提示选项,例如要使用的AI模型名称以及控制生成输出随机性或创造力的温度设置。
创建 ChatClient
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();
不同模型类型的ChatClient
在处理多个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);
}
}
然后可以使用 @0 注解将这些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端点
OpenAiApi 和 OpenAiChatModel 类提供了一个 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 流畅API
The ChatClient 流畅API允许您通过重载的 prompt 方法以三种不同的方式创建提示:
-
prompt(): 这个没有参数的方法让你能够开始使用流畅API,允许你构建用户、系统以及其他部分的提示。 -
prompt(Prompt prompt): 这个方法接受一个Prompt参数,让你可以使用 Prompt 的非流畅 API 创建的Prompt实例进行传递。 -
prompt(String content): 这是一个与上一个重载类似的便捷方法。它接收用户的文本内容。
ChatClient 响应
The ChatClient API 提供了几种使用流畅API格式化AI模型响应的方法。
返回聊天响应
The response from the AI model is a rich structure defined by the type ChatResponse.
It includes metadata about how the response was generated and can also contain multiple responses, known as 生成s, each with its own metadata.
The metadata includes the number of tokens (each token is approximately 3/4 of a word) used to create the response. This information is important because hosted AI models charge based on the number of tokens used per request.
以下是一个示例,通过调用call()方法后的chatResponse()方法,返回包含元数据的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(ParameterizedTypeReference<T> type)的重载entity方法,该方法允许您指定如泛型列表这样的类型:
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顾问参数来利用这一功能。
您可以使用defaultAdvisors()方法在ChatClient.Builder上设置此参数以全局方式应用于所有调用,或者如下面所示按每次调用来设置它:
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);
提示模板
The 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)内部使用的模板,这些顾问拥有自己的模板自定义机制(请参阅 自定义顾问模板)。 |
如果希望使用不同的模板引擎,可以直接将自定义的TemplateRenderer接口实现提供给ChatClient。您也可以继续使用默认的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() 返回值
在指定 call() 方法于 ChatClient 后,响应类型有几种不同的选择。
-
String content(): 返回响应的内容字符串 -
ChatResponse chatResponse(): 返回包含多个生成结果和响应元数据的对象,例如创建响应时使用了多少个Tokens。 -
ChatClientResponse chatClientResponse(): 返回一个包含ChatResponse对象和 ChatClient 执行上下文的ChatClientResponse对象,让你可以访问执行顾问期间使用的附加数据(例如,在 RAG 流程中检索的相关文档)。 -
entity()用于返回Java类型-
entity(ParameterizedTypeReference<T> type): 用于返回实体类型的Collection。 -
entity(Class<T> type): 用于返回特定实体类型。 -
entity(StructuredOutputConverter<T> structuredOutputConverter): 用于指定一个StructuredOutputConverter的实例,将其转换为一种String实体类型。
-
-
responseEntity()用于返回完整的AI模型响应(包括元数据和生成内容)以及结构化的输出实体。这在需要在一个调用中同时访问这两者时非常有用。-
responseEntity(Class<T> type): 用于返回一个包含完整ChatResponse对象和特定实体类型的ResponseEntity。 -
responseEntity(ParameterizedTypeReference<T> type): 用于返回一个包含完整ChatResponse对象和实体类型列表的ResponseEntity。 -
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(): 返回一个Flux的ChatResponse对象,该对象包含关于响应的附加元数据。 -
Flux<ChatClientResponse> chatClientResponse(): 返回包含ChatClientResponse对象和ChatResponse对象的Flux对象,为您提供在顾问执行过程中使用的附加数据访问权限(例如,在RAG流程中检索的相关文档)。
消息元数据
The 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();
添加系统消息元数据
同样,您还可以为系统消息添加元数据:<br/>
// 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 验证元数据以确保数据完整性:
-
元数据键不能为空或空字符串
-
元数据值不能为null
-
当传递一个Map时,键和值均不能包含null元素
// 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();
使用默认设置
创建一个ChatClient类中的默认系统文本的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 调用应用端点时,结果为:<br />
❯ 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 调用应用端点的结果是:<br/>
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): Thename用于引用用户文本中的功能。
Thedescription解释该功能的目的,并帮助AI模型选择正确的函数以获得准确的响应。
Thefunction参数是一个Java函数实例,当需要时,模型将执行此函数。 -
defaultFunctions(String… functionNames): 在应用上下文中定义的 `java.util.Function` 的 bean 名称。 -
defaultUser(String text),defaultUser(Resource text),defaultUser(Consumer<UserSpec> userSpecConsumer): 这些方法让你定义用户文本。Consumer<UserSpec>允许你使用 lambda 表达式来指定用户文本和任何默认参数。 -
defaultAdvisors(Advisor… advisor): 顾问允许修改用于创建Prompt的数据。
QuestionAnswerAdvisor实现通过在提示中附加与用户文本相关的内容信息,启用Retrieval Augmented Generation模式。 -
defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer): 此方法允许您使用Consumer配置多个顾问。您可以使用AdvisorSpec来定义多个顾问,这些顾问可以修改用于创建最终Prompt的数据。Consumer<AdvisorSpec>让您可以指定一个 lambda 表达式来添加顾问,例如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)
顾问
The 顾问API 提供了一种灵活且强大的方式,用于拦截、修改和增强您Spring应用程序中的AI驱动交互。
使用用户文本调用AI模型时,一个常见的模式是将上下文数据附加到提示词或扩充提示词。
此上下文数据可以是不同类型的。常见类型包括:
-
您自己的数据:这指的是AI模型未对其进行训练的数据。 即使模型见过类似数据,附加的上下文数据在生成响应时具有优先权。
-
会话历史记录: 聊天模型的API是无状态的。 如果你告诉AI模型你的名字,它在后续互动中不会记住。 为了确保之前的交互被考虑进去以生成响应,每次请求时必须发送会话历史记录。
ChatClient 中的 Advisor 配置
The 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);
}
| <p>将顾问添加到链中的顺序至关重要,因为它决定了它们执行的顺序。</p> <p>每个顾问以某种方式修改提示或上下文,并且一个顾问所做的更改会传递给链中的下一个顾问。</p> |
ChatClient.builder(chatModel)
.build()
.prompt()
.advisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.user(userText)
.call()
.content();
在该配置中,MessageChatMemoryAdvisor 将首先被执行,将对话历史添加到提示中。
随后,QuestionAnswerAdvisor 将根据用户的提问和添加的对话历史进行搜索,有可能提供更相关的结果。
检索增强生成
参见检索增强生成指南。
日志记录
The SimpleLoggerAdvisor 是一个顾问,记录 request 和 response 关于 ChatClient 的数据。这在调试和监控您的 AI 交互时可能会很有用。
| Spring AI 支持对大语言模型和向量存储交互的可观测性。 参见 可观测性 指南以获取更多信息。 |
要启用日志记录,请在创建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.properties或application.yaml文件中添加此内容。
您可以使用以下构造函数来自定义从AdvisedRequest和ChatResponse记录的数据:
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 条消息)。
当消息数量超过这个限制时,会移除较旧的消息,但保留系统消息。
如果添加了新的系统消息,则会从内存中清除所有之前的系统消息。
这确保了始终可以获取最近的上下文,并且保持内存使用量处于可控制范围内。
MessageWindowChatMemory 由 ChatMemoryRepository 抽象支持,该抽象为聊天对话内存提供了存储实现。
有多种可用的实现,包括 InMemoryChatMemoryRepository、JdbcChatMemoryRepository、CassandraChatMemoryRepository、Neo4jChatMemoryRepository、CosmosDBChatMemoryRepository、MongoChatMemoryRepository 和 RedisChatMemoryRepository。
对于更多详细信息和使用示例,请参阅聊天记忆文档。
实施说明
在框架ChatClient中结合使用命令式和响应式编程模型是一种独特的API特性。
通常,一个应用程序要么是响应式的,要么是命令式的,但不会两者兼具。
-
当自定义模型实现的HTTP客户端交互时,必须配置RestClient和WebClient。
|
由于Spring Boot 3.4中存在的bug,必须设置"spring.http.client.factory=jdk"属性。 否则,默认情况下它会被设置为"reactor",这会破坏某些AI工作流,如ImageModel。 |
-
流式处理仅通过Reactive栈支持。 imperative应用程序由于这个原因必须包含Reactive栈(例如,spring-boot-starter-webflux)。
-
非流式处理仅通过Servlet堆栈支持。 响应式应用程序为了这个原因必须包含Servlet堆栈(例如spring-boot-starter-web),并且预期某些调用会阻塞。
-
工具调用是强制性的,会导致阻塞的工作流。 这也导致了部分/中断的Micrometer观测(例如,ChatClient跨度和工具调用跨度没有连接,前者因此保持不完整)。
-
内置顾问对标准调用执行阻塞操作,对流式调用执行非阻塞操作。 顾问的流式调用所使用的Reactor调度器可以通过每个顾问类上的Builder进行配置。