SpringAI

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,模型仍然可以获取之前的对话记录。

重点总结
  1. chatId 是一组对话的唯一标识。
  2. 相同 chatId 共享上下文,不同 chatId 相互隔离。
  3. maxMessages 表示消息数量,不是对话轮数。
  4. 一轮对话通常包含一条 UserMessage 和一条 AssistantMessage
  5. MessageChatMemoryAdvisor 负责在调用模型前读取历史,并在调用后保存新消息。
  6. 内存记忆重启后丢失,JDBC 记忆重启后仍然存在。
  7. 持久化记忆不代表无限上下文,发送给模型的消息仍受 maxMessages 和模型上下文窗口限制。
  8. 生产环境不要把数据库密码直接写入配置文件,应使用环境变量或配置中心。

三. 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. 精炼总结
  1. Advisor 是 Spring AI 的调用拦截和增强机制,类似 AOP。
  2. CallAdvisor 用于同步调用,StreamAdvisor 用于流式调用。
  3. 多个 Advisor 按照 getOrder() 排序执行,数值越小越优先。
  4. Advisor 通过责任链依次调用,最终由 ChatModel 与模型交互。
  5. SimpleLoggerAdvisor 用于日志记录。
  6. MessageChatMemoryAdvisor 用于读取和保存对话记忆。
  7. SafeGuardAdvisor 用于敏感词和安全检查。
  8. Advisor 可以配置为全局默认,也可以只用于单次请求。
  9. 自定义 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

需要特别注意:

  1. getOrder() 决定进入顺序,值越小越先进入、越晚退出。
  2. chain.nextCall(request) 相当于调用栈中的下一层。
  3. 如果某个 Advisor 不调用 nextCall(),责任链会在这里被截断,后面的 Advisor 和 ChatModel 都不会执行。
  4. 流式调用虽然整体也是责任链结构,但响应是异步的 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") 指定本地模型。

相关推荐
mounter6257 小时前
GCCRS 突破与 Rust 入核:Linux 工具链的重大变革
linux·开发语言·rust·linux kernel·kernel
Buke..7 小时前
【APP 逆向】哔哩哔哩 sign 参数逆向(上):Frida 反调试绕过与 unidbg 调用
java·开发语言·爬虫·python·安卓
abcefg_h7 小时前
MCP 实战指南:如何在项目中使用 Model Context Protocol 及其通信原理
开发语言·后端·golang·mcp
头发够用的程序员7 小时前
ImportError: libopenblas.so.0: cannot open shared object file 完整复盘与解决方案
python·ubuntu
何以解忧,唯有..7 小时前
Python 线程编程:从入门到实战
开发语言·python
l1258658 小时前
# RAG上线评估指标体系:六大核心指标与压测实战全解析
数据库·人工智能·python·mysql·langchain·milvus
我命由我123458 小时前
人脸识别 - 人脸识别选帧
java·人工智能·python·算法·安全·java-ee·人脸识别
SomeB1oody8 小时前
【RustyML入门】7.2. 深入模型持久化
开发语言·后端·机器学习·rust·教程
yaoxin5211239 小时前
502. Java 反射 - 编写 MessageInterceptor 类
java·开发语言