Spring AI
charpter 1
text
平台:百炼 huggingface
工作流:defy n8n workflow
prompt:
zero-shot(零样本)
few-shot(少量样本)
prompt优化
1.COT(思维链)
2.SC(自我一致性) 多次调用模型(可以不同模型) 奇数次 少数服从多数
3.TOT(思维树) 多种推理路径 选出最优解
4.反思机制
5.react
08/20
一. 结构化输出
Bean:
BeanOutputConverter 支持
List:
ListOutputConverter 不支持
ParameterizedTypeReference 支持
Map:
MapOutputConverter 不支持
Map里用 List 转 Map
注意:
java
/**
* 使用 ChatClient 的 entity(Class) 自动添加格式约束并转换成 Bean。
*/
@GetMapping("/chat2")
public Book convertBookAutomatically() {
return chatClient
.prompt("请推荐一本 Java 相关的书")
.system(SYSTEM_PROMPT)
.call()
.entity(Book.class);
}
java
/**
* 使用 ParameterizedTypeReference 将结果转换成 List<Book>。
*/
@GetMapping("/books")
public List<Book> bookList(
@RequestParam(defaultValue = "请推荐3本Java相关的书") String message) {
return chatClient
.prompt(message)
.system(SYSTEM_PROMPT)
.call()
.entity(new ParameterizedTypeReference<List<Book>>() {
});
}
08/22
二. SpringAI 记忆模块
内存记忆-MessageWindowChatMemory
chat_memory_conversation_id:标识对话组的唯一id
底层就是个Map 通过chat_memory_conversation_id作为key取到对饮的List list
1.先afterPropertiesSet初始化里面设置:(ChatMemory也可以注入 不自己new)
ChatMemory chatMemory = MessageWindowChatMemory.builder().maxMessages(3).build();
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
注意:其中maxMessages(3)代表条messages 而不是一来一回算一条,一来一回算两条。
2.使用memory:
return chatClient
.prompt()
.user(message)
.advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, chatId))
.stream().content();
java
@RestController
@RequestMapping("/ai/memory")
public class ChatMemoryController implements InitializingBean {
@Autowired
private DashScopeChatModel chatModel;
private ChatClient chatClient;
@GetMapping("/chat1")
public Flux<String> chat1(String message, String chatId, HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return chatClient
.prompt()
.user(message)
.advisors(spec -> spec.param(ChatMemory.CONVERSATION_ID, chatId))
.stream().content();
}
@Override
public void afterPropertiesSet() throws Exception {
ChatMemory chatMemory = MessageWindowChatMemory.builder().maxMessages(10).build();
this.chatClient = ChatClient.builder(chatModel)
// 实现 Logger 的 Advisor
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
// 设置 ChatClient 中 ChatModel 的 Options 参数
.defaultOptions(
DashScopeChatOptions.builder()
.withTopP(0.7)
.build()
)
.build();
}
}
持久化记忆------JdbcChatMemory
内存记忆使用 Map 保存消息,应用重启后数据会丢失。
持久化记忆通过 JdbcChatMemoryRepository 将消息保存到 MySQL 等关系型数据库中。应用重启后,只要使用相同的 chatId,仍然可以恢复之前的对话上下文。
1. 添加依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-chat-memory-repository-jdbc</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
2. 配置数据库
spring:
datasource:
url: jdbc:mysql://localhost:3306/spring_ai_db?useUnicode=true&characterEncoding=UTF-8
username: root
password: root
driver-class-name: com.mysql.cj.jdbc.Driver
ai:
chat:
memory:
repository:
jdbc:
platform: mysql
initialize-schema: always
platform:指定数据库类型。initialize-schema: always:启动时自动初始化记忆表。- 数据库本身需要提前创建。
- 如果自动建表失败,可以手动创建
spring_ai_chat_memory表。
3. 创建持久化 ChatMemory
@Configuration
public class JdbcChatMemoryConfiguration {
@Bean
public ChatMemory jdbcChatMemory(
JdbcChatMemoryRepository repository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(repository)
.maxMessages(20)
.build();
}
}
核心关系:
MessageWindowChatMemory
↓
JdbcChatMemoryRepository
↓
MySQL
MessageWindowChatMemory 负责控制消息窗口大小,JdbcChatMemoryRepository 负责将消息持久化到数据库。
4. 配置 ChatClient
@Autowired
@Qualifier("jdbcChatMemory")
private ChatMemory jdbcChatMemory;
@Override
public void afterPropertiesSet() {
chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor
.builder(jdbcChatMemory)
.build()
)
.build();
}
5. 使用持久化记忆
@GetMapping("/callDb")
public Flux<String> callDb(
String message,
String chatId,
HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return chatClient.prompt()
.user(message)
.advisors(advisor -> advisor.param(
ChatMemory.CONVERSATION_ID,
chatId
))
.stream()
.content();
}
调用示例:
第一次:
/jdbc/memory/callDb?chatId=123&message=我是Hollis
第二次:
/jdbc/memory/callDb?chatId=123&message=我是谁
即使应用重启,第二次使用相同的 chatId,模型仍然可以获取之前的对话记录。
重点总结
chatId是一组对话的唯一标识。- 相同
chatId共享上下文,不同chatId相互隔离。 maxMessages表示消息数量,不是对话轮数。- 一轮对话通常包含一条
UserMessage和一条AssistantMessage。 MessageChatMemoryAdvisor负责在调用模型前读取历史,并在调用后保存新消息。- 内存记忆重启后丢失,JDBC 记忆重启后仍然存在。
- 持久化记忆不代表无限上下文,发送给模型的消息仍受
maxMessages和模型上下文窗口限制。 - 生产环境不要把数据库密码直接写入配置文件,应使用环境变量或配置中心。
三. Spring AI Advisor
1. Advisor 简介
Advisor 是 Spring AI 提供的 AI 调用拦截与增强机制,作用类似于 Spring AOP。
它可以在调用大模型前后统一处理请求和响应,适合实现:
- 日志记录 SimpleLoggerAdvisor
- 对话记忆 MessageChatMemoryAdvisor
- 敏感词拦截
- 权限校验
- 提示词增强
- 请求和响应修改
- 调用监控

2. 核心接口
public interface Advisor extends Ordered {
String getName();
int getOrder();
}
getOrder() 用于控制多个 Advisor 的执行顺序:
- 数值越小,优先级越高。
Ordered.HIGHEST_PRECEDENCE:最高优先级。Ordered.LOWEST_PRECEDENCE:最低优先级。
Advisor 主要分为:
Advisor
├── CallAdvisor 同步调用
├── StreamAdvisor 流式调用
└── BaseAdvisor 提供 before、after 模板
CallAdvisor
用于拦截同步调用:
public interface CallAdvisor extends Advisor {
ChatClientResponse adviseCall(
ChatClientRequest request,
CallAdvisorChain chain
);
}
StreamAdvisor
用于拦截流式调用:
public interface StreamAdvisor extends Advisor {
Flux<ChatClientResponse> adviseStream(
ChatClientRequest request,
StreamAdvisorChain chain
);
}
3. 执行流程
Advisor 采用责任链模式执行,整体过程和 AOP 类似:
请求
↓
Advisor 1 前置处理
↓
Advisor 2 前置处理
↓
ChatModel 调用
↓
Advisor 2 后置处理
↓
Advisor 1 后置处理
↓
响应
Advisor 必须调用责任链的下一个节点,否则调用会在当前 Advisor 处终止。
4. 常用内置 Advisor
SimpleLoggerAdvisor
记录模型调用前后的请求和响应:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
主要作用:
- 调用前记录请求
- 调用后记录响应
- 辅助调试提示词和模型结果
注意不要在生产日志中泄露用户隐私、API Key 等敏感数据。
MessageChatMemoryAdvisor
自动读取和保存对话历史:
ChatMemory chatMemory = MessageWindowChatMemory.builder()
.maxMessages(10)
.build();
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.build();
调用时指定会话 ID:
return chatClient.prompt()
.user(message)
.advisors(advisor -> advisor.param(
ChatMemory.CONVERSATION_ID,
chatId
))
.call()
.content();
它的主要工作是:
- 调用模型前,根据
chatId查询历史消息。 - 将历史消息加入当前请求。
- 调用模型后,保存用户问题和模型回答。
SafeGuardAdvisor
用于安全检查和敏感内容拦截。
如果输入违反预设规则,可以直接终止责任链,不再调用模型。
ChatModelCallAdvisor
负责真正调用 ChatModel,通常位于责任链末端,因此使用较低优先级:
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
5. 注册方式
全局默认 Advisor
作用于当前 ChatClient 的所有请求:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
new SimpleLoggerAdvisor(),
MessageChatMemoryAdvisor.builder(chatMemory).build()
)
.build();
单次请求 Advisor
只作用于当前调用:
chatClient.prompt()
.user(message)
.advisors(new SimpleLoggerAdvisor())
.call()
.content();
传递 Advisor 参数
chatClient.prompt()
.user(message)
.advisors(advisor -> advisor.param(
ChatMemory.CONVERSATION_ID,
chatId
))
.call()
.content();
6. 精炼总结
- Advisor 是 Spring AI 的调用拦截和增强机制,类似 AOP。
CallAdvisor用于同步调用,StreamAdvisor用于流式调用。- 多个 Advisor 按照
getOrder()排序执行,数值越小越优先。 - Advisor 通过责任链依次调用,最终由
ChatModel与模型交互。 SimpleLoggerAdvisor用于日志记录。MessageChatMemoryAdvisor用于读取和保存对话记忆。SafeGuardAdvisor用于敏感词和安全检查。- Advisor 可以配置为全局默认,也可以只用于单次请求。
- 自定义 Advisor 适合实现权限、审计、监控、缓存和提示词增强等横切功能。
7.多Advisor调用顺序
多个 Advisor 的调用过程可以理解为"栈式调用",和 Spring AOP、Servlet Filter 很相似。
假设有 3 个 Advisor:
Advisor A → Advisor B → Advisor C → ChatModel
执行顺序是:
A.before
B.before
C.before
ChatModel.call
C.after
B.after
A.after
也就是:
- 请求阶段:按照 Advisor 顺序依次进入,类似入栈。
- 调用模型:到达责任链最内层。
- 响应阶段:按照相反顺序返回,类似出栈。
可以写成:
请求 → A → B → C → ChatModel
响应 ← A ← B ← C ← ChatModel
例如:
public ChatClientResponse adviseCall(
ChatClientRequest request,
CallAdvisorChain chain) {
System.out.println("A before");
ChatClientResponse response = chain.nextCall(request);
System.out.println("A after");
return response;
}
多个 Advisor 都采用这种结构时,日志大致是:
A before
B before
C before
ChatModel
C after
B after
A after
需要特别注意:
getOrder()决定进入顺序,值越小越先进入、越晚退出。chain.nextCall(request)相当于调用栈中的下一层。- 如果某个 Advisor 不调用
nextCall(),责任链会在这里被截断,后面的 Advisor 和 ChatModel 都不会执行。 - 流式调用虽然整体也是责任链结构,但响应是异步的
Flux,后置逻辑通常需要通过 Reactor 操作符处理,不能完全按照普通同步方法理解。
Spring AI 调用本地 Ollama 模型
核心依赖
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-ollama</artifactId>
<version>1.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-autoconfigure-model-ollama</artifactId>
<version>1.1.0</version>
</dependency>
Ollama 配置
yml
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
model: deepseek-r1:7b
Controller
java
@RestController
@RequestMapping("/ai/ollama")
public class OllamaChatController {
@Autowired
@Qualifier("ollamaChatModel")
private ChatModel ollamaChatModel;
@GetMapping("/stream/chat")
public Flux<String> streamChat(
@RequestParam(defaultValue = "你是谁?") String message,
HttpServletResponse response) {
response.setCharacterEncoding("UTF-8");
return ollamaChatModel.stream(new Prompt(message))
.map(result -> result.getResult()
.getOutput()
.getText());
}
}
准备本地模型
ollama pull deepseek-r1:7b
调用接口
GET /ai/ollama/stream/chat?message=你是谁
项目中存在多个
ChatModel时,使用@Qualifier("ollamaChatModel")指定本地模型。