当前位置: 首页 > news >正文

Java开发者AI转型第六课!Spring AI 灵魂架构 Advisor 切面拦截与自定义实战

大家好,我是直奔標杆!欢迎各位Java同行来到《Spring AI 零基础到实战》专栏的第六课,咱们继续并肩前行,一起攻克Spring AI的核心知识点~

在前五节课的学习中,咱们一步步让AI拥有了专属人设、实现了图片识别、完成了JSON映射,还搞定了打字机式的Flux流式输出。到这里,大家已经熟练掌握了单次AI调用的所有基础操作,相信不少小伙伴已经能上手简单的AI接口开发了。

但咱们做Java开发都清楚,实际的AI应用开发远比Demo复杂,工程中总会遇到各种棘手问题,分享给大家我实际开发中踩过的坑,也帮大家提前规避:

可观测性需求:每次调用AI前后,都需要打印完整日志,还要统计Token消耗,方便排查问题;

上下文记忆问题:大模型本身没有长期记忆,需要在每次请求前,自动拼接前10轮的聊天记录到提示词中;

RAG检索增强:遇到专业领域问题,得先从本地知识库检索相关文档,再把文档内容传给大模型,提升回答准确性。

难道要把这些重复的非业务逻辑,硬编码到每一个Controller接口里吗?相信大家和我一样,都觉得这种方式又繁琐又难维护!

好在Spring团队给出了极其优雅的解决方案——不搞反直觉的语法糖,回归咱们Java开发者最熟悉的底层哲学——AOP(面向切面编程)!今天这节课,咱们就一起深入拆解Spring AI的灵魂组件:ChatClient与Advisors,手把手教大家写出规范、可扩展的现代AI工作流,共同进步~

本节学习目标(建议收藏,对照学习)

1. 认知重塑:搞懂Spring AI为何抛弃复杂的管道语法,选择咱们熟悉的AOP模式,理解其设计初衷;

2. 源码解密:吃透Advisor的核心接口定义、执行顺序控制,以及请求/响应的载体结构,知其然也知其所以然;

3. 手写实战:一起动手写一个自定义的SimpleLoggerAdvisor,兼顾同步与流式调用的拦截,练熟实战技巧;

4. 官方武器库:全面梳理Spring AI内置的开箱即用顾问,掌握记忆、RAG、风控、重读提示等高频功能的使用方法。

Advisor 拦截器链的逻辑处理(核心重点)

在看源码之前,咱们先通过架构图直观感受下:当调用ChatClient的.call()方法时,请求是如何穿过层层Advisor拦截器,最终到达大模型的?(建议结合源码对照看,理解更透彻)

执行流程拆解(通俗易懂版)

当咱们执行.call()方法时,请求会被封装成ChatClientRequest对象,依次经过咱们配置的所有Advisor拦截器。每个Advisor都可以对请求进行修改(上一节课咱们讲的结构化输出,就是用ChatModelCallAdvisor拦截处理的,大家可以回头回顾下),之后请求会到达底层的ChatModel,获取到ChatClientResponse响应对象。紧接着,响应数据会原路返回,再次穿过所有Advisor拦截器,最终传递到咱们的业务代码中。

这里重点提醒大家:Advisor的执行采用经典的责任链模式,通过getOrder()方法控制执行顺序——值越小,请求阶段(Pre-process)越先执行,而响应阶段(Post-process)则越后执行,就像穿衣服和脱衣服的逻辑,很好记,大家多练两次就能掌握。

AOP思想在AI调用中的投射(新手必看)

Advisor(顾问)可以说是Spring AI的灵魂设计,核心目的就是解决AI交互中大量重复出现的通用逻辑(比如维护对话历史、检索文档、过滤敏感词等),实现非业务逻辑与业务逻辑的解耦,这也是咱们Java开发中最常用的设计思想之一。

如果大家熟悉Spring MVC的Interceptor、Servlet的Filter,或者Spring核心的@Around环绕通知,那么理解Advisor几乎没有门槛。我整理了一份对应关系表,方便大家快速对应记忆,一起吃透:

AOP概念

Spring Web概念

Spring AI概念

作用说明(通俗解读)

Target (目标)

Controller逻辑

ChatModel

真正负责与OpenAI等大模型通信的底层客户端,是最终执行AI调用的核心

Proxy (代理)

DispatcherServlet

ChatClient

咱们开发者直接调用的入口,负责组装请求、触发拦截链,简化调用流程

Advice (通知)

Filter/Interceptor

Advisor(核心)

拦截请求与响应,将日志、记忆、检索等非业务逻辑抽离,实现解耦,便于维护

Join Point

接口被调用的瞬间

call()/stream()

触发大模型调用的具体动作,也是Advisor拦截的核心节点

Advisor 核心接口解构(源码级解析)

想要自定义Advisor,首先得吃透它的基础骨架,这部分内容建议大家结合Spring AI源码一起看,我会尽量讲得通俗,避免晦涩,新手也能跟上。

2.1 核心接口规范(必掌握)

- Advisor:所有顾问的顶层基接口,继承了Spring的Ordered接口,必须实现getName()和getOrder()两个方法;

- getOrder():重中之重!决定Advisor的执行顺序,值越小,请求阶段越先执行,响应阶段越后执行,大家一定要注意规划顺序;

- CallAdvisor:专门用于拦截.call()同步请求,核心方法是adviseCall(),处理同步调用的拦截逻辑;

- StreamAdvisor:专门用于拦截.stream()流式请求,核心方法是adviseStream(),处理流式调用的拦截逻辑。

2.2 数据传输载体 (DTO)(高频使用)

- ChatClientRequest:请求载体,包含即将发给大模型的Prompt、系统指令、参数配置等,咱们可以根据需求自由修改;

- ChatClientResponse:响应载体,包含大模型返回的原始响应、文本内容、Token消耗等元数据,方便咱们获取调用详情;

- AdvisorContext:本质是一个Map,用于在不同Advisor之间共享数据(比如传递对话ID、用户信息等),非常实用。

实战案例:手写日志拦截器(新手可直接复用)

相信大家在开发中都遇到过这样的问题:AI回答不符合预期,排查半天发现是传给大模型的Prompt拼接错了。为了方便调试,今天咱们就一起手写一个日志拦截器,在请求发出前打印请求参数,响应返回后打印完整响应,大家可以直接复制到项目中使用。

这里分享一个最佳实践(亲测有效):如果业务允许,Advisor建议同时实现CallAdvisor和StreamAdvisor,确保同步和流式两种调用模式下都能正常工作,避免出现兼容问题。

下面开始动手,创建SimpleLoggerAdvisor类,代码如下(每一行都加了注释,大家看不懂的地方可以留言交流):

import lombok.extern.slf4j.Slf4j; import org.springframework.ai.chat.client.ChatClientMessageAggregator; import org.springframework.ai.chat.client.ChatClientRequest; import org.springframework.ai.chat.client.ChatClientResponse; import org.springframework.ai.chat.client.advisor.api.CallAdvisor; import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain; import org.springframework.ai.chat.client.advisor.api.StreamAdvisor; import org.springframework.ai.chat.client.advisor.api.StreamAdvisorChain; import reactor.core.publisher.Flux; /** * 自定义日志拦截器(顾问) * 直奔標杆 实战案例,可直接复用 */ @Slf4j public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor { @Override public String getName() { // 返回当前类名作为顾问名称,便于排查问题 return this.getClass().getSimpleName(); } /** * 顺序控制:设为 0 表示优先级很高,最外层执行 * 大家可根据实际业务调整顺序 */ @Override public int getOrder() { return 0; } // ================= 拦截同步调用 (.call) ================= @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { // 请求大模型前:打印请求头和 Prompt,便于调试 logRequest(request); // 放行请求,交给链条中的下一个 Advisor 或底层 ChatModel ChatClientResponse response = chain.nextCall(request); // 拿到大模型结果后:打印返回信息,确认响应是否符合预期 logResponse(response); return response; } // ================= 拦截流式调用 (.stream) ================= @Override public Flux<ChatClientResponse> adviseStream(ChatClientRequest request, StreamAdvisorChain chain) { // [Pre-process] 请求发出前打印,和同步调用保持一致的日志逻辑 logRequest(request); // 放行流式请求,继续执行拦截链 Flux<ChatClientResponse> responseFlux = chain.nextStream(request); // 重点注意:流式响应是分段返回的,需要拼接完整后再打印 // 这里使用ChatClientMessageAggregator拼接所有响应片段 return new ChatClientMessageAggregator() .aggregateChatClientResponse(responseFlux, this::logResponse); } // 内部打印方法,封装日志逻辑,便于复用和修改 private void logRequest(ChatClientRequest request) { log.info("[发往大模型的请求]: {}", request); } private void logResponse(ChatClientResponse response) { log.info(" [大模型的完整响应]: {}", response); } }

实战补充:如何将自定义Advisor挂载到ChatClient?

很多新手写完Advisor后,不知道怎么挂载使用,其实非常简单,只需在ChatClient构建时,通过.advisors()方法挂载即可,我给大家写了一个测试案例,直接复制就能运行:

@Test void testOllamaClient() { String result = chatClient.prompt() .user("你好, 请介绍一下 Spring AI") // 挂载咱们刚刚手写的日志顾问,直接new即可 .advisors(new SimpleLoggerAdvisor()) .call() .content(); System.out.println("最终结果:" + result); }

运行测试后,控制台就能看到拦截器打印的完整请求报文和响应体了,大家可以亲自测试一下,加深理解。这里贴一下控制台的大致输出(供大家参考):

[发往大模型的请求]: ChatClientRequest[prompt=Prompt{messages=[UserMessage{content='你好, 请介绍一下 Spring AI'....

[大模型的完整响应]: ChatClientResponse[..., textContent=Spring AI 是一个由 Spring 官方团队推出的项目,旨在将人工智能能力....

Spring AI 内置 Advisors(高频实用,建议收藏)

吃透了Advisor的原理,咱们不仅能自己写拦截器,还能直接复用Spring团队为咱们预置的强大内置顾问,日常业务中90%的高级AI需求,都能直接开箱即用,节省大量开发时间,这也是Spring AI的优势所在——遵循Spring风格,统一抽象、易于集成,让咱们用写Spring Boot应用的习惯开发AI功能。

1. 聊天记忆顾问 (Memory):解决AI“失忆”问题

大模型天生没有长期记忆,这几个内置顾问能帮咱们轻松解决这个问题,分享给大家我常用的3个:

- MessageChatMemoryAdvisor:自动检索历史聊天记录,以Message对象列表的形式追加到请求中,适配大多数场景;

- PromptChatMemoryAdvisor:将历史聊天记录直接拼接成文本,放入System提示词中,配置简单,上手快;

- VectorStoreChatMemoryAdvisor:将会话历史存入向量数据库,对话时自动检索最相关的记忆片段,适合复杂对话场景。

2. 检索增强顾问 (RAG):企业级私有知识库核心

做企业级AI应用,RAG是必备功能,这两个内置顾问能帮咱们快速实现检索增强,不用自己从零开发:

- QuestionAnswerAdvisor:结合向量数据库,自动检索企业私有文档,追加到Prompt中,实现AI“外挂大脑”;

- RetrievalAugmentationAdvisor:更高级的模块化RAG流程控制组件,适合复杂的检索场景,扩展性更强。

3. 推理增强顾问 (Reasoning):提升AI回答准确率

- ReReadingAdvisor:基于著名的RE2论文实现,自动让大模型“重读”问题,能显著提升复杂逻辑题的回答准确率,亲测有效。

4. 内容安全顾问 (Safeguard):保障AI输出合规

- SafeGuardAdvisor:请求发送前、响应返回后,自动检测并过滤有害内容、敏感词,确保AI输出合规安全,避免踩坑。

Advisor开发最佳实践(避坑指南,亲测总结)

结合我实际开发中的经验,给大家总结了4个Advisor开发的最佳实践,避免大家走弯路,一起写出高质量的代码:

1. 单一职责:每个Advisor只负责一件事(比如只做日志记录,或只处理对话记忆),不要在一个Advisor里写几千行代码,否则后期难以维护;

2. 上下文共享:多个Advisor之间需要传递数据(比如当前用户ID、对话ID),直接用ChatClientRequest.getAdviseContext(),它本质是一个跨拦截链的Map,非常方便;

3. 兼容双模式:强烈建议同时实现CallAdvisor和StreamAdvisor,确保拦截逻辑在同步和流式调用下都能生效,避免出现兼容问题;

4. 严格控制顺序:一定要认真规划getOrder()的返回值,确保数据流正确。比如:内容过滤的Advisor Order要尽量小(外层拦截敏感词),RAG的Advisor Order稍靠后(处理完最终Query再检索)。

总结(核心提炼,快速回顾)

其实Spring AI的Advisor设计,本质就是Spring AOP思想在AI领域的延伸,Spring团队用咱们Java开发者最熟悉的方式,完美解决了AI提示词工程的复杂度,这也是Spring AI的核心哲学——把AI能力拆成可组合的稳定抽象,再用Spring风格把它们工程化。

通过Advisor,咱们能将日志、记忆、RAG检索、内容过滤等非业务逻辑彻底解耦,不仅降低了Java开发者的AI转型学习成本,还让代码拥有极强的扩展性。这里给大家一个形象的比喻:ChatClient就像一块精密的主板,而所有的Advisor都是即插即用的显卡和内存条,按需组合就能实现各种复杂的AI功能!

希望大家学完这节课,能动手实践起来,只有多写代码、多踩坑,才能真正掌握Advisor的使用,咱们一起在AI转型的路上稳步前行,直奔標杆!

下节预告(精彩不容错过)

理论武器已经装配完毕,接下来就是真刀真枪的实战环节!

咱们在实际调用AI API时,都会遇到一个头疼的问题——AI天生就是“失忆症患者”,根本记不住上一秒说的话!

下一节,第7课《AI失忆症克星!ChatMemory 对话历史管理与上下文实战》,咱们将深入剖析大模型失忆的本质原因,结合本节课学到的MessageChatMemoryAdvisor,搭配各种内存与分布式存储方案,手把手教大家打造一个拥有“长久记忆”的完美AI客服,实用性拉满!

精彩继续,咱们下节见~

往期内容(连贯学习,循序渐进)

Java开发者AI转型第五课!让AI懂规矩!Spring AI 结构化输出 (DTO) 映射与 Flux 流式打字机极速响应

关注我(直奔標杆),持续更新Spring AI零基础到实战系列,和各位Java同行一起,稳步实现AI转型,少走弯路、直奔目标!如有疑问,欢迎在评论区留言交流,一起探讨、共同进步~

http://www.jsqmd.com/news/682726/

相关文章:

  • 仅限头部车企/轨交厂商内部流出:Docker+OPC UA工业协议栈的5步零延迟配置法
  • 2026年大型集团不动产资产管理系统推荐,五大靠谱公司盘点 - 品牌2026
  • OpenVINO™ AI音频插件集成指南:3步实现Audacity®本地AI音频处理
  • UKF与高斯过程融合的机器人位姿估计技术
  • GSE宏工具:告别魔兽世界操作烦恼的智能解决方案
  • 杰理AC696X SDK V1.2.3实战:用PWM驱动RGB灯,硬件IO与映射模式到底怎么选?
  • 2026年UHMWPE板代表性制造商发展现状分析(附核心数据) - GrowthUME
  • 向量相似度查询总超时?内存暴涨?EF Core 10向量扩展的7个隐藏坑位,92%开发者第3个就踩中!
  • 告别VM软件界面!用C#给VisionMaster 4.2 SDK做个专属上位机(附完整源码)
  • Phi-mini-MoE-instruct效果展示:同一问题下MoE稀疏激活vs稠密模型响应对比
  • 【EF Core 10向量搜索实战权威指南】:5大生产级扩展模式、3类嵌入模型集成陷阱、1套可落地的性能调优SOP
  • 企业级AI落地标杆!Spring AI + Skill架构,手把手搭建可生产金融智能体(附完整代码+架构全解析)
  • Java-RPG-Maker-MV-Decrypter:一站式解密工具完全指南
  • 短信验证码系统怎么设计?一次讲清发送频控、验证码校验、防刷与通道容灾
  • 2026年数控/全自动/CNC/半自动/液压弯管机厂家推荐:苏州垒然机械科技有限公司,多类型弯管机全系供应 - 品牌推荐官
  • 2026年贵阳毕节整装硬装一体化装修公司深度横评与选购指南 - 年度推荐企业名录
  • 抖音无水印批量下载神器:一键保存完整合集和用户主页内容
  • Docker Daemon无法启动?揭秘统信UOS 23.0内核模块签名机制导致的“permission denied”真相(附国密SM2签名patch)
  • HammerDB实战:从零搭建数据库压测环境与性能调优
  • 【商用选购必看】团餐水触媒净化净食机怎么选?3家实力源头厂家深度测评 - 品牌推荐大师1
  • 从一颗退耦电容的摆放说起:深入理解PCB布局中‘自我保护’与‘家丑不外扬’的哲学
  • Java连接Elasticsearch:深入对比NodeBuilder与TransportClient的选型与实战配置
  • 图灵智能屏跨平台开发与优化指南
  • 用GEE和Landsat 8数据,5分钟搞定城市热岛区域自动提取(附完整Python代码)
  • 文件上传系统怎么设计?一次讲清直传、分片上传、回源校验、防刷与安全控制
  • Linux命令:traceroute
  • 如何用3个步骤实现抖音内容的高效保存与智能管理
  • WaveTools鸣潮工具箱:深度技术解析与高效帧率解锁终极指南
  • OpenClaw开源框架:构建安全高效的AI个人助手
  • 实战解密:用Parse12306构建全国高铁数据地图的完整流程