备注:Spring AI 每个里程碑过大,导致以前能实现的 Deep Think 方式,在 GA 版失效。本期站在源码角度修复 Deep Think 模式
M6 版本不需要修改源码时实现的 Deep Think 源码可见(看后文发现,原来 M6 版实现有问题,确反而生效):github.com/GTyingzi/Sp...
本期最新 1.0 GA 版本实战代码可见:github.com/GTyingzi/sp... 下的 advisor/advisor-deep-think
AI 模型返回 reasoningcontent
调用 AI 模型的 API,返回对应的结果,这是比较常规的操作了

reasoningcontent 字段就是我们调用 AI 模型的 API 返回的思考内容,我们需要把该字段透传出来
- 同时注意到此时的 finishreason 为 null,请先记住该字段,这是现阶段 SpringAI GA 版下 Deep Think 模式失效的罪魁祸首

M6 版的 Deep Think 实现
源码地址可见:github.com/GTyingzi/Sp...
效果如下:

机制是我们自定义 ReasoningContentAdvisor 类去实现 BaseAdvisor 接口,重写 after 方法,取出 reasoningContent 内容
java
package com.yingzi.advisor.component;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.advisor.api.AdvisedRequest;
import org.springframework.ai.chat.client.advisor.api.AdvisedResponse;
import org.springframework.ai.chat.client.advisor.api.BaseAdvisor;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Objects;
/**
* @author yingzi
* @date 2025/3/21:17:36
*/
public class ReasoningContentAdvisor implements BaseAdvisor {
private static final Logger logger = LoggerFactory.getLogger(ReasoningContentAdvisor.class);
private final int order;
public ReasoningContentAdvisor(Integer order) {
this.order = order != null ? order : 0;
}
@NotNull
@Override
public AdvisedRequest before(@NotNull AdvisedRequest request) {
return request;
}
@NotNull
@Override
public AdvisedResponse after(AdvisedResponse advisedResponse) {
ChatResponse resp = advisedResponse.response();
if (Objects.isNull(resp)) {
return advisedResponse;
}
logger.info(String.valueOf(resp.getResults().get(0).getOutput().getMetadata()));
String reasoningContent = String.valueOf(resp.getResults().get(0).getOutput().getMetadata().get("reasoningContent"));
if (StringUtils.hasText(reasoningContent)) {
List<Generation> thinkGenerations = resp.getResults().stream()
.map(generation -> {
AssistantMessage output = generation.getOutput();
AssistantMessage thinkAssistantMessage = new AssistantMessage(
String.format("<think>%s</think>", reasoningContent) + output.getText(),
output.getMetadata(),
output.getToolCalls(),
output.getMedia()
);
return new Generation(thinkAssistantMessage, generation.getMetadata());
}).toList();
ChatResponse thinkChatResp = ChatResponse.builder().from(resp).generations(thinkGenerations).build();
return AdvisedResponse.from(advisedResponse).response(thinkChatResp).build();
}
return advisedResponse;
}
@Override
public int getOrder() {
return this.order;
}
}
GA 版 Deep Think 实现
GA 版对于 Advisor 做了比较大的改动,详情可见:[Advisor基础] + [AdvisorChain链]
首先来看下正常请求的流程图:用户 Query 流式请求 -> DefaultAroundAdvisorChain 类的 nextStream 方法 ---> 调用 BaseAdvisor 接口类(被 ReasoningContentAdvisor 实现)的默认 adviseStream 方法 ---> 调用 ReasoningContentAdvisor 类的 before 方法 -> 调用 ChatModelStreamAdvisor 类获取 Flux -> 调用 ReasoningContentAdvisor 类的 after 方法

流式一段段的返回结果,在每段结果中是否会调用 ReasoningContentAdvisor 类的 after 方法由 AdvisorUtils 类的 onFinishReason 方法控制,校验通过才会调用
让我们回到 ReasoningContentAdvisor 类实现 BaseAdvisor 接口,重写 after 方法
java
package com.spring.ai.tutorial.advisor.component;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.AdvisorChain;
import org.springframework.ai.chat.client.advisor.api.BaseAdvisor;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.model.Generation;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Objects;
/**
* @author yingzi
* @date 2025/3/21:17:36
*/
public class ReasoningContentAdvisor implements BaseAdvisor {
private static final Logger logger = LoggerFactory.getLogger(ReasoningContentAdvisor.class);
private final int order;
public ReasoningContentAdvisor(Integer order) {
this.order = order != null ? order : 0;
}
@Override
public int getOrder() {
return this.order;
}
@Override
public ChatClientRequest before(@NotNull final ChatClientRequest chatClientRequest, @NotNull final AdvisorChain advisorChain) {
return chatClientRequest;
}
@Override
public ChatClientResponse after(@NotNull final ChatClientResponse chatClientResponse, @NotNull final AdvisorChain advisorChain) {
ChatResponse resp = chatClientResponse.chatResponse();
if (Objects.isNull(resp)) {
return chatClientResponse;
}
logger.debug(String.valueOf(resp.getResults().get(0).getOutput().getMetadata()));
String reasoningContent = String.valueOf(resp.getResults().get(0).getOutput().getMetadata().get("reasoningContent"));
if (StringUtils.hasText(reasoningContent)) {
List<Generation> thinkGenerations = resp.getResults().stream()
.map(generation -> {
AssistantMessage output = generation.getOutput();
AssistantMessage thinkAssistantMessage = new AssistantMessage(
String.format("<think>%s</think>", reasoningContent) + output.getText(),
output.getMetadata(),
output.getToolCalls(),
output.getMedia()
);
return new Generation(thinkAssistantMessage, generation.getMetadata());
}).toList();
ChatResponse thinkChatResp = ChatResponse.builder().from(resp).generations(thinkGenerations).build();
return ChatClientResponse.builder().chatResponse(thinkChatResp).build();
}
return chatClientResponse;
}
}
发现只有 AI 模型 API 返回为"stop"的才被传递出来,此时的 reasoncontent 是无值的
- reasoncontent 有值时:finishreason 的 AI 模型 API 返回为"null"
- reasoncontent 无值时:finishreason 的 AI 模型 API 返回为"stop"
我们回到 AdvisorUtils 类的 onFinishReason 方法,发现要求 finishreason 有字段,才通过校验

而我们现阶段 reasoncontent 有值时,finishreason 的 AI 模型 API 返回为"null",后续一些逻辑处理,使得 finishReason=""
,导致校验不通过

到这里,解决方案就出来了,重写 AdvisorUtils 类,去掉 finishReason 判断为空的校验逻辑即可
java
package org.springframework.ai.chat.client.advisor;
import java.util.function.Predicate;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.util.StringUtils;
public final class AdvisorUtils {
private AdvisorUtils() {
}
public static Predicate<ChatClientResponse> onFinishReason() {
return (chatClientResponse) -> {
ChatResponse chatResponse = chatClientResponse.chatResponse();
return chatResponse != null && chatResponse.getResults() != null && chatResponse.getResults().stream().anyMatch((result) -> result != null && result.getMetadata() != null);
};
}
}
流式输出结果如下:
不对啊,M6 版也有 BaseAdvisor 啊,那为什么那边可以测验通过呢?让我们重新回到 M6 的 BaseAdvisor 类进行 debug,发现 finishReash 字段存了个"NULL"的字符串,这里因为实现不完善留下的 Bug 反而正常调用了 ReasoningContentAdvisor 类 😆

往期资料

Spring AI + Spring Ai Aliabba系统化学习资料
本教程将采用2025年5月20日正式的GA版,给出如下内容
- 核心功能模块的快速上手教程
- 核心功能模块的源码级解读
- Spring ai alibaba增强的快速上手教程 + 源码级解读
版本:
- JDK21
- SpringBoot3.4.5
- SpringAI 1.0.0
- SpringAI Alibaba 跟着最新
免费渠道:
- 为Spring Ai Alibaba开源社区解决解决有效的issue or 提供有价值的PR,可免费获取上述教程
- 往届微信推文
收费服务:收费69.9元
- 飞书在线云文档
- Spring AI会员群教程代码答疑
学习交流圈
你好,我是影子,曾先后在🐻、新能源、老铁就职,兼任Spring AI Alibaba开源社区的Committer。目前新建了一个交流群,一个人走得快,一群人走得远,另外,本人长期维护一套飞书云文档笔记,涵盖后端、大数据系统化的面试资料,可私信免费获取

