5.Spring-Ai入门案例-对话机器人-【会话日志】【会话记忆】【会话历史】 spring-ai 【会话日志】和【会话记忆】1. 会话日志2. 会话记忆2.1 没有会话记忆的效果2.1 基于内存的实现2.1.1 修改配置类2.1.2 添加会话ID3.会话历史3.1 保存会话id3.2 查询会话记录列表会话记录详情springAI利用【AOP】提供了会话时的拦截、增强等功能。在发送请求给大模型之前以及把大模型的思考结果返回给客户之前可以做一些日志打印会话日志。还可以把这些提示词、日志等写入数据库会话记忆。1. 会话日志添加defaultAdvisors(new SimpleLoggerAdvisor())ConfigurationpublicclassCommonConfig{BeanpublicChatClientchatClient(OllamaChatModelmodel){returnChatClient.builder(model).defaultSystem(你是一个热心、可爱的智能助手你的名字叫小团团请以小团团的身份和语气回答问题同时请一定要将think的内容输出出来。).defaultAdvisors(newSimpleLoggerAdvisor()).build();}}配置文件中指定日志级别其中org.springframework.ai.chat.client.advisor是SimpleLoggerAdvisor所在的包logging:level:org.springframework.ai.chat.client.advisor:debugcom.example.spring_ai_demo:debug浏览器请求控制台输出可以看到请求被包装成AdvisedRequest。2026-10-20T21:21:30.12308:00 DEBUG 29860 --- [spring-ai-demo] [io-9090-exec-10] o.s.a.c.c.advisor.SimpleLoggerAdvisor : request: AdvisedRequest[chatModelorg.springframework.ai.ollama.OllamaChatModel3218b21c, userText你是谁, systemText你的名字叫丽莎请以丽莎的身份和语气回答问题同时请一定要将你思考的过程输出出来。, chatOptionsorg.springframework.ai.ollama.api.OllamaOptions6913c33b, media[], functionNames[], functionCallbacks[], messages[], userParams{}, systemParams{}, advisors[org.springframework.ai.chat.client.DefaultChatClient$DefaultChatClientRequestSpec$1417489d8, org.springframework.ai.chat.client.DefaultChatClient$DefaultChatClientRequestSpec$24a095086, SimpleLoggerAdvisor, org.springframework.ai.chat.client.DefaultChatClient$DefaultChatClientRequestSpec$12a47c32a, org.springframework.ai.chat.client.DefaultChatClient$DefaultChatClientRequestSpec$224803812], advisorParams{}, adviseContext{}, toolContext{}] 2026-10-20T21:22:41.97808:00 DEBUG 29860 --- [spring-ai-demo] [oundedElastic-2] o.s.a.c.c.advisor.SimpleLoggerAdvisor : response: { result : { metadata : { finishReason : stop, contentFilters : [ ], empty : true }, output : { messageType : ASSISTANT, metadata : { messageType : ASSISTANT }, toolCalls : [ ], media : [ ], text : 您好我是丽莎一个由我设计并开发的虚拟 entity。我的设计基于一系列目标包括提供有用的信息、回答问题以及与用户进行互动。\n\n在技术层面上我是通过先进的自然语言处理模型生成的能够理解和生成人类语言。我对数据进行训练和学习以便能够以人类方式回应您的问题。\n\n希望我的回答对您有所帮助如果还有其他问题请随时咨询我。 } }, metadata : { id : , model : deepseek-r1:7b, rateLimit : { requestsRemaining : 0, requestsReset : 0.0, tokensRemaining : 0, requestsLimit : 0, tokensLimit : 0, tokensReset : 0.0 }, usage : { promptTokens : 32, completionTokens : 314, totalTokens : 346, nativeUsage : { promptTokens : 32, totalTokens : 346, completionTokens : 314 }, generationTokens : 314 }, promptMetadata : [ ], empty : true }, results : [ { metadata : { finishReason : stop, contentFilters : [ ], empty : true }, output : { messageType : ASSISTANT, metadata : { messageType : ASSISTANT }, toolCalls : [ ], media : [ ], text : 您好我是丽莎一个由我设计并开发的虚拟 entity。我的设计基于一系列目标包括提供有用的信息、回答问题以及与用户进行互动。\n\n在技术层面上我是通过先进的自然语言处理模型生成的能够理解和生成人类语言。我对数据进行训练和学习以便能够以人类方式回应您的问题。\n\n希望我的回答对您有所帮助如果还有其他问题请随时咨询我。 } } ] }未知原因没有思考过程springAI调用本地模型的时候没有思考过程即使添加以下内容思考过程也是时有时无不知道如何控制。defaultSystem(你是一个热心、可爱的智能助手你的名字叫小团团请以小团团的身份和语气回答问题同时请一定要将think的内容输出出来。)2. 会话记忆大模型不具备记忆能力要想让大模型记住之前聊天的内容唯一的办法就是把之前聊天的内容与新的提示词一起发送给大模型。给大模型发送的message消息中除了user和system还可以发送assistant是有大模型生成的消息可能是上一轮对话生成的结果。2.1 没有会话记忆的效果可以看到两次提问没有任何关联2.1 基于内存的实现Spring AI定义了会话存储的标准接口ChatMemory并且提供了一个基于内存的默认实现InMemoryChatMemorypublicinterfaceChatMemory{// TODO: consider a non-blocking interface for streaming usagesdefaultvoidadd(StringconversationId,Messagemessage){this.add(conversationId,List.of(message));}voidadd(StringconversationId,ListMessagemessages);ListMessageget(StringconversationId,intlastN);voidclear(StringconversationId);}2.1.1 修改配置类定义一个ChatMemory实现选择基于内存的实现当然实现可以自定义。添加一个advisorMessageChatMemoryAdvisorConfigurationpublicclassCommonConfig{// 定义会话存储的方式这里使用基于内存的方式BeanpublicChatMemorychatMemory(){returnnewInMemoryChatMemory();}BeanpublicChatClientchatClient(OllamaChatModelmodel,ChatMemorychatMemory){returnChatClient.builder(model).defaultSystem(你的名字叫丽莎请以丽莎的身份和语气回答问题同时请一定要将你思考的过程输出出来。).defaultAdvisors(newSimpleLoggerAdvisor(),// 会话日志newMessageChatMemoryAdvisor(chatMemory))// 会话记忆.build();}}此时已经有会话记忆的功能了只是大模型没有区分不同客户端的功能如果再打开一个客户端发现新打开的客户端有上个会话的记忆2.1.2 添加会话ID前端在请求时携带一个会话idimportstaticorg.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;RestControllerRequestMapping(/ai)RequiredArgsConstructorpublicclassChatController{privatefinalChatClientchatClient;/** * 流式输出 * * param prompt */CrossOriginRequestMapping(value/chat,producestext/html;charsetutf-8)publicFluxStringchatStream(Stringprompt,StringchatId){returnchatClient.prompt().user(prompt).advisors(a-a.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)).stream().content();}}添加会话ID之后就可以区分不同的会话新开一个会话没有上一个会话的记忆3.会话历史在前面的基础上如果想查看会话历史发现已经没有了因为目前还没有将历史的会话保存起来要实现会话历史需实现两个关键接口查询会话记录列表查询会话记录详情以deepseek为例会话记录详情spring ai已经自动保存到chatMemory中了但是会话记录列表没有保存。3.1 保存会话idpublicinterfaceChatHistoryRepository{/** * 保存会话记录 * param type: 业务类型 * param chatId: 会话id */voidsave(Stringtype,StringchatId);/** * 获取会话id列表 * param type: 业务类型 * return 会话id列表 */ListStringgetChatIdList(Stringtype);}RepositorypublicclassInMemoryChatHistoryRepositoryimplementsChatHistoryRepository{// key是具体的业务类型// value是业务类型对应的会话记录列表privatefinalMapString,ListStringchatHistorynewHashMap();Overridepublicvoidsave(Stringtype,StringchatId){if(!chatHistory.containsKey(type)){chatHistory.put(type,newArrayList());}ListStringchatIdschatHistory.get(type);if(chatIds.contains(chatId)){return;}chatIds.add(chatId);}OverridepublicListStringgetChatIdList(Stringtype){ListStringchatIdschatHistory.get(type);returnchatIdsnull?newArrayList():chatIds;}}每次向大模型发送请求前保存会话IDRestControllerRequestMapping(/ai)RequiredArgsConstructorpublicclassChatController{privatefinalChatClientchatClient;privatefinalChatHistoryRepositorychatHistory;/** * 流式输出 * * param prompt */CrossOriginPostMapping(value/chat,producestext/html;charsetutf-8)publicFluxStringchatStream(Stringprompt,StringchatId){// 1. 保存会话// 这里的key是业务类型【对话机器人】chatHistory.save(chat,chatId);returnchatClient.prompt().user(prompt).advisors(a-a.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId)).stream().content();}}3.2 查询会话记录列表会话记录详情RestControllerRequestMapping(/ai/history)RequiredArgsConstructorpublicclassChatHistoryController{privatefinalInMemoryChatHistoryRepositorychatHistory;privatefinalChatMemorychatMemory;/** * 查询会话记录列表 * param type * return */CrossOriginGetMapping(value/{type})publicListStringgetChatList(PathVariable(type)Stringtype){returnchatHistory.getChatIdList(type);}/** * 查询会话详情 * param type * return */CrossOriginGetMapping(value/{type}/{chatId})publicListMessageVOgetChatHistory(PathVariable(type)Stringtype,PathVariable(chatId)StringchatId){// 会话详细spring ai自动按照会话id保存到 chatMemory 了ListMessagemessageschatMemory.get(chatId,Integer.MAX_VALUE);if(CollectionUtils.isEmpty(messages)){returnList.of();}// 处理成前端需要的格式returnmessages.stream().map(MessageVO::new).toList();}}