记录AI学习之路Day15:浅记简单的SpringAI框架使用(advisor的使用)

发布时间:2026/9/24 14:30:36

记录AI学习之路Day15:浅记简单的SpringAI框架使用(advisor的使用) 浅记简单的SpringAI框架使用 — Advisor 的使用一、什么是 Advisor1.1 一句话概括Advisor 是 Spring AI 中的对话拦截器链——在每次与 LLM 交互的前后自动注入记忆、检索知识、改写问题、记录日志。没有 Advisor 用户消息 → LLM → 回复 有 Advisor 用户消息 → [记忆增强] → [RAG 检索] → [改写优化] → LLM → [日志记录] → 回复 每个 Advisor 是一个切面在 LLM 调用前后执行自己的逻辑。1.2 类比 Spring AOP// AOP方法执行前后加逻辑Around(execution(* com.example.*.*(..)))publicObjectaround(ProceedingJoinPointpjp){// 前置增强Objectresultpjp.proceed();// 方法执行// 后置增强returnresult;}// AdvisorLLM 调用前后加逻辑Advisoradvisor(request,chain)-{// 前置修改 prompt、注入上下文AdvisedRequestmodifieddoSomething(request);// 调用下一个 Advisor 或 LLMChatResponseresponsechain.next(modified);// 后置修改回复、记录日志returndoSomethingElse(response);};二、为什么需要 Advisor场景做一个智能客服要求 1. 记得客户之前说过什么记忆 2. 回答时检索产品手册RAG 3. 不能回答政治敏感问题安全护栏 4. 每次对话记录日志审计 传统写法 把所有逻辑揉在 Service 层 → 300 行代码改一处动全身 Advisor 写法 memoryAdvisor → ragAdvisor → guardAdvisor → logAdvisor → LLM 每个 Advisor 只做一件事自由组合插拔式增减三、快速开始3.1 依赖dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-advisors/artifactId/dependency!-- 聊天记忆 --dependencygroupIdorg.springframework.ai/groupIdartifactIdspring-ai-starter-chat-memory/artifactId/dependency3.2 第一个 Advisor 示例RestControllerpublicclassChatController{privatefinalChatClientchatClient;publicChatController(ChatClient.Builderbuilder){this.chatClientbuilder// 链式绑定 Advisor.defaultAdvisors(newSimpleLoggerAdvisor(),// ① 日志newChatMemoryAdvisor(memory),// ② 记忆newQuestionAnswerAdvisor(vectorStore)// ③ RAG).build();}PostMapping(/chat)publicStringchat(RequestBodyStringmessage){returnchatClient.prompt().user(message).call().content();}}## 《汽车保养》参数文档 保养周期总览以 5000 公里或 6 个月为小保养单位 | 里程/时间 | 保养类型 | 核心项目 | 预计费用区间 | |-----------|----------|----------|--------------| | 5000km/6个月 | 小保养 | 机油 机滤 | 300-600 元 | | 10000km/12个月 | 小保养 | 机油 机滤 空气滤 | 400-700 元 | | 20000km/24个月 | 中保养 | 汽油滤 空调滤 火花塞检查 | 800-1500 元 | | 40000km/48个月 | 大保养 | 变速箱油 刹车油 冷却液 | 2500-4500 元 | | 60000km/72个月 | 大保养 | 正时皮带/链条检查 减震器检查 | 3500-6000 元 |对话流程 用户: 6个月以内汽车多久保养一次 → ① SimpleLoggerAdvisor → 记录收到消息 6个月以内汽车多久保养一次 → ② ChatMemoryAdvisor → 注入历史对话上下文 → ③ QuestionAnswerAdvisor → 从向量库检索《汽车保养》参数文档 → LLM → 6个月以内汽车多久保养一次参考汽车保养{时间:6个月 保养类型小保养 核心项目机油 机滤 保养费用300-600 元} → ① SimpleLoggerAdvisor → 记录返回回复 xxx四、内置 Advisor 详解4.1 ChatMemoryAdvisor — 对话记忆作用把历史对话存起来每次请求自动注入上下文。ConfigurationpublicclassMemoryConfig{BeanpublicChatMemorychatMemory(){// 基于 JdbcTemplate 的持久化记忆存 MySQLreturnnewJdbcChatMemory(jdbcTemplate);}BeanpublicChatClientchatClient(ChatClient.Builderbuilder,ChatMemorymemory){returnbuilder.defaultAdvisors(newChatMemoryAdvisor(memory)).build();}}ChatMemory 三种实现实现存储位置适用场景InMemoryChatMemory内存Map开发测试JdbcChatMemoryMySQL生产持久化CassandraChatMemoryCassandra高并发-- JdbcChatMemory 需要的表MySqlCREATETABLEchat_memory(idVARCHAR(36)PRIMARYKEY,conversation_idVARCHAR(36)NOTNULL,contentTEXTNOTNULL,message_typeVARCHAR(20)NOTNULL,-- USER / ASSISTANTmetadata JSON,created_atTIMESTAMPDEFAULTNOW(),INDEXidx_conversation(conversation_id,created_at));控制记忆注入量newChatMemoryAdvisor(memory).setChatMemoryRetrieveSize(10)// 只取最近 10 条.setAdvisorPrefix(以下是之前的对话\n)// 注入时的前缀.setAdvisorSuffix(\n---\n);// 注入时的后缀4.2 QuestionAnswerAdvisor — RAG 知识检索作用从向量数据库检索相关知识拼到 prompt 里。ConfigurationpublicclassRagConfig{BeanpublicVectorStorevectorStore(EmbeddingModelembeddingModel){// 基于 PostgreSQL pgvectorreturnnewPgVectorStore(jdbcTemplate,embeddingModel,PgVectorStoreConfig.builder().tableName(knowledge_base).dimensions(1536).indexType(PgVectorStoreConfig.PgIndexType.HNSW).build());}BeanpublicQuestionAnswerAdvisorqaAdvisor(VectorStorevectorStore){returnQuestionAnswerAdvisor.builder(vectorStore).searchThreshold(0.7)// 相似度阈值低于 0.7 的不采用.topK(5)// 检索 Top 5.promptContextPrepend(\n参考以下知识回答\n)// 注入的知识前缀.build();}BeanpublicChatClientchatClient(ChatClient.Builderbuilder,ChatMemorymemory,QuestionAnswerAdvisorqaAdvisor){returnbuilder.defaultAdvisors(newChatMemoryAdvisor(memory),qaAdvisor).build();}}// 也可按需动态绑定不等同于全局 defaultAdvisorsPostMapping(/chat/rag)publicStringchatWithRag(RequestBodyStringmessage){returnchatClient.prompt().user(message).advisors(qaAdvisor)// 仅本次对话使用 RAG不持久.call().content();}4.3 SimpleLoggerAdvisor — 日志记录BeanpublicSimpleLoggerAdvisorloggerAdvisor(){returnnewSimpleLoggerAdvisor(// 请求日志r-log.info([AI请求] 用户: {},r.userText()),// 响应日志r-log.info([AI回复] {} (耗时{}ms, tokens:{}),r.getResult().getOutput().getContent(),r.getMetadata().get(time-to-first-token),r.getMetadata().get(usage)),0// Advisor 优先级数字越小先执行);}4.4 SafeGuardAdvisor — 安全护栏作用过滤敏感内容阻止非法提问。publicclassSafeGuardAdvisorimplementsCallAroundAdvisor{privatefinalListStringblockedKeywordsList.of(攻击,破解,翻墙,贩卖//等等等等);OverridepublicAdvisedResponsearoundCall(AdvisedRequestrequest,CallAroundAdvisorChainchain){StringuserMessagerequest.userText();// ① 前置检查for(Stringkeyword:blockedKeywords){if(userMessage.contains(keyword)){returnAdvisedResponse.builder().response(newChatResponse(List.of(newGeneration(抱歉我不能回答这个问题)))).build();// 阻止请求不调 LLM}}// ② 放行 → 下一个 Advisor → LLMAdvisedResponseresponsechain.nextAroundCall(request);// ③ 后置检查检查 LLM 回复是否包含敏感内容Stringreplyresponse.response().getResult().getOutput().getContent();for(Stringkeyword:blockedKeywords){if(reply.contains(keyword)){returnAdvisedResponse.builder().response(newChatResponse(List.of(newGeneration(抱歉回答被拦截)))).build();}}returnresponse;}OverridepublicStringgetName(){returnsafeGuard;}OverridepublicintgetOrder(){return0;}// 最先执行}4.5 ReReadingAdvisor — 提示词增强作用把用户的简短问题自动改写成更精确的 prompt。BeanpublicReReadingAdvisorreReadingAdvisor(){returnnewReReadingAdvisor();}// 用户输入6个月以内汽车多久保养一次// LLM 看到的请详细回答以下问题6个月以内汽车多久保养一次\n\n请确保回答准确、全面。4.6 ClearContextAdvisor — 清理上下文// 场景客户切换话题之前的对话历史没用了PostMapping(/chat/new-topic)publicStringnewTopic(RequestBodyStringmessage){returnchatClient.prompt().user(message).advisors(newClearContextAdvisor())// 清空记忆.call().content();}五、自定义 Advisor — 三步实现完整示例Token 计数 超长提示词自动截断ComponentpublicclassTokenLimitAdvisorimplementsCallAroundAdvisor{privatestaticfinalintMAX_TOKENS6000;OverridepublicAdvisedResponsearoundCall(AdvisedRequestrequest,CallAroundAdvisorChainchain){// ① 前置估算 token 数intestimatedTokensestimateTokens(request.userText()request.systemText());if(estimatedTokensMAX_TOKENS){log.warn(提示词过长 ({} tokens)自动截断,estimatedTokens);// 截断 userTextStringtruncatedtruncateByTokens(request.userText(),MAX_TOKENS);// 构建新请求AdvisedRequestnewRequestAdvisedRequest.from(request).userText(truncated).build();returnchain.nextAroundCall(newRequest);}// ② 放行AdvisedResponseresponsechain.nextAroundCall(request);// ③ 后置记录实际 token 用量intactualTokensestimateTokens(response.response().getResult().getOutput().getContent());log.info(实际消耗 token: {},actualTokens);returnresponse;}OverridepublicStringgetName(){returntokenLimit;}OverridepublicintgetOrder(){return1;}// 简单估算中文约 1.5 字 1 token英文约 0.75 词 1 tokenprivateintestimateTokens(Stringtext){return(int)(text.length()*0.5);}privateStringtruncateByTokens(Stringtext,intmaxTokens){intmaxCharsmaxTokens*3;returntext.length()maxChars?text.substring(0,maxChars)...:text;}}六、Advisor 链与执行顺序BeanpublicChatClientchatClient(ChatClient.Builderbuilder){returnbuilder.defaultAdvisors(newSafeGuardAdvisor(),// order0 最先拦截恶意请求newTokenLimitAdvisor(),// order1 控制长度newReReadingAdvisor(),// order10 优化 promptnewChatMemoryAdvisor(...),// order20 注入历史newQuestionAnswerAdvisor(...),// order30 RAG 检索newSimpleLoggerAdvisor(...)// order999 最后记录一切).build();}执行顺序用户 → LLM 方向: 用户 → SafeGuard → TokenLimit → ReReading → Memory → RAG → Logger → LLM 返回顺序LLM → 用户方向 LLM → Logger → RAG → Memory → ReReading → TokenLimit → SafeGuard → 用户 就像一个过滤器链正向执行前置逻辑反向执行后置逻辑。七、动态 vs 默认 Advisor// 默认 Advisor对所有请求生效chatClientbuilder.defaultAdvisors(memoryAdvisor,loggerAdvisor).build();// 动态 Advisor按需添加覆盖默认PostMapping(/chat/deep-search)publicStringdeepSearch(RequestBodyStringmessage){returnchatClient.prompt().user(message).advisors(qaAdvisor,deepSearchAdvisor)// 本次对话额外加两个.call().content();}// 动态 Advisor按需移除默认PostMapping(/chat/anonymous)publicStringanonymous(RequestBodyStringmessage){returnchatClient.prompt().user(message).advisors(newClearContextAdvisor())// 清除记忆.call().content();}八、Spring AI MCP 互通Advisor 和 MCP Server 可以互补使用用户请求 │ ▼ Advisor 链会话管理 │ ├→ ChatMemoryAdvisor加载历史记忆 ├→ QuestionAnswerAdvisorRAG 检索 ├→ SafeGuardAdvisor安全护栏 │ ▼ MCPTOOLS/SKILL Server外部工具调用 │ ├→ search_flights查航班 ├→ get_weather查天气 ├→ query_database查数据库 │ ▼ LLM 回复九、完整项目结构src/main/java/com/liangzai/chat/ ├── config/ │ ├── ChatClientConfig.java → ChatClient Advisor 链配置 │ ├── MemoryConfig.java → ChatMemory 持久化配置 │ └── RagConfig.java → 向量库 RAG Advisor ├── advisor/ │ ├── SafeGuardAdvisor.java → 安全护栏 │ ├── TokenLimitAdvisor.java → Token 限制 │ └── AuditAdvisor.java → 审计日志落库 ├── controller/ │ └── ChatController.java → REST API └── Application.java十、总结Advisor 的本质 面向 AI 对话的 AOP 没有 Advisor每次对话都要手动拼记忆、查知识库、过滤敏感词 有了 Advisor全部自动化链式编排插拔式增减 核心能力 ChatMemoryAdvisor → 记住历史 QuestionAnswerAdvisor → RAG 检索 SafeGuardAdvisor → 安全护栏 自定义 Advisor → 任何你需要的增强逻辑 选择优先级 内置 自定义 — 永远先看 Spring AI 有没有现成的 默认 动态 — 全局相同的放 defaultAdvisors按需加载的放 .advisors()
延伸阅读

更多相关文章

2026/9/20 17:37:35

本地化LLM部署方案:Ollama、OpenLLM、LocalAI与Dify实战指南

1. 为什么需要本地化LLM部署方案?在ChatGPT等云端大模型服务普及的今天,本地部署LLM(Large Language Model)正在成为开发者社区的新趋势。上周我帮一家医疗初创公司部署本地问答系统时,他们的CTO提出了三个核心诉求&am…

2026/9/22 7:08:00

Chrome扩展开发:Browser Action API详解与实战

1. 谷歌插件开发概述 Browser Action API是Chrome扩展开发中最核心的组件之一,它允许开发者通过浏览器工具栏图标与用户进行交互。这个API在Manifest V3中取代了传统的browserAction,成为现代Chrome扩展开发的标准方式。 我在实际开发中发现&#xff0c…

2026/9/20 17:37:52

向量数据库原理与实战:从语义搜索到工业级选型

1. 什么是向量数据库?——从“找猫图”开始的真实需求你有没有试过在手机相册里翻半小时,就为了找到去年夏天那张“阳光洒在猫耳朵上”的照片?关键词搜“猫”,出来几百张;搜“阳光”,又混进一堆风景照&…

2026/9/24 14:26:21

USB3.0端到端链路设计:从SSTX电容看物理层信号完整性

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 14:26:21

信创机房动环监控技术原理解读与应用实践分析

信创机房动环监控的应用背景与发展现状 随着信息技术的迅速发展,信创机房动环监控逐渐成为数据中心和机房管理中的重要组成部分。动态环境监控技术通过实时跟踪机房内温湿度、空气流通及电力数据等,为管理者提供了更为精准的信息。这一技术的引入不光提高…

2026/9/24 14:26:21

Web端云渲染低延迟实战:NVENC+WebRTC链路优化与画质调优

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 14:26:21

KiCad 10深度评测:新特性、安装配置与高效布线实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/23 12:07:00

GAMP 5 基于风险的计算机化系统验证:软件分类与审计追踪实践

简介:《A Risk-Based Approach to Compliant GxP Computerized Systems》即业内熟知的GAMP 5指南,面向制药企业质量与IT合规人员、验证工程师及计算机化系统管理者,用于解决GxP法规环境下系统合规性难以科学落地的问题。文档以风险管理为主线…

2026/9/23 12:06:55

安全托管MSSP实战:从静态防御到人机协同的攻防运营与应急响应

简介:这份PPT围绕互联网业务安全托管服务展开,面向企业安全负责人、IT运维人员及关注MSSP/MSS选型的读者,重点回应传统安全过度依赖人工、碎片化静态防御难以对抗产业化攻击等痛点。资源共1个pptx文件,包体约30.63MB,以…

2026/9/24 0:00:21

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:21

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:21

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/22 16:34:32

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/22 20:01:30

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/22 13:25:41

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码