JB5-1-SpringAI(一)

06-01 1561阅读

Java道经第5卷 - 第1阶 - SpringAI(一)


传送门:JB5-1-SpringAI(一)

传送门:JB5-2-SpringAI(二)

文章目录

  • S01. SpringAI入门
    • E01. SpringAI概念
      • 1. 传统VS生成式
      • 2. SpringAI功能
      • 3. 获取API_KEY
      • E02. 整合后端项目
        • 1. 添加三方依赖
        • 2. 开发主配文件
        • 3. 开发启动类
        • 4. 开发控制器
        • E03. 整合前端项目
          • 1. 添加Axios依赖
          • 2. 添加ElementPlus
          • 3. 开发全局样式
          • 4. 开发Vue页面
          • S02. SpringAI初级
            • E01. 预设角色
              • 1. 开发控制器
              • E02. 日志通知
                • 1. 开发主配文件
                • 2. 开发控制器
                • E03. 自定义通知
                  • 1. 开发通知类
                  • 2. 开发控制器
                  • E04. 记忆通知
                    • 1. 开发控制器
                    • E05. 工具调用
                      • 1. 开发配置类
                      • 2. 开发控制器
                      • E06. 检索增强
                        • 1. 开发检索文件
                        • 2. 开发配置类
                        • 3. 开发控制器
                        • E07. 文生图
                          • 1. 开发控制器

                            心法:本章使用 Maven 父子结构项目进行练习

                            练习项目结构如下:

                            |_ v5-1-llm-springai
                            	|_ 5501 test
                            	|_ 6501 springai-web
                            

                            武技:搭建练习项目结构

                            1. 创建父项目 v5-1-llm-springai,删除 src 目录。
                            2. 在父项目中锁定版本:注意目前 SpringAI 支持 SpringBoot3.2.x 和 SpringBoot 3.3.x 版本:
                                17
                                17
                                UTF-8
                                4.13.2
                                1.18.24
                                5.8.25
                                3.2.5
                                1.0.0-M6
                                1.0.0-M6.1
                                1.0.0-M7
                            
                            
                            1. 在父项目中配置仓库:因为 Spring 官方目前并没有将 spring-ai 相关的包发布到 Maven 的中央仓库中,所以需要使用 标签单独配置特定的,可能包含 spring-ai 依赖的仓库,参考官方文档 最终配置如下:
                            
                                
                                
                                    spring-snapshots
                                    Spring Snapshots
                                    https://repo.spring.io/snapshot
                                    
                                        false
                                    
                                
                                
                                
                                    Central Portal Snapshots
                                    central-portal-snapshots
                                    https://central.sonatype.com/repository/maven-snapshots/
                                    
                                        false
                                    
                                    
                                        true
                                    
                                
                                
                                
                                    spring-milestones
                                    Spring Milestones
                                    https://repo.spring.io/milestone
                                    
                                        false
                                    
                                
                            
                            
                            1. 在父项目中管理依赖:需要同时管理 SpringBoot 依赖和 SpringAI 依赖:
                            
                                
                                    
                                    
                                        org.springframework.boot
                                        spring-boot-starter-parent
                                        ${springboot.version}
                                        pom
                                        import
                                    
                                    
                                    
                                        org.springframework.ai
                                        spring-ai-bom
                                        ${spring-ai-bom.version}
                                        pom
                                        import
                                    
                                
                            
                            
                            1. 在父项目中引入通用依赖:
                                
                                
                                    junit
                                    junit
                                    ${junit.version}
                                    test
                                
                                
                                
                                    org.projectlombok
                                    lombok
                                    ${lombok.version}
                                    provided
                                
                                
                                
                                    cn.hutool
                                    hutool-all
                                    ${hutool-all.version}
                                
                            
                            
                            1. 创建子项目 test,不引入任何依赖。

                            S01. SpringAI入门

                            E01. SpringAI概念

                            心法:SpringAI 项目致力于简化集成人工智能功能的应用程序开发流程,解决了 AI 集成的问题,避免引入不必要的复杂性,它从 LangChain 和 LlamaIndex 等知名 Python 项目中获取灵感,但它并非这些项目的直接移植版本。

                            SpringAI 理念:下一波生成式 AI 应用程序不会局限于 Python 开发者群体,而是会在多种编程语言中广泛普及。

                            SpringAI 特点:

                            • API 支持:提供跨 AI 提供商的可移植 API,支持聊天、文本到图像、嵌入等模型,具备同步和流式处理 API 选项。
                            • 模型支持:涵盖所有主流的 AI 模型提供商,如 OpenAI、Microsoft、Amazon、Google、Ollama 等,支持的模型类型包括聊天完成、嵌入、文本到图像、音频转录、文本到语音、适度、结构化输出等。
                            • 数据库支持:适配众多主流矢量数据库提供商,如 Apache Cassandra、Azure Vector Search 等。
                            • SpringBoot 支持:通过 SpringBoot 自动配置和启动器,方便在项目中快速集成 AI 模型和向量存储。

                              SpringAI 交互流程:核心包括 Application 应用程序和 Generative AI 生成式人工智能两个角色:

                              • To There:App 将自身的数据和通过 API 获取的信息发送给 AI,供其处理和利用。
                              • To Here:AI 处理完信息后,将结果返回给应用程序。

                                JB5-1-SpringAI(一)

                                1. 传统VS生成式

                                心法:生成式人工智能是能依据提示生成文本、图像、音频、视频、软件代码等全新内容的人工智能。

                                传统的 AI 和 生成式 AI 对比如下:

                                技术原理方面:

                                • 传统的 AI:常基于规则和算法,通过对大量标注数据的学习来提取特征,实现分类、预测等任务。
                                • 生成式 AI:以生成模型为核心,通过对海量数据的无监督或半监督学习,掌握数据内在模式和分布,进而生成新数据。

                                  功能特点方面:

                                  • 传统的 AI:擅长执行特定任务,像语音识别、图像识别、医疗影像诊断、金融风险预测等,专注在已知模式和规则下对输入数据分类、判断和预测,不具备内容创造能力。
                                  • 生成式 AI:突出特点是创造新内容,涵盖文本、图像、音频、视频等,还能进行代码补全、场景模拟等。例如根据文本描述生成对应图像,或依据简单旋律拓展成完整乐曲。

                                    应用场景方面:

                                    • 传统的 AI:广泛用于需要精准判断和预测的领域,比如在安防监控中识别异常行为,电商推荐系统依据用户行为和偏好推荐商品,工业生产中检测产品缺陷等。
                                    • 生成式 AI:多用于创意和内容生成领域,比如广告营销生成创意文案和设计,游戏开发自动生成地图、角色和剧情,影视制作生成特效和虚拟场景。

                                      2. SpringAI功能

                                      心法:目前 SpringAI 包基础会话,会话记忆,RAG 增强,通知助手,工具调用,模型评估,数据提取和模型观察等核心功能。

                                      基础会话功能 ChatClient API:通过这个 API,你可以方便地向 AI 聊天模型发送消息,并且接收它回复的消息,就像在和一个真实的人聊天一样。

                                      会话记忆功能 Chat Conversation Memory:该功能可以让聊天机器人记住之前的对话内容,就像人有记忆一样,这样在新的对话中,它就能根据之前的交流来更好地回答问题。

                                      RAG 增强功能 Retrieval Augmented Generation:该功能就像是给聊天机器人一个知识宝库,当它回答问题时,不仅可以依靠自己学到的知识,还能从这个宝库里快速找到相关的信息来丰富答案,让回答更准确、更全面。

                                      通知助手功能 Advisors API:该功能就像是一个智能顾问助手,可以在 Spring 应用程序中对 AI 驱动的交互进行干预和优化,提供灵活且强大的支持,包括拦截请求、修改参数以及增强交互效果等,以满足不同的业务需求和优化应用程序的 AI 功能。

                                      工具调用功能 Tools/Function Calling:该功能可以让模型能按需请求执行客户端工具函数获取实时信息:

                                      • 比如 AI 模型觉得需要知道今天的天气,于是它请求执行一个获取天气信息的函数。
                                      • 这个函数可以是本地开发的,其内部会从相关的数据源(比如天气网站的接口)获取实时的天气数据。
                                      • 然后 AI 模型就能根据这些数据继续完成它的任务,比如给你提供出行建议等。

                                        模型评估功能 AI Model Evaluation:该功能就像一个质检员,专门负责检查 AI 生成的文本、图像等内容是否符合要求,有没有出现幻觉响应,通过 AI 模型评估,我们可以及时发现这些问题,采取措施来改进 AI 模型,让它生成更准确、更有用的内容:

                                        • 幻觉响应:就是 AI 生成了一些不符合事实或者与给定信息不相关的内容。

                                          数据提取功能 ETL framework:支持 ETL框架,即提取(Extract)、转换(Transform)、加载(Load )流程:

                                          • 先把各种文档数据从不同地方提取出来,比如从文件系统、数据库等。
                                          • 接着按照一定规则对文档数据处理转换,像把文本格式统一、提取关键信息。
                                          • 最后把处理好的数据加载到目标存储系统里,方便后续 AI 模型使用这些数据进行训练或推理等操作。

                                            模型观察功能 Observability:可以收集和分析 AI 运行时产生的数据,像模型处理请求的耗时、资源使用量、生成结果的质量等,通过这些数据,开发人员能知道 AI 系统是否正常运行、有没有性能问题,还能找到出错原因,方便优化和改进 AI 应用。

                                            3. 获取API_KEY

                                            武技:获取阿里云百炼的 API_KEY

                                            1. 登录 阿里云百炼 页面。
                                            2. 依次点击 右上角头像 - API KEY - 创建我的 API KEY,然后将 API KEY 记录下来(SK 开头的),归属业务空间选择默认业务空间即可,描述可省略。

                                            JB5-1-SpringAI(一)

                                            JB5-1-SpringAI(一)

                                            1. 回到首页,根据提示开通模型调用服务:

                                            JB5-1-SpringAI(一)

                                            JB5-1-SpringAI(一)

                                            1. 将API-KEY设置到环境变量:
                                            # 设置系统环境变量
                                            setx DASHSCOPE_API_KEY sk-xxxxxxx
                                            # 检查 API-KEY 的环境变量是否生效
                                            echo %DASHSCOPE_API_KEY%
                                            

                                            E02. 整合后端项目

                                            武技:在 test 子项目中整合 SpringAI

                                            1. 添加三方依赖

                                            	
                                            	
                                            		org.springframework.boot
                                            		spring-boot-starter-web
                                            	
                                                
                                                
                                                    com.alibaba.cloud.ai
                                                    spring-ai-alibaba-starter
                                                    ${spring-ai-alibaba-starter.version}
                                                
                                            
                                            

                                            2. 开发主配文件

                                            server:
                                              port: 5501 # 端口号
                                            spring:
                                              application:
                                                name: test # 项目名称
                                              ai:
                                                dashscope:
                                                  api-key: ${DASHSCOPE_API_KEY} # 阿里云百炼 API_KEY
                                            

                                            3. 开发启动类

                                            package com.joezhou;
                                            /** @author 周航宇 */
                                            @SpringBootApplication
                                            public class SpringAiApp {
                                                public static void main(String[] args) {
                                                    SpringApplication.run(SpringAiApp.class, args);
                                                }
                                            }
                                            

                                            4. 开发控制器

                                            心法:ChatClient 用于与 AI 模型通信,支持同步和流式编程模型。

                                            1. 开发控制器:推荐使用 ChatClient.Builder.build() 的方式创建 ChatClient 客户端对象:
                                            package com.joezhou.controller;
                                            /** @author 周航宇 */
                                            @RequestMapping("/api/v1/aiChat")
                                            @RestController
                                            @CrossOrigin
                                            public class AiChatController {
                                                private final ChatClient chatClient;
                                                public AiChatController(ChatClient.Builder chatClientBuilder) {
                                                    this.chatClient = chatClientBuilder.build();
                                                }
                                            }
                                            
                                            1. 开发控制方法 base():
                                            @GetMapping("base")
                                            public String base(@RequestParam("msg") String msg) {
                                                return chatClient
                                                        // 初始化新会话并创建一个包含消息上下文的提示对象,可以理解为“开启新对话”
                                                        .prompt()
                                                        // 设置客户端传递过来的对话内容
                                                        .user(msg.trim())
                                                        // 同步发送消息,此时该方法会将响应结果一次性响应给前端
                                                        .call()
                                                        // 获取响应消息,此时需要将这个消息返回给前端
                                                        .content();
                                            }
                                            
                                            1. 测试 base() 控制方法:
                                            ### base()
                                            GET http://localhost:5501/api/v1/aiChat/base?msg=讲个笑话
                                            
                                            1. 开发控制方法 stream():
                                            /**
                                             * 流式对话中,控制方法的返回值类型需改更改为 Flux 类型
                                             * 流式对话中,需要在 @RequestMapping 注解中指定 produces=MediaType.TEXT_EVENT_STREAM_VALUE 项
                                             */
                                            @GetMapping(value = "stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
                                            public Flux stream(@RequestParam("msg") String msg) {
                                                Flux content = chatClient
                                                        // 初始化新会话并创建一个包含消息上下文的提示对象,可以理解为“开启新对话”
                                                        .prompt()
                                                        // 设置客户端传递过来的对话内容
                                                        .user(msg.trim())
                                                        // 流式响应,将响应结果以流的形式推送给前端
                                                        .stream()
                                                        // 获取响应消息,此时需要将这个消息返回给前端
                                                        .content();
                                                // 约定返回一个结束标记,方便前端灵活终止 SSE 推送
                                                return content.concatWith(Flux.just("[over]"));
                                            }
                                            
                                            1. 测试 stream() 控制方法:
                                            ### stream():中文可能会乱码,可以暂时使用英文对话进行测试。
                                            GET http://localhost:5501/api/v1/aiChat/stream?msg=用纯英文讲个笑话
                                            

                                            E03. 整合前端项目

                                            武技:创建 v5-1-llm-springai/springai-web 项目

                                            1. 使用 vite 创建 vue 项目:
                                            # 切换到工作空间目录,注意路径中不要有中文
                                            cd D:\workspace\java\v5-1-llm-springai
                                            # 创建Vue项目(第一遍安装需要输入y安装vite)
                                            npm create vite@5.5.1 springai-web -- --template vue
                                            
                                            1. 在 vite.config.js 文件中修改项目端口:
                                            import {defineConfig} from 'vite'
                                            import vue from '@vitejs/plugin-vue'
                                            // https://vitejs.dev/config/
                                            export default defineConfig({
                                                plugins: [vue()],
                                                server: {
                                                    host: 'localhost',//ip地址
                                                    port: 6051, // 设置服务启动端口号
                                                }
                                            })
                                            

                                            1. 添加Axios依赖

                                            # 安装Axios组件组件
                                            npm install axios@1.6.7 --save
                                            

                                            2. 添加ElementPlus

                                            1. 安装 ElementPlus 依赖:
                                            # 局部安装ElementPlus组件库
                                            npm install element-plus@2.5.3 --save
                                            
                                            1. 在 main.js 文件中引入 ElementPlus 依赖:
                                            import { createApp } from 'vue';
                                            import './style.css';
                                            import App from './App.vue';
                                            // ElementPlus组件库: 核心对象,核心CSS,显隐CSS,国际化对象
                                            import ElementPlus from 'element-plus';
                                            import 'element-plus/dist/index.css';
                                            import 'element-plus/theme-chalk/display.css';
                                            import {zhCn} from "element-plus/es/locale/index";
                                            // 使用ElementPlus组件库
                                            let app = createApp(App);
                                            app.use(ElementPlus, {locale: zhCn});
                                            app.mount('#app');
                                            

                                            3. 开发全局样式

                                            1. 在 style.css 文件中开发全局样式:
                                            body {
                                                margin: 0; /* 外边距 */
                                                padding: 20px 20%; /* 内边距 */
                                            }
                                            .timeline {
                                                background-color: #F5F5F5; /* 背景颜色 */
                                                padding-top: 20px; /* 内边距 */
                                                padding-right: 40px; /* 内边距 */
                                                border-radius: 10px; /* 圆角 */
                                                box-sizing: border-box; /* 忽略边框和内边距影响 */
                                                height: 75vh; /* 75% 视窗高度 */
                                                overflow-y: auto; /* 超出部分显示滚动条 */
                                            }
                                            .el-checkbox {
                                                margin-top: 40px; /* 外边距 */
                                                margin-right: 0 !important; /* 外边距 */
                                            }
                                            .image {
                                                width: 200px; /* 宽度 */
                                                height: 200px; /* 高度 */
                                            }
                                            .send-ipt {
                                                height: 60px; /* 高度 */
                                            }
                                            .userContent {
                                                background-color: #f1d2d2; /* 背景颜色 */
                                                display: inline-block; /* 内联块元素 */
                                                padding: 10px 20px; /* 内边距 */
                                                border-radius: 10px; /* 圆角 */
                                            }
                                            

                                            4. 开发Vue页面

                                            JB5-1-SpringAI(一)

                                            1. 布局:开发 HTML 代码如下:
                                              
                                                
                                                  
                                                    
                                            示例图片
                                            {{ e.content }}
                                            {{ e.content }}
                                            1. 脚本:开发 JS 代码如下:
                                            相关 API 方法描述
                                            sendMsg()当用户发送消息时触发
                                            baseQA(url)向指定 URL 发送消息,使用 AJAX 接收同步响应内容
                                            streamQA(url)向指定 URL 发送消息,使用 EventSource 接收流式响应内容
                                            imageQA(url)向指定 URL 发送消息,使用 URL.createObjectURL() 接收二进制响应内容
                                            import {ref} from "vue";
                                            import axios from "axios";
                                            // AI接口地址
                                            const API_URL = 'http://localhost:5501/api/v1/aiChat/base';
                                            // 设置超时时间为 300 秒,因为文生图比较慢,需要等待较长时间
                                            const AJAX = axios.create({timeout: 300000});
                                            // AI欢迎词配置
                                            let activities = ref([{content: '我是AI,有何指教?', timestamp: now(), role: 'ai'}]);
                                            // 用户输入的内容
                                            let msg = ref('');
                                            // AI是否正在回复中
                                            let isReplying = false;
                                            // 是否开启流式问答
                                            let enableStream = ref(true);
                                            // 是否开启文生图
                                            let enableImage = ref(false);
                                            // 时间线对象
                                            let timeline;
                                            // 当用户发送消息时触发
                                            function sendMsg() {
                                              // 如果用户输入的内容为空,则不处理
                                              if (msg.value === '') return;
                                              // 如果AI正在回复中,则不处理
                                              if (isReplying) return;
                                              // 将AI回复中标记为true
                                              isReplying = true;
                                              // 加入用户输入的信息和AI回复的信息
                                              activities.value.push({content: msg.value, timestamp: now(), role: 'user'});
                                              activities.value.push({content: 'waiting...', timestamp: now(), role: 'ai'});
                                              // 文生图
                                              if (enableImage.value) imageQA(API_URL);
                                              // 流式问答
                                              else if (enableStream.value) streamQA(API_URL.replace('base', 'stream'));
                                              // 基础问答
                                              else baseQA(API_URL);
                                              // 清空用户输入的内容
                                              msg.value = '';
                                            }
                                            /*========== 基础对话 ==========*/
                                            async function baseQA(url) {
                                              // 发送请求
                                              let res = await AJAX.get(`${url}?msg=${msg.value}`);
                                              // 如果请求成功,则加入AI回复的信息,如果请求失败,则加入错误信息
                                              activities.value[activities.value.length - 1].content =
                                                  res.status === 200 ? res.data : '请求失败,请稍后重试!';
                                              // 始终滚动到底部
                                              timeline.scrollTop = timeline.scrollHeight - timeline.clientHeight;
                                              // 将AI回复中标记为false
                                              isReplying = false;
                                            }
                                            /*========== 流式对话 ==========*/
                                            // SSE客户端对象:用于接收服务端推送的消息
                                            let sse;
                                            function streamQA(url) {
                                              // 关闭上一个SSE连接
                                              if (sse) sse.close();
                                              // SSE服务端推送时
                                              sse = new EventSource(`${url}?msg=${msg.value}`);
                                              // SSE客户端接收到消息时
                                              sse.onmessage = (ev) => {
                                                // 如果读取到 [over] 结束标记,则关闭 SSE 连接,否则1秒执行一次
                                                if (ev.data === '[over]') {
                                                  sse.close();
                                                  isReplying = false;
                                                  return;
                                                }
                                                // 拼接AI回复的信息
                                                activities.value[activities.value.length - 1].content += ev.data;
                                                // 始终滚动到底部
                                                timeline.scrollTop = timeline.scrollHeight - timeline.clientHeight;
                                              };
                                              // SSE连接成功时
                                              sse.onopen = () => activities.value[activities.value.length - 1].content = '';
                                            }
                                            /*========== 文生图 ==========*/
                                            async function imageQA(url) {
                                              // 发送请求
                                              let res = await AJAX.get(`${url}?msg=${msg.value}`, {responseType: 'blob'});
                                              // 如果请求成功,则加入AI回复的图片
                                              if (res.status === 200) {
                                                activities.value[activities.value.length - 1].type = 'image';
                                                activities.value[activities.value.length - 1].content = "";
                                                activities.value[activities.value.length - 1].content += "图片已生成!";
                                                activities.value[activities.value.length - 1].src = URL.createObjectURL(res.data);
                                                // 始终滚动到底部
                                                timeline.scrollTop = timeline.scrollHeight - timeline.clientHeight;
                                                // 将AI回复中标记为false
                                                isReplying = false;
                                              }
                                            }
                                            // 获取当前时间
                                            function now() {
                                              let now = new Date();
                                              return now.toLocaleDateString() + " " + now.toLocaleTimeString();
                                            }
                                            onload = () => timeline = document.querySelector("#timeline");
                                            
                                            

                                            S02. SpringAI初级

                                            E01. 预设角色

                                            心法:在使用 ChatClient.Builder 构建 ChatClient 时,可以使用 builder.defaultSystem() 方法设置预设的系统信息(字符串),该信息通常会作为初始指令传递给 AI 模型,帮助其理解对话的上下文、角色或任务要求。

                                            注意事项:

                                            1. 系统信息通常是纯文本,但某些 API 可能支持 Markdown 或其他格式(需查阅具体文档)。
                                            2. 避免使用特殊字符或格式,除非 API 明确支持。
                                            3. 系统信息会应用于所有对话轮次,除非在单次请求中覆盖它。
                                            4. 某些 API 可能对系统信息的长度有限制(例如 OpenAI 的 GPT 模型建议控制在几百个 token 内)。
                                            5. 不要在系统信息中包含敏感信息(如 API 密钥、用户数据等)。

                                            武技:测试 SpringAI 的预设角色功能

                                            1. 开发控制器

                                            1. 开发控制器类:
                                            package com.joezhou.controller;
                                            /** @author 周航宇 */
                                            @RequestMapping("/api/v1/systemRole")
                                            @RestController
                                            @CrossOrigin
                                            public class SystemRoleController {
                                                private static final String SYSTEM_ROLE_PROMPT = """
                                                        你叫詹姆斯9527,你是一个脾气非常不好的人;
                                                        你从来不会用 “我” 来指代自己,你只会用 “老子” 来指代自己;
                                                        今天的日期是 {today};
                                                        """;
                                                private final ChatClient chatClient;
                                                public SystemRoleController(ChatClient.Builder chatClientBuilder) {
                                                    this.chatClient = chatClientBuilder
                                                            .defaultSystem(SYSTEM_ROLE_PROMPT)
                                                            .build();
                                                }
                                                @GetMapping("base")
                                                public String base(@RequestParam("msg") String msg) {
                                                    return chatClient
                                                            .prompt()
                                                            .user(msg)
                                                            .system(e -> e.param("today", LocalDate.now()))
                                                            .call()
                                                            .content();
                                                }
                                                @GetMapping(value = "stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
                                                public Flux stream(@RequestParam("msg") String msg) {
                                                    return chatClient
                                                            .prompt()
                                                            .user(msg)
                                                            .system(e -> e.param("today", LocalDate.now()))
                                                            .stream()
                                                            .content()
                                                            .concatWith(Flux.just("[over]"));
                                                }
                                            }
                                            
                                            1. 测试控制类:
                                            ### base()
                                            GET http://localhost:5501/api/v1/systemRole/base?msg=你是谁啊
                                            ### stream()
                                            GET http://localhost:5501/api/v1/systemRole/stream?msg=用纯英文告诉我你是谁啊
                                            
                                            1. 在 App.vue 文件中,将前端代码替换为对应路径进行测试:
                                            	// AI接口地址
                                            	const URL = 'http://localhost:5501/api/v1/systemRole/base';
                                            
                                            

                                            E02. 日志通知

                                            心法:SimpleLoggerAdvisor 是一个极为实用的 AOP(面向切面编程)增强器,它的主要功能是自动记录 AI 服务交互过程中的请求(Request)和响应(Response)数据,常用于开发调试,生产监控,合规审计等场景。

                                            SimpleLoggerAdvisor 核心特性如下:

                                            • 全链路数据捕获:
                                              • 能够精准记录请求到 AI 服务的完整提示信息(包含用户消息、系统消息、历史对话等内容)。
                                              • 可以详细记录从 AI 服务返回的响应数据,涵盖生成的文本、元数据(像 token 数量、模型信息)以及其他相关信息。
                                              • 灵活的日志级别控制:
                                                • 支持根据不同的日志级别(例如 DEBUG、INFO)来开启或关闭日志记录功能。
                                                • 可以对日志的格式进行自定义设置,从而满足不同的调试和审计要求。
                                                • 非侵入式设计:
                                                  • 采用 AOP 切面方式实现,不会对现有的业务逻辑造成影响。
                                                  • 能够通过简单的配置进行启用或禁用操作。

                                                    SimpleLoggerAdvisor 具体工作流程如下:

                                                    1. 在使用 ChatClient.Builder 构建 ChatClient 时,使用 builder.defaultAdvisors() 方法设置默认的 SimpleLoggerAdvisor 实例。
                                                    2. 在发送请求之前,SimpleLoggerAdvisor 会捕获并记录所有即将发送给 AI 服务的消息内容。
                                                    3. 在收到响应之后,SimpleLoggerAdvisor 会提取并记录响应中的关键信息。
                                                    4. 这些记录的信息会被输出到应用的日志系统中(如 SLF4J、Logback 等),方便后续查看和分析。

                                                    武技:添加日志通知

                                                    1. 开发主配文件

                                                    心法:想要使用日志通知,则需要将 advisor 包的日志记录级别设置为 DEBUG 级别。

                                                    logging:
                                                      level:
                                                        org:
                                                          springframework:
                                                            ai:
                                                              chat:
                                                                client:
                                                                  advisor: DEBUG # advisor 日志级别
                                                    

                                                    2. 开发控制器

                                                    1. 开发控制器:
                                                    package com.joezhou.controller;
                                                    /** @author 周航宇 */
                                                    @RequestMapping("/api/v1/logAdvisor")
                                                    @RestController
                                                    @CrossOrigin
                                                    public class LogAdvisorController {
                                                        private final ChatClient chatClient;
                                                        public LogAdvisorController(ChatClient.Builder chatClientBuilder) {
                                                            this.chatClient = chatClientBuilder
                                                                    // p1:请求日志打印内容(lambda 方式)
                                                                    // p2:响应日志打印内容(lambda 方式)
                                                                    // p3:通知顺序,值越小越靠前
                                                                    .defaultAdvisors(new SimpleLoggerAdvisor(
                                                                            req -> req.userText(),
                                                                            res -> res.getResult().getOutput().getText(),
                                                                            0
                                                                    ))
                                                                    .build();
                                                        }
                                                        @GetMapping(value = "base")
                                                        public String base(@RequestParam("msg") String msg) {
                                                            return chatClient
                                                                    .prompt()
                                                                    .user(msg)
                                                                    .call()
                                                                    .content();
                                                        }
                                                        @GetMapping(value = "stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
                                                        public Flux stream(@RequestParam("msg") String msg) {
                                                            return chatClient
                                                                    .prompt()
                                                                    .user(msg)
                                                                    .stream()
                                                                    .content().concatWith(Flux.just("[over]"));
                                                        }
                                                    }
                                                    
                                                    1. 测试控制器:发送请求后,观察控制台是否记录了对应的请求和响应的日志:
                                                    ### base():发送请求后,观察控制台是否记录了对应的请求和响应的日志
                                                    GET http://localhost:5501/api/v1/logAdvisor/base?msg=讲个笑话
                                                    ### stream():发送请求后,观察控制台是否记录了对应的请求和响应的日志
                                                    GET http://localhost:5501/api/v1/logAdvisor/stream?msg=用纯英文讲个笑话
                                                    

                                                    E03. 自定义通知

                                                    心法:Advisors API 提供了一种灵活而强大的方法来拦截、修改和增强 Spring 应用程序中的 AI 驱动的交互,它允许在 ChatClient 执行 call() 或 stream() 等方法时自动触发特定行为。

                                                    Advisors 核心组件如下:

                                                    核心组件描述
                                                    CallAroundAdvisor非流式环绕通知器
                                                    CallAroundAdvisorChain非流式环绕通知链
                                                    StreamAroundAdvisor流式环绕通知器
                                                    StreamAroundAdvisorChain流式环绕通知链
                                                    AdvisedRequest建议请求对象,和 AdvisedResponse 共享一个上下文对象
                                                    AdvisedResponse建议响应对象,和 AdvisedRequest 共享一个上下文对象

                                                    Advisors 执行流程如下:

                                                    1. SpringAI 框架根据用户的提示(Prompt)创建一个建议请求对象(AdvisedRequest)对象,并附带一个空的通知上下文对象(AdvisorContext)。
                                                    2. 通知链(Advisor Chain)中的每个通知(Advisor)都会处理或修改该请求:
                                                    3. SpringAI 框架中,后一个通知(Adivsor)会将请求发送给聊天模型(Chat Model)。
                                                    4. 聊天模型(Chat Model)返回聊天响应(ChatResponse)给通知链(Advisor Chain)并转换为建议响应对象(AdvisedResponse),该对象中包含共享的通知上下文对象(AdvisorContext)实例。
                                                    5. 通知链(Advisor Chain)中的每个通知(Advisor)都会处理或修改该响应。
                                                    6. 最终将建议响应对象(AdvisedResponse)转换为聊天响应(ChatResponse)并返回给客户端。

                                                    JB5-1-SpringAI(一)

                                                    武技:开发并使用自定义通知类

                                                    1. 开发通知类

                                                    自定义通知类要求如下:

                                                    1. 重写 CallAroundAdvisor -> aroundCall():对应 chatClient 的 call() 方法的环绕通知。
                                                    2. 重写 StreamAroundAdvisor -> aroundStream():对应 chatClient 的 stream() 方法的环绕通知。
                                                    3. 重写 Advisor -> getName():用于获取唯一的 Advisor 名称。
                                                    4. 重写 Ordered -> getOrder():用于设置在通知链中 Advisor 的执行顺序,数字越小越靠前。
                                                    package com.joezhou.advisor;
                                                    /** @author 周航宇 */
                                                    @SuppressWarnings("all")
                                                    @Slf4j
                                                    public class MyAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
                                                        /**
                                                         * 对应 chatClient 的 call() 方法的环绕通知
                                                         *
                                                         * @param advisedRequest 请求对象
                                                         * @param chain          通知链,用于管理这些通知的执行顺序
                                                         * @return 响应对象
                                                         */
                                                        @Override
                                                        public AdvisedResponse aroundCall(AdvisedRequest advisedRequest,
                                                                                          CallAroundAdvisorChain chain) {
                                                            log.info("进入 aroundCall() 方法");
                                                            log.info("请求参数:{}", advisedRequest.userText());
                                                            // 继续执行通知链中的下一个通知
                                                            // 如果已经是最后一个通知,则会调用目标方法
                                                            // 并将目标方法的执行结果封装在 AdvisedResponse 中返回
                                                            AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);
                                                            log.info("响应结果:{}", advisedResponse
                                                                    .response()
                                                                    .getResult()
                                                                    .getOutput()
                                                                    .getText());
                                                            log.info("离开 aroundCall() 方法");
                                                            return advisedResponse;
                                                        }
                                                        /**
                                                         * 对应 chatClient 的 stream() 方法的环绕通知
                                                         *
                                                         * @param advisedRequest 请求对象
                                                         * @param chain          通知链,用于管理这些通知的执行顺序
                                                         * @return 响应对象
                                                         */
                                                        @Override
                                                        public Flux aroundStream(AdvisedRequest advisedRequest,
                                                                                                  StreamAroundAdvisorChain chain) {
                                                            log.info("进入 aroundStream() 方法");
                                                            log.info("请求参数:{}", advisedRequest.userText());
                                                            // 继续执行通知链中的下一个通知
                                                            // 如果已经是最后一个通知,则会调用目标方法
                                                            // 并将目标方法的执行结果封装在 Flux 中返回。
                                                            Flux advisedResponseFlux = chain.nextAroundStream(advisedRequest);
                                                            log.info("离开 aroundStream() 方法");
                                                            advisedResponseFlux = advisedResponseFlux.doOnNext(advisedResponse -> {
                                                                log.info("响应结果:{}", advisedResponse
                                                                        .response()
                                                                        .getResult()
                                                                        .getOutput()
                                                                        .getText());
                                                            });
                                                            return advisedResponseFlux;
                                                        }
                                                        /** 唯一的 Advisor 名称 */
                                                        @Override
                                                        public String getName() {
                                                            return this.getClass().getSimpleName();
                                                        }
                                                        /** 在通知链中 Advisor 的执行顺序,数字越小越靠前 */
                                                        @Override
                                                        public int getOrder() {
                                                            return 0;
                                                        }
                                                    }
                                                    

                                                    2. 开发控制器

                                                    1. 开发控制器:
                                                    package com.joezhou.controller;
                                                    /** @author 周航宇 */
                                                    @RestController
                                                    @CrossOrigin
                                                    @RequestMapping("/api/v1/myAdvisor")
                                                    public class MyAdvisorController {
                                                        private final ChatClient chatClient;
                                                        public MyAdvisorController(ChatClient.Builder chatClientBuilder) {
                                                            this.chatClient = chatClientBuilder
                                                                    .defaultAdvisors(new MyAdvisor())
                                                                    .build();
                                                        }
                                                        @GetMapping("base")
                                                        public String base(@RequestParam("msg") String msg) {
                                                            return chatClient
                                                                    .prompt()
                                                                    .user(msg)
                                                                    .call()
                                                                    .content();
                                                        }
                                                        @GetMapping(value = "stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
                                                        public Flux stream(@RequestParam("msg") String msg) {
                                                            return chatClient
                                                                    .prompt()
                                                                    .user(msg)
                                                                    .stream()
                                                                    .content()
                                                                    .concatWith(Flux.just("[over]"));
                                                        }
                                                    }
                                                    
                                                    1. 测试控制器:发送请求后,观察控制台是否记录了对应的请求和响应的日志:
                                                    ### base():发送请求后,观察控制台是否记录了对应的请求和响应的日志
                                                    GET http://localhost:5501/api/v1/myAdvisor/base?msg=讲个笑话
                                                    ### stream():发送请求后,观察控制台是否记录了对应的请求和响应的日志
                                                    GET http://localhost:5501/api/v1/myAdvisor/stream?msg=用纯英文讲个笑话
                                                    

                                                    E04. 记忆通知

                                                    心法:ChatMemory 接口用于存储聊天对话历史记录,它提供了添加消息到对话、从对话中检索消息和清除对话历史记录的方法。

                                                    ChatMemory 常用实现:

                                                    1. InMemoryChatMemory:基于内存存储:
                                                      • 线程安全:底层使用 CopyOnWriteArrayList 实现,线程安全。
                                                      • 临时存储:应用重启后数据丢失,适用于短期会话。
                                                      • 简单轻量:无需外部依赖,直接使用 JVM 内存。
                                                      • 典型场景:测试环境或开发调试。
                                                      • LimitedSizeChatMemory:基于内存存储:
                                                        • 容量限制:通过 maxMessages 参数控制历史消息数量(如保留最近 10 条)。
                                                        • 自动截断:超出容量时移除最早的消息,避免内存溢出。
                                                        • 适配性强:可包装其他实现,如 InMemoryChatMemory 等。
                                                        • 典型场景:需控制内存使用的生产环境。
                                                        • RedisChatMemory:基于 Redis 存储:
                                                          • 分布式共享:支持多实例应用共享会话历史。
                                                          • 持久化:数据可配置为持久化存储(如 RDB/AOF)。
                                                          • 高性能:基于内存的缓存,读写速度快。
                                                          • 典型场景:微服务架构或需要长时间保留对话历史的应用。
                                                          • JdbcChatMemory:基于关系型数据库:
                                                            • 结构化存储:消息存储在数据库表中,支持复杂查询。
                                                            • 事务支持:与数据库事务集成,确保数据一致性。
                                                            • 扩展性:可利用数据库索引和备份机制。
                                                            • 典型场景:需要深度分析对话数据的应用(如客服系统)。
                                                            • SessionChatMemory:基于 Spring 会话:
                                                              • Web 集成:自动与用户 HTTP 会话绑定。
                                                              • 会话生命周期:数据随会话过期而清除。
                                                              • 适配性:依赖 Spring Web 模块,需配置 HttpSession 对象。
                                                              • 典型场景:Web 应用中的用户对话管理。
                                                              • CompositeChatMemory:组合多个 ChatMemory 实现:
                                                                • 多策略支持:同时使用多种存储方式(如内存 + Redis)。
                                                                • 分层存储:例如,近期消息存内存,历史消息存数据库。
                                                                • 自定义路由:通过策略决定消息存储位置。
                                                                • 典型场景:混合性能与持久化需求的场景。

                                                    ChatMemory 使用流程:

                                                    1. 创建 InMemoryChatMemory 实例:仅做测试。
                                                    2. 创建 PromptChatMemoryAdvisor 实例:它是一个专门负责自动管理对话历史的 Advisor:
                                                      • 在发送请求前,自动从 ChatMemory 中提取历史消息并添加到请求的 messages 列表中。
                                                      • 在收到响应后,自动将新生成的消息(用户消息和助手回复)保存到 ChatMemory 中。
                                                      • 在使用 ChatClient.Builder 构建 ChatClient 时,使用 builder.defaultAdvisors() 方法设置默认的 Advisor 对象。
                                                      • 使用 chatClient.advisors(e -> e.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10)) 设置记忆大小。

                                                    武技:测试使用记忆通知功能

                                                    1. 开发控制器

                                                    1. 开发控制器类:
                                                    package com.joezhou.controller;
                                                    /** @author 周航宇 */
                                                    @RequestMapping("/api/v1/chatMemory")
                                                    @RestController
                                                    @CrossOrigin
                                                    public class ChatMemoryController {
                                                        private final ChatClient chatClient;
                                                        public ChatMemoryController(ChatClient.Builder chatClientBuilder) {
                                                            this.chatClient = chatClientBuilder
                                                                    .defaultAdvisors(new PromptChatMemoryAdvisor(new InMemoryChatMemory()))
                                                                    .build();
                                                        }
                                                        @GetMapping("base")
                                                        public String base(@RequestParam("msg") String msg) {
                                                            return chatClient
                                                                    .prompt()
                                                                    .user(msg)
                                                                    // 设置记忆大小
                                                                    .advisors(e -> e.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                                                                    .call()
                                                                    .content();
                                                        }
                                                        @GetMapping(value = "stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
                                                        public Flux stream(@RequestParam("msg") String msg) {
                                                            return chatClient
                                                                    .prompt()
                                                                    .user(msg)
                                                                    // 设置记忆大小
                                                                    .advisors(e -> e.param(AbstractChatMemoryAdvisor.CHAT_MEMORY_RETRIEVE_SIZE_KEY, 10))
                                                                    .stream()
                                                                    .content()
                                                                    .concatWith(Flux.just("[over]"));
                                                        }
                                                    }
                                                    
                                                    1. 测试控制类:
                                                    ### base() - 01
                                                    GET http://localhost:5501/api/v1/chatMemory/base?msg=我今年100岁了
                                                    ### base() - 02
                                                    GET http://localhost:5501/api/v1/chatMemory/base?msg=我多大了
                                                    ### stream() - 01
                                                    GET http://localhost:5501/api/v1/chatMemory/stream?msg=I am 101 years old.
                                                    ### stream() - 02
                                                    GET http://localhost:5501/api/v1/chatMemory/stream?msg=how old are me?
                                                    
                                                    1. 在 App.vue 页面中,将前端代码替换为对应路径进行测试:
                                                    	// AI接口地址
                                                    	const URL = 'http://localhost:5501/api/v1/chatMemory/base';
                                                    
                                                    

                                                    E05. 工具调用

                                                    心法:Tool Calling 是一种提升大语言模型(LLM)功能的技术,通过定义一组工具(如当前本地时间查询、当前天气查询等),使 LLM 能够调用这些工具以完成特定任务,其核心思想是将 LLM 的生成能力与外部工具的功能相结合,从而增强其解决问题的能力。

                                                    Tools Calling 的主要流程如下:

                                                    • 定义工具:通过注解或接口明确工具的功能及其描述。
                                                    • 注册工具:将工具集成到 LLM 的调用环境中。
                                                    • 调用工具:在用户输入中触发工具调用,LLM 根据工具的描述生成相应的调用请求。

                                                      JB5-1-SpringAI(一)

                                                      武技:测试工具调用

                                                      1. 开发配置类

                                                      1. 开发函数调用配置类:
                                                      相关注解描述
                                                      @Tool用于将方法标记为可被 AI 调用的工具
                                                      @ToolParam描述方法参数的含义,帮助 AI 理解参数用途
                                                      package com.joezhou.tools;
                                                      /** @author 周航宇 */
                                                      public class WeatherTool {
                                                          @Tool(description = "获取今天的天气")
                                                          String getWeather(@ToolParam(description = "城市名称") String cityName) {
                                                              if ("上海".equals(cityName)) {
                                                                  return "今天上海的天气是:晴转多云,再转大雨,再转冰雹,1103摄氏度";
                                                              } else if ("北京".equals(cityName)) {
                                                                  return "今天北京的天气是:多云转晴,再转小雨,再转闪电,1104摄氏度。";
                                                              } else {
                                                                  return "不知道";
                                                              }
                                                          }
                                                      }
                                                      

                                                      2. 开发控制器

                                                      1. 开发控制器:在使用 ChatClient.Builder 构建 ChatClient 时,使用 builder.defaultTools() 方法设置默认的 Tool Calling 对象:
                                                      package com.joezhou.controller;
                                                      /** @author 周航宇 */
                                                      @RequestMapping("/api/v1/toolCalling")
                                                      @RestController
                                                      @CrossOrigin
                                                      public class ToolCallingController {
                                                          private final ChatClient chatClient;
                                                          public ToolCallingController(ChatClient.Builder chatClientBuilder) {
                                                              this.chatClient = chatClientBuilder
                                                                      .defaultTools(new WeatherTool())
                                                                      .build();
                                                          }
                                                          @GetMapping("base")
                                                          public String base(@RequestParam("msg") String msg) {
                                                              return chatClient
                                                                      .prompt()
                                                                      .user(msg)
                                                                      .call()
                                                                      .content();
                                                          }
                                                          @GetMapping(value = "stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
                                                          public Flux stream(@RequestParam("msg") String msg) {
                                                              return chatClient
                                                                      .prompt()
                                                                      .user(msg)
                                                                      .stream()
                                                                      .content()
                                                                      .concatWith(Flux.just("[over]"));
                                                          }
                                                      }
                                                      
                                                      1. 测试控制器:
                                                      ### base()
                                                      GET http://localhost:5501/api/v1/toolCalling/base?msg=北京天气如何
                                                      ### stream()
                                                      GET http://localhost:5501/api/v1/toolCalling/stream?msg=上海天气如何
                                                      
                                                      1. 在 App.vue 文件中,将前端代码替换为对应路径进行测试:
                                                      	// AI接口地址
                                                      	const URL = 'http://localhost:5501/api/v1/toolCalling/base';
                                                      
                                                      

                                                      E06. 检索增强

                                                      心法:RAG,全称 Retrieval Augmented Generation,检索增强生成。

                                                      RAG 理解:

                                                      • RAG 就像给语言模型请了个 “专属小助手”,传统语言模型只能靠提前背好的 “知识点”(预训练知识)回答问题,一旦遇到新知识或特定领域问题,可能就答不上来,或者答案不准确。
                                                      • 而 RAG 额外设置了一个 “知识仓库”,当遇到问题时,它先快速从这个仓库(比如外部文档、数据库)里,用高效的检索算法找到和问题最相关的信息,再把这些信息交给语言模型,让模型结合它们给出答案。这样一来,模型不仅能拿到最新鲜、准确的知识,在回答专业问题时也更靠谱,相当于随时有新 “教材” 辅助答题,而不是只靠老本行了。

                                                        QuestionAnswerAdvisor:Spring AI 提供的拦截器,负责自动执行 RAG 流程,当调用 chatClient.call() 时,QuestionAnswerAdvisor 会自动执行以下操作:

                                                        1. 拦截用户发送的问题内容。
                                                        2. 将用户问题转换为向量(Embedding)。
                                                        3. 在 VectorStore 中检索相关文档(最相似的文档片段)。
                                                        4. 将检索结果作为上下文注入 AI 模型的提示(Prompt)中。
                                                        5. 将增强后的提示被发送给 AI 模型,生成回答时会参考检索到的上下文。

                                                        武技:搭建检索增强环境

                                                        1. 开发检索文件

                                                        1. 开发 classpath:rag/company.txt 检索文件如下:
                                                        公司成立于1945年10月1号。
                                                        公司法人是塔拉。
                                                        公司经理是周杰伦。
                                                        公司法务代表是林俊杰。
                                                        公司销售团队包括赵四,刘能,广坤,王云等。
                                                        

                                                        2. 开发配置类

                                                        package com.joezhou.config;
                                                        /** @author 周航宇 */
                                                        @Configuration
                                                        public class RagConfig {
                                                            @Autowired
                                                            private ResourceLoader resourceLoader;
                                                            /**
                                                             * 构建一个VectorStore对象,用于存储文档
                                                             *
                                                             * @param embeddingModel 嵌入模型,用于将文本转换为向量
                                                             * @return VectorStore对象
                                                             */
                                                            @SneakyThrows
                                                            @Bean
                                                            VectorStore vectorStore(EmbeddingModel embeddingModel) {
                                                                // 创建一个SimpleVectorStore对象,用于存储文档
                                                                SimpleVectorStore simpleVectorStore = SimpleVectorStore.builder(embeddingModel).build();
                                                                // 从classpath下加载文件
                                                                Resource resource = resourceLoader.getResource("classpath:rag/company.txt");
                                                                // 使用Java 8的Stream API读取文件内容
                                                                String content = new String(
                                                                        resource.getInputStream().readAllBytes(),
                                                                        StandardCharsets.UTF_8);
                                                                // 生成一个 RAG 文档
                                                                List documents = List.of(new Document(content));
                                                                // 将文档添加到SimpleVectorStore中
                                                                simpleVectorStore.add(documents);
                                                                // 返回SimpleVectorStore对象
                                                                return simpleVectorStore;
                                                            }
                                                        }
                                                        

                                                        3. 开发控制器

                                                        1. 开发控制器:
                                                        package com.joezhou.controller;
                                                        /** @author 周航宇 */
                                                        @RequestMapping("/api/v1/rag")
                                                        @RestController
                                                        @CrossOrigin
                                                        public class RagController {
                                                            private final ChatClient chatClient;
                                                            public RagController(ChatClient.Builder chatClientBuilder, VectorStore vectorStore) {
                                                                this.chatClient = chatClientBuilder
                                                                        .defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
                                                                        .build();
                                                            }
                                                            @GetMapping("base")
                                                            public String base(@RequestParam("msg") String msg) {
                                                                return chatClient
                                                                        .prompt()
                                                                        .user(msg)
                                                                        .call()
                                                                        .content();
                                                            }
                                                            @GetMapping(value = "stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
                                                            public Flux stream(@RequestParam("msg") String msg) {
                                                                return chatClient
                                                                        .prompt()
                                                                        .user(msg)
                                                                        .stream()
                                                                        .content()
                                                                        .concatWith(Flux.just("[over]"));
                                                            }
                                                        }
                                                        
                                                        1. 测试控制类:
                                                        ### base()
                                                        GET http://localhost:5501/api/v1/rag/base?msg=公司成员架构是怎么样的
                                                        ### stream()
                                                        GET http://localhost:5501/api/v1/rag/stream?msg=公司成立时间
                                                        
                                                        1. 在 App.vue 文件中,将前端代码替换为对应路径进行测试:
                                                        	// AI接口地址
                                                        	const URL = 'http://localhost:5501/api/v1/rag/base';
                                                        
                                                        

                                                        E07. 文生图

                                                        心法:ImageModel 是 SpringAI 提供的,与图像生成模型(如通义万相)交互的服务接口。

                                                        文生图流程:

                                                        1. 使用 ImageOptionsBuilder.builder().build() 创建图像的配置对象:
                                                          • width/height:图像尺寸(1024×1024 像素)。
                                                          • model:使用的模型(阿里云通义万相的基础文生图模型)。
                                                          • N:生成图像的数量(此处为 1 张)。
                                                          • 将用户描述和配置封装为 ImagePrompt 对象。
                                                          • 调用模型服务生成图像,返回 ImageResponse 响应对象。
                                                          • 检查结果列表是否为空(防止无生成结果)。
                                                          • 获取第一个生成图像的 URL(模型通常返回临时存储的 URL)。
                                                          • 从 URL 下载图像到内存(BufferedImage)。
                                                          • 通过 HttpServletResponse 的输出流将图像以 JPEG 格式返回给客户端。
                                                          • 使用 try-with-resources 确保流自动关闭,避免资源泄漏。

                                                        武技:根据用户输入的文本描述,调用阿里云通义万相模型生成图像,并将图像返回给客户端。

                                                        1. 开发控制器

                                                        1. 开发控制器:
                                                        package com.joezhou.controller;
                                                        import java.net.URL;
                                                        /** @author 周航宇 */
                                                        @RequestMapping("/api/v1/image")
                                                        @RestController
                                                        @CrossOrigin
                                                        public class ImageController {
                                                            private final ImageModel imageModel;
                                                            public ImageController(ImageModel imageModel) {
                                                                this.imageModel = imageModel;
                                                            }
                                                            @GetMapping({"base", "stream"})
                                                            public void base(@RequestParam("msg") String msg,
                                                                             HttpServletResponse resp) throws IOException {
                                                                // 创建一个 ImageOptions 对象,使用 ImageOptionsBuilder 构建器模式进行配置
                                                                ImageOptions options = ImageOptionsBuilder.builder()
                                                                        // 设置生成图片的宽度为 1024 像素
                                                                        .width(1024)
                                                                        // 设置生成图片的高度为 1024 像素
                                                                        .height(1024)
                                                                        // 指定使用的图像生成模型为 "wanx-v1",是阿里云通义万相模型中的基础文生图模型
                                                                        .model("wanx-v1")
                                                                        // 设置生成图片的数量为 1 张
                                                                        .N(1)
                                                                        // 构建 ImageOptions 对象
                                                                        .build();
                                                                // 创建一个 ImagePrompt 对象,用于封装生成图片的提示信息和配置选项
                                                                // msg 是用户传入的生成图片的提示文本,options 是前面配置好的图片生成选项
                                                                ImagePrompt imagePrompt = new ImagePrompt(msg, options);
                                                                // 调用 imageModel 对象的 call 方法,传入 ImagePrompt 对象,发起图片生成请求
                                                                // 并将生成结果存储在 ImageResponse 对象中
                                                                ImageResponse imageResponse = imageModel.call(imagePrompt);
                                                                // 从 ImageResponse 对象中获取生成的图片结果列表
                                                                List results = imageResponse.getResults();
                                                                // 检查生成结果列表是否为空
                                                                if (results.isEmpty()) {
                                                                    // 如果为空,抛出一个运行时异常,提示没有找到生成结果
                                                                    throw new RuntimeException("No results found");
                                                                }
                                                                // 从结果列表中获取第一个生成结果
                                                                // 并从该结果中获取生成图片的 URL
                                                                String imageUrl = results.get(0).getOutput().getUrl();
                                                                // 创建一个 URL 对象,用于表示生成图片的 URL
                                                                URL url = new URL(imageUrl);
                                                                // 使用 ImageIO 类的 read 方法从指定的 URL 读取图片,并将其存储为 BufferedImage 对象
                                                                BufferedImage bufferedImage = ImageIO.read(url);
                                                                // 从 HttpServletResponse 对象中获取输出流,使用 try-with-resources 语句确保流会自动关闭
                                                                try (ServletOutputStream outputStream = resp.getOutputStream()) {
                                                                    // 使用 ImageIO 类的 write 方法将 BufferedImage 对象以 JPEG 格式写入响应输出流
                                                                    ImageIO.write(bufferedImage, "jpg", outputStream);
                                                                    // 刷新输出流,确保所有数据都被发送到客户端
                                                                    outputStream.flush();
                                                                }
                                                            }
                                                        }
                                                        
                                                        1. 在 App.vue 文件中,将前端代码替换为对应路径进行测试:
                                                        	// AI接口地址
                                                        	const URL = 'http://localhost:5501/api/v1/image/base';
                                                        
                                                        

                                                        Java道经第5卷 - 第1阶 - SpringAI(一)


                                                        传送门:JB5-1-SpringAI(一)

                                                        传送门:JB5-2-SpringAI(二)

免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们。

目录[+]

取消
微信二维码
微信二维码
支付宝二维码