🚀 本文目标:对 ChatClient 进行详细解读,包括简介、如何创建/配置 ChatClient、Prompt 提示词、Advisor 增强、ChatClient 响应等内容。
【经过整理,比 Spring AI 官方文档,更容易阅读!】

ChatClient 简介
ChatClient提供了与 AI 模型通信的流式 API,支持同步和响应式(Reactive)编程模型。
流式 API 提供了用于构建传递给 AI Model 作为输入的 Prompt 的各个组成部分的方法。Prompt 包含指导 AI Model 输出和行为的指令文本。从 API 的角度来看,prompts 由消息集合组成。
AI Model 处理两类消息:
- 用户消息:来自用户的直接输入
- 系统消息:由系统生成以指导对话
消息中通常包含占位符,即使用 Prompt 模板,在运行时根据用户输入进行替换,以自定义 Ai Model 对用户输入的响应。还可以指定 Prompt 选项,例如要使用的 Ai Model 名称,AI Model 的选项(比如 temperature 参数)。

类似于服务层,可被 Controller 层直接调用,通过这些调用,快速实现与 AI 交互流程的组装。包括一些基础功能,比如 Prompt、Structured Output、ChatOptions,还支持一些高级功能,比如 Chat Memory、Function Calling、RAG 等。
与原子 API ( **ChatModel**、**Message**、**ChatMemory**等)的比较:
1、 使用 ChatClient可以将于 LLM 及其他组件交互的复杂性隐藏在背后,因为基于 LLM 的应用程序通常需要多个组件协同工作(例如,提示词模板、聊天记忆、LLM Model、输出解析器、RAG 组件等),并通常涉及多个交互,因此协调他们会让编码变得繁琐。
2、使用原子 API,则灵活性更好,但是需要写大量样板式代码。
以 ChatClient为例,其默认实现类为 DefaultChatClient,其实现类内部持有 ChatModel对象。
ChatClient 用法
创建/配置 ChatClient
使用 Builder
使用 ChatClient.Builder对象来构造 ChatClient,实现各种配置的自定义。
**方法 1️⃣:使用 SpringBoot 自动配置创建的默认 ****ChatClient**实例
Spring AI 自动装配单个 ChatClient.BuilderBean,所以在下面的构造器中可直接使用。
public HelloWorldController(ChatClient.Builder chatClientBuilder) {
this.dashScopeChatClient = chatClientBuilder
.defaultSystem(DEFAULT_PROMPT)
.defaultAdvisors(
new SimpleLoggerAdvisor()
)
.build();
}
方法 2️⃣:手动编程方式创建**ChatClient**实例
ChatModel myChatModel = ... // 一般是自动注入的
// 使用 builder
ChatClient.Builder builder = ChatClient.builder(myChatModel);
// 使用 create,底层还是使用的 builder
ChatClient chatClient = ChatClient.create(myChatModel);
同时,指定默认参数,可以避免每次调用都重复设置。运行时可提供不带 default前缀的相应方法来覆盖这些默认值。
- defaultSystem():指定默认的 system message,用于定义 AI 助手的角色和行为。
- defaultUser():指定默认的 user message。
- defaultOptions():设置特定于模型的模型选项,比如模型温度值、最大 Token 等。
- defaultTools():定义默认的工具。
- defaultAdvisors():定义的默认的增强逻辑,用于在请求处理过程中提供额外的逻辑。比如修改请求、修改响应,实现内容过滤、格式化等功能。常见的是实现 RAG 能力。
- defaultTemplateRenderer():提示词模板的默认渲染器,支持将模板 + 动态数据渲染出一个提示词。
使用多个 ChatModel
在单个应用程序中,有可能会用到多个 ChatModel 的场景:
- 不同类型的任务使用不同的模型(性价比考虑):比如,复杂推理就用强大的模型,简单任务就用更快、更便宜的模型。
- 组合使用不同专业能力的模型(专业分工考虑):比如,A 模型针对数据库能力有很强的优化,B 模型对前端处理能力很强,则可以组合 A、B 模型实现最大化的能力。
- 多模型供应商容灾:提供多组模型,单一模型服务不可用时,还能回退到其它可用模型。
- 多模型进行 A/B 测试:比如像对比下基准模型和其它模型的差异效果。
实现思路及场景:默认情况下,Spring AI 会自动配置单个 ChatClient.BuilderBean。如果我们需要配置多个 ChatClient,那可以使用 spring.ai.chat.client.enabled=false禁用掉自动化配置,然后手动创建多个 ChatClient 实例。
- 场景 1:使用一个 ChatModel 创建出多个 ChatClient 实例
- 场景 2:使用不同的 ChatModel 创建不同的 ChatClient 实例,使用时需使用 @Qualifier指定 bean 名称
- 场景 3:多个 OpenAI 兼容的 API 端点,基于 OpenAiApi属性类,自定义创建多个兼容的 API 端点,每一个 API 端点的 BaseUrl 和 ApiKey 不同。

运行时配置 ChatClient
ChatClient 构造完成后,使用 prompt 方法,即可覆盖掉所有的 default 默认配置。
- mutate(),直接复制出一个新的 ChatClient 实例
- prompt():返回 ChatClientRequestSpec对象,可流式设置覆盖掉默认配置。


Prompt 提示词
Prompt 作为 AI 大模型的输入,指导 AI 模型生成特定的输出。Prompt 可以是简单的字符串,也可以是一项工程。
- 简单字符串:就像 ChatGPT 中输入到对话框中的文本。
- Prompt 工程:撰写有效的 Prompt 既是一门艺术,也是一门科学。通过提高 Prompt 的有效性,来显著改善生成结果。
在 ChatClient 接口中定义了 system message 和 user message。
支持文本传入+参数变量替换,多模态支持,元数据支持。
interface PromptUserSpec {
PromptUserSpec text(String text);
PromptUserSpec text(Resource text, Charset charset);
PromptUserSpec text(Resource text);
PromptUserSpec params(Map<String, Object> p);
PromptUserSpec param(String k, Object v);
PromptUserSpec media(Media... media);
PromptUserSpec media(MimeType mimeType, URL url);
PromptUserSpec media(MimeType mimeType, Resource resource);
PromptUserSpec metadata(Map<String, Object> metadata);
PromptUserSpec metadata(String k, Object v);
}
interface PromptSystemSpec {
PromptSystemSpec text(String text);
PromptSystemSpec text(Resource text, Charset charset);
PromptSystemSpec text(Resource text);
PromptSystemSpec params(Map<String, Object> p);
PromptSystemSpec param(String k, Object v);
PromptSystemSpec metadata(Map<String, Object> metadata);
PromptSystemSpec metadata(String k, Object v);
}
Token
既然说到了提示词,不得不提一下 Token。
Token 是 AI 模型工作原理的基石。输入时,模型会将单词转换为 token,输出时,模型会将 token 转换为单词。
在英语中,**1 个 token ≈ 1 个单词 * 75%**。比如,莎士比亚共 90 万个单词,翻译过来大约有 120 万个 Token。
在托管 AI 模型中,**token = 金钱**。托管的 AI 模型,按照 token 数量来收费,输入和输出都会影响总 token 数量。
**模型会受到 token 的限制。**这会限制单个 API 调用中处理的文本量。这个阈值通常称之为“上下文窗口”,模型不会处理超过此限制的任何文本。比如 ChatGPT3 的 token 限制为 4K,而 GPT4 则提供不同的选项,例如 8K、16K 和 32K。如果想要用 GPT4 总结莎士比亚全集,那么需要制定软件工程策略类切分数据并在模型的上下文窗口限制内呈现数据。Spring AI 项目可以帮忙我们完成这个任务。
Prompt 模板
ChatClient流式 API 允许将 system message 和 user message 使用模板来填充,在运行时将模板中的变量替换为实际的值。
String answer = ChatClient.create(chatModel).prompt()
.user(u -> u.text("说出{actor}演过的三部电影").param("actor", "成龙"))
.call()
.content();
Spring AI 提供了 **TemplateRenderer**接口来根据指定的字符串模板及变量 Map 来进行模板中的变量替换。默认实现类有两个,如下所示:
- StTemplateRenderer:使用 StringTemplate v4实现字符串模板替换,默认开始和结束符为 {变量名}。
- NoOpTemplateRenderer:啥也不干,返回原值。
如果上述默认实现类不满足需求,比如想要开始结束符为 <变量名>(因为 JSON 用的是{},所以可能冲突),则可在 ChatClient 对象中自行指定 StTemplateRenderer实例即可。
String answer = ChatClient.create(chatModel).prompt()
.user(u -> u.text("说出{actor}演过的三部电影").param("actor", "成龙"))
.templateRenderer(StTemplateRenderer.builder().startDelimiterToken('<').endDelimiterToken('>').build())
.call()
.content();
消息元数据
ChatClient 支持向 system message 和 user message 上添加元数据。元数据提供有关消息的附加上下文信息,可供 AI Model 或下游处理使用(比如处理 Advisor 中的消息或检查对话历史时使用)。元数据也支持默认元数据和运行时元数据。注意:传递的元数据的 key 和 value 均不可为 null。
示例:运行时传入元数据 userId 和 messageId。
Prompt prompt = new Prompt(DEFAULT_PROMPT, OpenAiChatOptions.builder()
.temperature(0.7)
.build());
Map<String, Object> metadata = new HashMap<>();
metadata.put("userId", "甘蓝聊Java");
metadata.put("messageId", "145612235");
return chatClient.prompt(prompt)
.user(u -> u.text("说出{actor}演过的三部电影").param("actor", actor).metadata(metadata))
.call()
.content();
Advisor(增强)
Advisor API 提供了灵活、强大的方式来拦截、修改和增强 Spring AI 交互。在使用用户输入的文本构造 Prompt 调用 AI 模型时,一个常见的模式是使用上下文数据来扩充 Prompt,最终使用扩充后的 Prompt 与模型交互。
用于扩充 Prompt 的上下文数据可以是不同类型的,常见类型包括:
- 自己的数据:这些是 AI 模型尚未训练过的数据,比如特定领域知识、产品文档等。即 RAG。
- 聊天记忆数据:ChatClient 是无状态 API,如果你告诉 AI 模型你的名字,它不会在后续交互中记住它,每次请求必须发送对话聊天历史记录,以确保生成响应时考虑到先前的交互。
AdvisorSpec 接口
ChatClient 提供了 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 上。比如,下面的例子中,先执行 MessageChatMemoryAdvisor(聊天记忆),然后在执行 QuestionAnswerAdvisor(检索增强生成 RAG)。
ChatClient.builder(chatModel)
.build()
.prompt()
.advisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
)
.user(userText)
.call()
.content();
最简单的实现:日志记录
Spring AI 为了方便调试、监控 Spring AI 应用的交互,添加了一个 SimpleLoggerAdvisor 增强,**实现对 ****ChatClient**请求和响应的日志打印。
实现原理:在 ChatClient 的 Advisor 调用链上打印请求和响应日志。
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
logRequest(chatClientRequest);
ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
logResponse(chatClientResponse);
return chatClientResponse;
}
protected void logRequest(ChatClientRequest request) {
logger.debug("request: {}", this.requestToString.apply(request));
}
protected void logResponse(ChatClientResponse chatClientResponse) {
logger.debug("response: {}", this.responseToString.apply(chatClientResponse.chatResponse()));
}
开启方式:
步骤 1、ChatClient中加入 SimpleLoggerAdvisor
ChatResponse response = ChatClient.create(chatModel).prompt()
.advisors(new SimpleLoggerAdvisor())
.user("Tell me a joke?")
.call()
.chatResponse();
步骤 2、将 org.springframework.ai.chat.client.advisor日志级别设置为 debug
logging:
level:
org:
springframework:
ai:
chat:
client:
advisor: debug
自定义日志打印:如果想要自定义日志打印,支持传入 Function,将 request 和 response 转换为 String 即可。注意:生产环境敏感信息的日志输出要谨慎。
// 构造方法
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
);
其它实现:RAG、ChatMemory
一下两部分,会在后续文章中详细介绍。
QuestionAnswerAdvisor检索增强生成 RAG。
MessageChatMemoryAdvisor 聊天记忆。
MessageChatMemoryAdvisor.builder(chatMemory).build(),
QuestionAnswerAdvisor.builder(vectorStore).build()
ChatClient 响应 – ChatResponse
ChatClient API 提供了多种方法来格式化来自 AI 模型的响应,使用流式 API。
AI 模型的响应是一种由 ChatResponse 类型定义的丰富结构。
ChatResponse = 响应生成的元数据 + 若干子响应(称为 Generation,也包含自己的元数据)。元数据包括创建响应的 token 数量信息。
简单调用(使用 call() 方法)返回:支持返回字符串内容、转换为实体类、支持泛型转换、支持指定转换器转换为实体等。注意:call()本身不会触发 AI 调用,仅仅是指示 Spring AI 是否使用同步或流式调用。实际的 AI 请求发生在调用 content()、chatResponse()等方法时。
interface CallResponseSpec {
// 类型1:最简单的,返回字符串
@Nullable
String content();
// 类型2:返回ChatResponse,会包含元数据和若干generation数据
@Nullable
ChatResponse chatResponse();
// 类型3:返回ChatClientResponse,比上面多了ChatClient执行上下文
// 比如,RAG流程中检索到的相关文档
ChatClientResponse chatClientResponse();
// 类型4:返回Java类型,支持泛型转换,自定义转换器
@Nullable
<T> T entity(ParameterizedTypeReference<T> type);
@Nullable
<T> T entity(StructuredOutputConverter<T> structuredOutputConverter);
@Nullable
<T> T entity(Class<T> type);
// 类型5:使用ResponseEntity包裹的完整对象
<T> ResponseEntity<ChatResponse, T> responseEntity(Class<T> type);
<T> ResponseEntity<ChatResponse, T> responseEntity(ParameterizedTypeReference<T> type);
<T> ResponseEntity<ChatResponse, T> responseEntity(StructuredOutputConverter<T> structuredOutputConverter);
}
流式调用返回(使用 stream() 方法):和上面类似的返回对象,除了它是 Flux 类型。
interface StreamResponseSpec {
Flux<String> content();
Flux<ChatResponse> chatResponse();
Flux<ChatClientResponse> chatClientResponse();
}
ChatClient 其它说明
ChatClient同时支持阻塞式(Servlet)与响应式(WebFlux)编程模型。在实际使用过程中,需要二选一,要么阻塞式,要么响应式。
- 阻塞式:Servlet,添加 spring-boot-starter-web依赖
- 响应式:WebFlux,添加 spring-boot-starter-webflux依赖
特别说明:
1、**Tool Calling 是阻塞式的,会阻塞工作流。**也会导致部分/中断 Micrometer 观察(比如,ChatClient spans 和 tool calling spans 未连接,第一个因此保存不完整)。
2、内置 Advisor 对标准调用执行阻塞操作,对流式调用执行非阻塞操作。用于 Advisor 流式调用的 Reactor Scheduler 可以通过每个 Advisor 类上的 Builder 进行配置。
参考
1.Chat Client API | Spring AI Alibaba
2.https://docs.spring.io/spring-ai/reference/api/chatclient.html#_creating_a_chatclient
相关博文
1.第 1 篇 Spring AI Aliaba – AI 快速体验 2.第 2 篇 Spring AI Alibaba 初体验:原来 Java 也能轻松玩转 AI Agent 3.第 3 篇 Spring AI – Model API 入门指南 4.第 4 篇 深入理解 Spring AI ChatClient:一篇就够了,比官方文档更友好 5.第 5 篇 Spring AI – Tool Calling 全面解析:从基础到高级应用
网硕互联帮助中心



评论前必须登录!
注册