Spring AI聊天补全组件排行榜:ChatClient与Advisor

2026-06-16阅读 0热度 0
Pro

全面掌控 Spring AI 聊天补全:ChatClient、PromptTemplate、Advisor 实战精解

为什么必须理解 ChatClient 的运作机制?

上一章我们跑通了第一个 Spring AI 应用,但只是 "Hello World" 级别。生产环境落地时,你离不开:系统角色设定、提示词模板化、Token 用量监控、请求响应拦截……这些能力 Spring AI 早已封装好。本文将从底层到高阶,彻底拆解 ChatClient 的全貌。

全面掌握 Spring AI 聊天补全:ChatClient、PromptTemplate、Advisor 实战精解

核心概念一览

在 Spring AI 中,一次对话请求从发起到返回,经历如下链路:

你的代码 → ChatClient → [Advisor链] → ChatModel → LLM API → ChatResponse → 你的代码

组件职责地图:

组件职责类比
ChatClient面向开发者的流畅 API 入口餐厅前台
ChatModel底层与 LLM 交互的模型接口后厨大厨
Prompt封装消息列表 + 选项的请求对象菜单 + 口味备注
Message单条消息(用户/系统/助手/工具)菜单上的一道菜
Advisor请求/响应的拦截处理链服务员、质检员

第一节:底层接口设计剖析

1.1 通用 Model 接口

Spring AI 定义了一套通用接口,所有 AI 模型都遵循同一契约:

public interface Modelextends ModelRequest, TRes extends ModelResponse> {TRes call(TReq request);}

直白地说:无论 GPT、Claude 还是 Qwen,调用方式统一为 call(请求) → 返回 响应。Spring 的核心优雅就在这里——统一抽象,屏蔽底层差异。

ModelRequest 负责请求(指令 + 选项),ModelResponse 负责响应(结果 + 元数据)。而 ModelResult 内部拆分为 output(AI 生成的内容)和 metadata(生成过程的元数据)。

1.2 ChatModel:专为对话设计的 Model

public interface ChatModel extends Model {ChatResponse call(Prompt prompt);ChatOptions getDefaultOptions();}

ChatModel 将请求类型特化为 Prompt,响应类型特化为 ChatResponse。每个模型实现(例如 OllamaChatModel)都携带自己的默认选项。

第二节:Message 的四种角色定位

一次完整对话中,消息分为四种角色:

类型英文作用场景
用户消息USER人说的"帮我写个排序算法"
助手消息ASSISTANTAI 回的生成的代码/回答
系统消息SYSTEM给 AI 的"人设""你是一个 Java 专家"
工具消息TOOL函数调用结果查询天气后的返回数据

关键洞察:系统消息(SystemMessage)是控制 AI 行为的"隐形遥控器"。默认的系统消息 "You're a helpful assistant." 过于宽泛,生产环境务必自定义!

Prompt prompt = new Prompt(List.of(new SystemMessage("You are a Java programming expert."),new UserMessage(input)));

️ 第三节:ChatOptions 参数调优实战

大模型暴露了许多可调参数。Spring AI 通过 ChatOptions 开放这些能力:

public interface ChatOptions extends ModelOptions {String getModel(); // 模型名称Double getFrequencyPenalty();// 频率惩罚(防重复)Integer getMaxTokens();// 最大输出 token 数Double getPresencePenalty(); // 主题惩罚(防跑题)List getStopSequences();// 停止词Float getTemperature();// 温度(创造性 vs 确定性)Float getTopP(); // 核采样Integer getTopK(); // Top-K 采样}

3.1 全局默认配置(application.yaml)

spring:ai:ollama:chat:options:model: qwen3:0.6btemperature: 0.7 # 0=保守,1=放飞

3.2 请求级覆盖(代码运行时动态调整)

Prompt prompt = new Prompt(input, OllamaChatOptions.builder().temperature(0.1) // 本次要求严谨.stop(List.of("Observation:")).build());

第四节:Token 用量监控,防止账单超支

调用云模型按 Token 计费,拿到响应后第一件事就是检查用量!

ChatResponse response = chatClient.prompt().user(input).call().chatResponse();// AI 回复的文本String content = response.getResult().getOutput().getText();// Token 用量详情Usage usage = response.getMetadata().getUsage();int promptTokens = usage.getPromptTokens();// 输入消耗int completionTokens = usage.getCompletionTokens();// 输出消耗long totalTokens = usage.getTotalTokens(); // 总计

ChatResponseMetadata 还包含哪些数据?

  • RateLimit:限流信息(剩余调用次数、重置时间)
  • Usage:Token 消耗明细
  • PromptMetadata:提示词过滤元数据

️ 第五节:ChatClient 链式 API 实战演练

ChatClient 是 Spring AI 给开发者的"糖衣炮弹",链式调用极度丝滑:

5.1 最简调用方式

String content = chatClient.prompt().system("You are a master chef.") // 设定角色.user("How to cook fish?")// 用户提问.call()// 执行调用.content();// 提取文本结果

5.2 直接传入现有 Prompt 对象

Prompt prompt = new Prompt("hello");String content = chatClient.prompt(prompt).call().content();

5.3 ChatClient.Builder 预置默认值

ChatClient chatClient = builder.defaultSystem("你是一个专业的技术翻译")// 默认系统消息.defaultAdvisors(loggingAdvisor) // 默认顾问.defaultOptions(options) // 默认选项.build();

Builder 方法速查表:

方法作用
defaultUser预设默认用户消息
defaultSystem预设默认系统消息
defaultTools预设可用工具
defaultOptions预设聊天选项
defaultAdvisors预设顾问链

第六节:PromptTemplate 提示词模板——告别硬编码拼接

6.1 模板的价值

设想一个烹饪建议服务,用户只输入 "鱼",但发给 AI 的提示词应该是:"如何在 5 分钟内做一道美味的鱼?" 这种"用户输入 + 固定模板"的模式,用 PromptTemplate 最合适。

6.2 模板文件 + 变量替换

src/main/resources/prompts/cooking.st

How to cook {dish} in 5 minutes?

Java 代码:

@Value("classpath:/prompts/cooking.st")private Resource promptResource;public String chat(String dish) {Prompt prompt = new PromptTemplate(promptResource).create(Map.of("dish", dish));return chatClient.prompt(prompt).call().content();}

6.3 模板 + 系统消息组合技

Message userMessage = new PromptTemplate(promptResource).createMessage(Map.of("dish", dish));Prompt prompt = new Prompt(List.of(new SystemMessage("You are a master chef."),userMessage));

6.4 Builder 模式 + 默认值

PromptTemplate template = PromptTemplate.builder().resource(promptResource).variables(Map.of("value", "hello"))// 默认值.build();// 运行时覆盖Prompt prompt = template.create(Map.of("value", "world"));

6.5 自定义模板引擎

默认使用 StringTemplate({var} 语法),如果希望切换,可以自行实现:

public interface TemplateRenderer extends BiFunction, String> {String apply(String template, Map variables);}// 使用时PromptTemplate template = PromptTemplate.builder().resource(promptResource).renderer(myCustomRenderer)// 注入自定义渲染器.build();

️ 第七节:Advisor 顾问链——Spring AI 的"中间件"

这是 Spring AI 最具架构感的设计!Advisor 类似 Spring 的 AOP 拦截器,在请求到达模型前后插入通用逻辑。

7.1 核心接口

public interface CallAdvisor extends Advisor {ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain);}

执行原理:多个 Advisor 组成链条,每个 Advisor 可以:

  1. 修改请求(ChatClientRequest
  2. 调用下一个 Advisor(chain.nextCall()
  3. 修改响应(ChatClientResponse
  4. 直接返回,短路后续执行

7.2 写个日志顾问,调试利器

public class LoggingAdvisor implements CallAdvisor {private static final Logger LOGGER = LoggerFactory.getLogger("ChatClient.debugger");@Overridepublic ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {debug(request);// 记录请求var response = chain.nextCall(request); // 继续执行debug(response); // 记录响应return response;}@Overridepublic int getOrder() {return Ordered.LOWEST_PRECEDENCE;// 放在最后执行}}

启用日志(application.yaml):

logging:level:ChatClient.debugger: DEBUG

7.3 修改请求:重写用户文本

public class RewriteUserTextAdvisor implements CallAdvisor {@Overridepublic ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {if (request.context().containsKey("updated_user_text")) {String updatedText = (String) request.context().get("updated_user_text");// 使用 mutate 构建新的不可变请求var mutatedRequest = request.mutate().prompt(request.prompt().augmentUserMessage(updatedText)).build();return chain.nextCall(mutatedRequest);}return chain.nextCall(request);}}

REST 控制器里使用:

@RestControllerpublic class RewriteController {private final ChatClient chatClient;public RewriteController(ChatClient.Builder builder) {this.chatClient = builder.defaultAdvisors(new RewriteUserTextAdvisor()).build();}@PostMapping("/rewrite")public String rewrite(@RequestBody Request req) {return chatClient.prompt().user(req.input()).advisors(spec -> spec.param("updated_user_text", StringUtils.trimToEmpty(req.updatedInput()))).call().content();}}

7.4 两种启用方式

方式代码生效范围
全局默认builder.defaultAdvisors(advisor)该 Builder 创建的所有 ChatClient
单次请求.prompt().advisors(advisor).call()仅当前请求

7.5 顾问顺序的重要性

Advisor 继承自 Ordered,数字越小越先执行。Spring AI 内置顾问有固定顺序,自定义顾问建议使用大于 0 的值,避免冲突。

第八节:递归顾问——Spring AI 1.1 的王炸特性

Spring AI 1.1 之前,顾问链每个 Advisor 只能执行一次。1.1 引入了递归顾问,让链的一部分可以重复执行——这就为复杂 Agent 工作流提供了可能。

8.1 核心:copy 方法

CallAdvisorChain.copy(this) 可以复制当前 Advisor 之后的剩余链条,生成一个子链反复调用。

8.2 实战:让 AI 讲三次笑话

public class TellJokeAdvisor implements CallAdvisor {@Overridepublic ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {var responses = new ArrayList();for (int i = 0; i < 3; i++) {// 修改提示词:让 AI 讲笑话var prompt = request.prompt().augmentUserMessage(userMsg -> userMsg.mutate().text("Tell a joke about " + userMsg.getText()));// 复制子链并调用var response = chain.copy(this).nextCall(request.mutate().prompt(prompt.build()).build()).chatResponse();responses.add(response.getResult().getOutput().getText());}// 合并三次结果返回return ChatClientResponse.builder().chatResponse(ChatResponse.builder().generations(List.of(new Generation(AssistantMessage.builder().content(String.join("n", responses)).build()))).build());}}

调用效果:输入 "programmer",返回 3 个关于程序员的笑话!

总结:一张图看懂 Spring AI 聊天补全

┌─────────────────────────────────────────────────────────────┐│ChatClient││.prompt() → .system()/.user() → .call()/.stream() │└────────────────────┬────────────────────────────────────────┘ │┌────────────────────▼────────────────────────────────────────┐│Advisor Chain ││[LoggingAdvisor][RewriteAdvisor][TellJokeAdvisor] ││ 记录日志修改请求递归执行│└────────────────────┬────────────────────────────────────────┘ │┌────────────────────▼────────────────────────────────────────┐│ChatModel.call(Prompt)││┌─────────────────────────────────────────────┐│││Prompt││││├─ SystemMessage (人设/指令) ││││├─ UserMessage (用户问题)││││└─ ChatOptions (temperature/model等) │││└─────────────────────────────────────────────┘│└────────────────────┬────────────────────────────────────────┘ │ HTTP┌────────────────────▼────────────────────────────────────────┐│LLM API │└────────────────────┬────────────────────────────────────────┘ │┌────────────────────▼────────────────────────────────────────┐│ ChatResponse ││├─ Generation → AssistantMessage (文本)││├─ Usage (Prompt/Completion/Total Tokens)││└─ RateLimit (限流信息)│└─────────────────────────────────────────────────────────────┘

互动时间

你在 Spring AI 实战中踩过哪些坑?

  • A:Prompt 模板管理混乱
  • B:Advisor 顺序理不清,调试到崩溃
  • C:Token 用量监控缺失,月底账单爆炸
  • D:其他,评论区见
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策