聊天客户端 API
本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springdoc.cn 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。 |
ChatClient
通过 Fluent API 与 AI 模型交互,同时支持同步和流式编程模型。
有关 |
Fluent API 提供构建 Prompt 组件的方法,这些组件将作为输入传递给 AI 模型。Prompt 包含指导 AI 模型输出和行为的指令文本。从 API 角度看,提示词由消息集合构成。
AI 模型处理两类主要消息:用户消息(直接来自用户的输入)和系统消息(由系统生成以引导对话)。
这些消息通常包含占位符,运行时根据用户输入进行替换,从而定制 AI 模型对用户输入的响应。
还可以指定一些 Prompt 选项,如要使用的 AI 模型名称,以及控制生成输出随机性/创造性的 temperature
参数。
创建 ChatClient
ChatClient
通过 ChatClient.Builder
对象创建。你可获取 Spring Boot 自动配置的 ChatModel 对应的 ChatClient.Builder
实例,或以编程式自行构建。
使用自动配置的 ChatClient.Builder
在最简单的使用场景中,Spring AI 通过 Spring Boot 自动配置生成 ChatClient.Builder
Prototype 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();
}
}
此示例中,userInput
为用户消息内容。call()
方法向 AI 模型发送请求, content()
方法以 String
形式返回模型响应。
多聊天模型协作
单一应用中需使用多个聊天模型的典型场景包括:
-
不同任务类型选用不同模型(如复杂推理用高性能模型,简单任务用快速经济型模型)
-
主模型服务不可用时启用备用机制
-
不同模型或配置的 A/B 测试
-
根据用户偏好提供可选的模型
-
组合专用模型(如代码生成与创意内容分别使用不同模型)
Spring AI 默认自动配置单个 ChatClient.Builder
Bean,但应用中可能需要使用多个聊天模型。处理方法如下:
所有场景均需通过设置属性 spring.ai.chat.client.enabled=false
来禁用 ChatClient.Builder
自动配置。
该设置允许手动创建多个 ChatClient
实例。
单一模型类型下的多ChatClient实例
本节涵盖常见场景:需创建多个使用相同底层模型类型但配置不同的 ChatClient
实例。
// 以编程式创建 ChatClient实 例
ChatModel myChatModel = ... // 已由Spring Boot自动配置完成
ChatClient chatClient = ChatClient.create(myChatModel);
// 或使用 Builder 实现更精细控制
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);
}
}
随后可通过 @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 端点
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 Fluent 风格 API
ChatClient
Fluent 式 API 通过重载 prompt
方法提供三种提示词创建方式:
-
`prompt()
:无参方法启动 Fluent 式API,支持逐步构建用户消息、系统消息等提示词组件。 -
prompt(Prompt prompt)
:接收Prompt
参数,支持通过非 Fluent 式 API 构建的 Prompt 实例。 -
prompt(String content)
:便捷方法,接收用户文本内容,功能类似前项重载。
ChatClient 响应
ChatClient
API 通过 Fluent 式接口提供多 种AI 模型响应格式化方式。
返回 ChatResponse
AI 模型返回的 ChatResponse
是包含丰富信息的结构化响应:
-
元数据:响应生成详情
-
多响应支持:Generations 数组(每个含独立元数据)
-
Token 统计:约 3/4 单词计为 1 个 Token(计费依据)
下面是在 call()
方法后调用 chatResponse()
返回包含元数据的 ChatResponse
对象的示例。
ChatResponse chatResponse = chatClient.prompt()
.user("Tell me a joke")
.call()
.chatResponse();
返回实体
通常需要将返回的 String
映射为实体类,entity()
方法正提供此功能。
例如,给定一个 Java record:
record ActorFilms(String actor, List<String> movies) {}
如下所示,通过 entity()
方法可轻松将 AI 模型输出映射至该 record 类:
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>>() {});
流式响应
stream()
方法支持异步获取响应,示例如下:
Flux<String> output = chatClient.prompt()
.user("Tell me a joke")
.stream()
.content();
也可通过 chatResponse()
方法以 Flux<ChatResponse>
流式获取响应。
未来版本将提供便捷方法支持响应式流直接返回 Java 实体。当前版本需通过 Structured Output Converter(结构化输出转换器)显式聚合响应,如下所示。这也演示了 Fluent 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<ActorFilms> actorFilms = this.converter.convert(this.content);
提示模版
ChatClient
Fuent 式 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 默认采用基于 Terence Parr 开发的 StringTemplate 引擎的 StTemplateRenderer
实现。
Spring AI 还提供 NoOpTemplateRenderer
,用于无需模板处理的场景。
Spring AI 还提供 NoOpTemplateRenderer
实现。
通过 .templateRenderer() 在 ChatClient 上直接配置的 TemplateRenderer 仅作用于构建链(Builder Chain)中直接定义的提示内容(如 .user() / .system() )。它不会影响 Advisor(如 QuestionAnswerAdvisor )内部使用的模板 — 这些模板有独立的定制机制(参见 自定义Advisor模板 章节)。
|
如需改用其他模板引擎,可直接向 ChatClient
提供 TemplateRenderer
接口的自定义实现。也可保留默认 StTemplateRenderer
但进行自定义配置。
例如,默认模板变量采用 {}
语法。若提示词中包含 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
转为实体类型。
-
也可使用 stream()
方法替代 call()
。
stream() 返回值
ChatClient
的 stream()
方法调用后,响应类型有以下处理选项:
-
Flux<String> content()
:返回 AI 模型生成字符串的 Flux 流。 -
Flux<ChatResponse> chatResponse()
:返回包含响应元数据的ChatResponse
对象Flux
流。 -
Flux<ChatClientResponse> chatClientResponse()
:返回包含ChatResponse
对象和ChatClient
执行上下文的ChatClientResponse
对象 Flux 流,可访问 Advisor 执行期间的附加数据(如 RAG 流程检索的相关文档)。
使用默认值
在 @Configuration
类中为 ChatClient
配置默认 system
(系统)消息可简化运行时代码。预设默认值后,调用时仅需指定 user
消息,无需每次请求重复设置系统消息。
默认的系统消息
以下示例将系统消息配置为始终以海盗口吻回复。为避免在运行时代码中重复系统消息,我们将在 @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
实现的 JavaDoc 文档。 -
defaultFunction(String name, String description, java.util.function.Function<I, O> function)
:-
name
:函数在用户文本中的引用标识 -
description
:描述函数用途,辅助AI模型精准选择函数 -
function
:模型按需调用的 JavaFunction
实例
-
-
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
实现通过追加与用户消息相关的上下文信息,启用检索增强生成(RAG)模式。 -
defaultAdvisors(Consumer<AdvisorSpec> advisorSpecConsumer)
: 该方法允许通过Consumer
使用AdvisorSpec
配置多个 Advisor。Advisor 可修改最终Prompt
的生成数据。Consumer<AdvisorSpec>
支持以 lambda 形式添加 Advisor(如QuestionAnswerAdvisor
),该 Advisor 基于用户消息追加相关上下文信息,实现检索增强生成(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<UserSpec> userSpecConsumer)
-
advisors(Advisor… advisor)
-
advisors(Consumer<AdvisorSpec> advisorSpecConsumer)
Advisor
Advisor API 为 Spring 应用中的 AI 驱动交互提供灵活强大的拦截、修改和增强能力。
调用 AI 模型时,常见模式是在用户消息基础上追加或增强上下文数据。
此类上下文数据可分为多种类型,常见包括:
-
自有数据:AI 模型未训练过的数据。即使模型接触过类似数据,追加的上下文数据仍会优先影响响应生成。
-
对话历史:聊天模型 API 是无状态的。若告知 AI 模型你的姓名,后续交互中它不会记住。必须每次请求都发送对话历史,确保生成响应时考虑先前交互。
ChatClient 中的 Advisor 配置
ChatClient
Fluent 式 API 提供 AdvisorSpec
接口用于配置 Advisor。该接口支持添加参数、批量设置参数以及向链中添加单个或多个 Advisor。
interface AdvisorSpec {
AdvisorSpec param(String k, Object v);
AdvisorSpec params(Map<String, Object> p);
AdvisorSpec advisors(Advisor... advisors);
AdvisorSpec advisors(List<Advisor> advisors);
}
Advisor 加入链的顺序至关重要,它决定了它们的执行顺序。每个 Advisor 都会以某种方式修改提示词或上下文,且一个 Advisor 所做的更改会传递给链中的下一个 Advisor。 |
ChatClient.builder(chatModel)
.build()
.prompt()
.advisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.user(userText)
.call()
.content();
在此配置中,MessageChatMemoryAdvisor
将首先执行,将对话历史添加到提示词中。随后,QuestionAnswerAdvisor
将基于用户问题和添加的对话历史执行搜索,可能提供更相关的结果。
检索增强生成
请参阅 R检索增强生成指南。
日志
SimpleLoggerAdvisor
是一个记录 ChatClient
请求和响应数据的 Advisor,可用于调试和监控 AI 交互。
Spring AI 支持对 LLM 和向量存储交互的可观测性。详情请参阅 可观测性指南。 |
启用日志记录需在创建 ChatClient
时向 Advisor 链添加 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<AdvisedRequest, String> requestToString,
Function<ChatResponse, String> responseToString
)
示例用法:
SimpleLoggerAdvisor customLogger = new SimpleLoggerAdvisor(
request -> "Custom request: " + request.userText,
response -> "Custom response: " + response.getResult()
);
这允许你根据具体需求定制日志信息。
在生产环境中需谨慎记录敏感信息。 |
聊天记忆
ChatMemory
接口定义了聊天对话记忆的存储机制,提供添加消息、检索消息及清空对话历史的方法。
当前内置实现为:MessageWindowChatMemory
。
MessageWindowChatMemory
是聊天记忆实现,维护最多指定数量(默认 20 条)的消息窗口。当消息超出限制时,旧消息会被移除(系统消息除外)。若添加新系统消息,则清除所有旧系统消息,确保始终保留最新上下文的同时控制内存占用。
MessageWindowChatMemory
基于 ChatMemoryRepository
抽象层实现,该抽象层提供多种聊天记忆存储方案,包括:
-
InMemoryChatMemoryRepository
(内存存储) -
JdbcChatMemoryRepository
(JDBC 关系型数据库存储) -
CassandraChatMemoryRepository
(Cassandra 存储) -
Neo4jChatMemoryRepository
(Neo4j 图数据库存储)
详见 聊天记忆功能 文档获取详细说明和使用示例。
实现说明
ChatClient
API 的独特之处在于融合了命令式与响应式编程模型。通常应用会择一使用,而非两者兼用。
-
定制 Model 实现的 HTTP 客户端交互时,需同时配置
RestClient
和WebClient
。
由于 Spring Boot 3.4 的缺陷,必须设置 |
-
流式传输仅通过响应式技术栈支持。因此命令式应用需引入响应式依赖(如
spring-boot-starter-webflux
)。 -
非流式传输仅通过 Servlet 技术栈支持。因此响应式应用需引入 Servlet 依赖(如
spring-boot-starter-web
),且需知悉部分调用将出现阻塞。 -
工具调用采用命令式设计,会导致工作流阻塞。这将产生不完整的
Micrometer
观测数据(例如ChatClient
的追踪 span 与工具调用的 span 未关联,且前者因此保持未完成状态)。 -
内置 Advisor 对标准调用执行阻塞操作,对流式调用执行非阻塞操作。用于 Advisor 流式调用的 Reactor Scheduler 可通过各 Advisor 类的 Builder 进行配置。