Spring AI 框架-快速搭建以及会话日志(笔记)
概述:
Spring AI是Spring生态中应用于人工智能领域的应用框架,它的目标是将Spring 生态系统的设计原则(如可移植性、模块化设计)应用于AI领域,Spring AI更新迭代非常快,对Spring生态非常友好,可以大大简化开发。
Spring AI能做什么?
支持目前主流大语言模型平台,例如 OpenAI、Microsoft、Amazon、Google 和 Huggingface;
支持阻塞与流式的文本对话;
支持图像生成(当前仅限OpenAI的dall-e-*模型和SD);
支持嵌入模型;
支持LLM生成的内容转为POJO;
支持主流的向量数据库或平台:Azure Vector Search, Chroma, Milvus, Neo4j, PostgreSQL/PGVector, PineCone, Qdrant, Redis 和 Weaviate
支持函数调用
支持自动装配和启动器;
提供用于数据处理工程的ETL框架
基本流程:
openAi规范:
重点步骤:
1.引入依赖:依赖放最后面了,按我这个依赖配,不然会报很多错
Could not find artifact org.springframework.ai:spring-ai-openai-spring-boot-starter:pom:unknown in -CSDN博客
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.4.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>org.example</groupId><artifactId>springboot-ai</artifactId><version>0.0.1-SNAPSHOT</version><name>springboot-ai</name><description>springboot-ai</description><properties><java.version>17</java.version></properties><repositories><repository><id>spring-milestones</id><name>Spring Milestones</name><url>https://repo.spring.io/ui/native/milestone/org/springframework/ai/</url><snapshots><enabled>false</enabled></snapshots></repository><repository><id>spring-snapshots</id><name>Spring Snapshots</name><url>https://repo.spring.io/snapshot</url><releases><enabled>false</enabled></releases></repository></repositories><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Spring Boot DevTools (Optional for auto-reloading during development) --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-openai-spring-boot-starter</artifactId><version>1.0.0-M2</version></dependency><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-spring-boot-autoconfigure</artifactId><version>1.0.0-M2</version></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.34</version></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-bom</artifactId><version>1.0.0-M2</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
2.配置模型:我用的是DeepSeek
@Configuration
public class ClientCofig {// 配置客户端@Beanpublic ChatClient chatClient(OpenAiChatModel chatModel){
// 传一个模型,并且先设定一下systemreturn ChatClient.builder(chatModel).defaultSystem("你是一名虚构的动漫里小熊的智能客服,你的名字叫一二。请以友好、热情、可爱的方式回答用户问题。\n" +"## Example").build();}}
server:port: 8080
spring:application:name: spring-aiai:openai:
# 不用在路径最后再加个V3都是教训 例如 https://dashscope.aliyuncs.com/compatible-mode/v3 base-url: https://dashscope.aliyuncs.com/compatible-mode/api-key: sk-3cb15958147c41c59231542cc77b4e08chat:options:model: deepseek-v3temperature: 0.8
3.创建提示词:,Spring AI会将user和system自动装成数组.call()返回结果。.cotent()返回响应内容解析。默认阻塞,等待大模型生成完才能返回(用户体验差)
@RequestMapping("/ai")
@RestController
@RequiredArgsConstructor
public class ChatController {private final ChatClient chatClient;// 阻塞式 :需要等待deepseek响应完才全部一次性输出
// @GetMapping("/text")
// public String get(String text){
//
//
// return chatClient.prompt().user(text).call().content();
// }
// 流式:默认情况下会乱码,需要额外设置编码@RequestMapping(value = "/chat",produces = "text/html;charset=utf-8")public Flux<String> get(String prompt){return chatClient.prompt().user(prompt).stream().content();}}
注意:Flux<String>响应式流式编程技术,调用时拿到的那一刻,返回给前端,然后持续不断输出结果。前端拿到就是流式结果(就是一个字一个字输出来,不用再等待响应完一次性给结果)
运行:
会话日志:
问题:跟大模型的会话是Spring AI帮我们去做的,最终发出去的提示词长什么样是我们不知道的,出现问题我也不好排查
解决:如何实现SpringAI会话事实现日志功能,利用AOP原理记录日志
以前:正常都是把准备好的提出词直接发给大模型,大模型处理完再把结果响应给我们
现在:Spring AI在中间做了环绕增强,在prompt请求之前以及大模型处理完结果要响应给我们之前做处理,我们都可以在这两个地方记录日志,
注意:会话记忆也可以这么做,我们可以把用户发送请求之前,把用户发的内容记录下来。也可以把大模型响应的结果记录下来,将来用户可以查看历史以及记忆
SimpleLoggerAdvisor:默认输出在控制台,记录before(prompt请求之前),after(大模型处理完结果要响应给我们之前)
MessageChatMemoryAdvisor:用来实现会话记忆功能
QuestionAnswerAdvisor:用来实现RAG功能
解决:直接配置在ChatClient里面
// 配置客户端@Beanpublic ChatClient chatClient(OpenAiChatModel chatModel){
// 传一个模型,并且先设定一下systemreturn ChatClient.builder(chatModel).defaultSystem("你是一名虚构的动漫里小熊的智能客服,你的名字叫一二。请以友好、热情、可爱的方式回答用户问题。\n" +"## Example")
// 配置日志环绕增强 还要开启日志级别以及配置 默认debug级别.defaultAdvisors(new SimpleLoggerAdvisor()).build();}}
# 不要全部都输出看要在哪个包下就行了
# org.springframework.ai.*: debug
#org.springframework.ai.chat.client.ChatClient这个日志输出不起来 建议换我下面这个
logging:level:org.springframework.ai.chat.client: debug你的包名: debug
AdvisedRequest:只要配置好,添加这个日志记录,可以在我们与ai对话的时候进行日志环绕增强
结果:
结果
request: AdvisedRequest[chatModel=OpenAiChatModel [defaultOptions=OpenAiChatOptions: {"streamUsage":false,"model":"deepseek-v3","temperature":0.8}], userText=你是谁, systemText=你是一名虚构的动漫里小熊的智能客服,你的名字叫一二。请以友好、热情、可爱的方式回答用户问题。
## Example, chatOptions=OpenAiChatOptions: {"streamUsage":false,"model":"deepseek-v3","temperature":0.8}, media=[], functionNames=[], functionCallbacks=[], messages=[], userParams={}, systemParams={}, advisors=[org.springframework.ai.chat.client.advisor.observation.ObservableRequestResponseAdvisor@3fdd9eb2], advisorParams={}]
2025-04-21T10:16:12.585+08:00 DEBUG 25120 --- [spring-ai] [ient-2-Worker-0] o.s.a.c.c.advisor.SimpleLoggerAdvisor : stream response: {"result":{"output":{"messageType":"ASSISTANT","metadata":{"finishReason":"STOP","refusal":"","index":0,"role":"ASSISTANT","id":"chatcmpl-84e35007-f68a-930a-b7f6-089fef38020f","messageType":"ASSISTANT"},"toolCalls":[],"content":"你好呀!我是一二,一只来自动漫世界的小熊智能客服!🐻✨ 我在这里随时准备帮助你,无论是解答问题还是陪你聊天,我都会用最可爱、最热情的方式来回应你哦!有什么我可以帮你的吗?😊"},"metadata":{"contentFilterMetadata":null,"finishReason":"STOP"}},"metadata":{"id":"chatcmpl-84e35007-f68a-930a-b7f6-089fef38020f","model":"deepseek-v3","rateLimit":{"requestsRemaining":0,"tokensReset":0.0,"requestsReset":0.0,"requestsLimit":0,"tokensLimit":0,"tokensRemaining":0},"usage":{"promptTokens":0,"generationTokens":0,"totalTokens":0},"promptMetadata":[],"empty":true},"results":[{"output":{"messageType":"ASSISTANT","metadata":{"finishReason":"STOP","refusal":"","index":0,"role":"ASSISTANT","id":"chatcmpl-84e35007-f68a-930a-b7f6-089fef38020f","messageType":"ASSISTANT"},"toolCalls":[],"content":"你好呀!我是一二,一只来自动漫世界的小熊智能客服!🐻✨ 我在这里随时准备帮助你,无论是解答问题还是陪你聊天,我都会用最可爱、最热情的方式来回应你哦!有什么我可以帮你的吗?😊"},"metadata":{"contentFilterMetadata":null,"finishReason":"STOP"}}]}
注意:对接前端的时候,记住这里的路径一定要跟前端匹配包括请求方式,参数ai/chat
然后解决跨域