2026/9/30 10:45:58

Spring AI Alibaba实战:RAG、NL2SQL与Agent落地方案全解析

Spring AI Alibaba实战:RAG、NL2SQL与Agent落地方案全解析 1. 开场第四篇了聊聊Spring AI Alibaba真正的落地场景先说一个我自己的感受。前面三篇我们把Spring AI Alibaba的模型接入、Prompt模板、输出解析这些基础能力过了一遍说实话光靠这些能力你只能做一个能聊天的Demo。真正让这个框架有价值的是当你把大模型跟企业的数据、业务逻辑、系统操作打通之后。RAG、NL2SQL、Agent这三件事恰恰是Spring AI Alibaba在底层帮你做了大量封装的地方。这篇我打算直接从实战角度出发把三个核心场景完整拆开讲怎么把企业内部文档喂给大模型做检索增强RAG怎么让大模型直接对数据库写查询语句NL2SQL以及怎么把一个有工具调用能力的Agent在Spring Boot项目里跑起来。这篇文章适合谁你已经掌握Spring AI Alibaba的基本用法能调通一个聊天接口但想更进一步把它接到真实业务系统里的开发者。我会把每一步的配置、代码、常见坑都写清楚。另外文中的方案都基于Spring AI Alibaba 1.0.0-M3.1版本如果你用的是更新版本留意一下API的兼容性。2. 先理清Spring AI Alibaba的组件架构2.1 它不是Spring Cloud Alibaba别搞混刚接触Spring AI Alibaba的同事经常问一个问题它跟Spring Cloud Alibaba是一回事吗这个问题很关键因为它决定了你后续排错的方向。Spring Cloud Alibaba负责的是微服务治理包括服务注册发现、配置中心、限流熔断这套东西。而Spring AI Alibaba是阿里云通义千问团队主导的一个AI应用开发框架它的目标是让Java开发者能快速把大模型能力集成到业务系统里同时提供一套统一抽象避免被某个模型厂商绑定死。换句话说Spring Cloud Alibaba解决的是微服务怎么互联的问题Spring AI Alibaba解决的是应用怎么用上大模型的问题。两者可以共存但职责完全分开。我见过有人以为引入Spring AI Alibaba就必须同时引入Nacos其实没有这回事除非你自己项目里有微服务需求。2.2 核心模块一览Spring AI Alibaba的项目结构可以从GitHub的仓库里看到它包含几个关键模块我按实际使用频率排个序spring-ai-alibaba-core核心抽象层。定义了大模型的统一接口包括ChatModel、StreamingChatModel、EmbeddingModel等。spring-ai-alibaba-dashscope通义千问的适配器。把DashScope的模型qwen-plus、qwen-max等封装成标准接口。spring-ai-alibaba-tool-calling工具调用能力。让模型可以在对话过程中调用你注册好的自定义函数。spring-ai-alibaba-graph图编排能力。用于构建复杂的Agent工作流。spring-ai-alibaba-examples官方示例工程。如果你只是简单聊天只需要引入core和dashscope。但要做RAG你还需要一个向量数据库的适配器框架支持Redis、Milvus、PGVector等。做NL2SQL的时候更多依赖的是Prompt工程和Schema描述不一定需要额外模块。做Agent的话tool-calling和graph就派上用场了。2.3 为什么建议直接用它而不是自己封装Http调用我在早期项目里试过直接用HTTP SDK调通义千问的接口做一两个功能没问题但做到后面你会发现自己在重复造轮子。模型调用管理、上下文对话轮次、Prompt模板化、输出Json格式校验、工具调用的参数解析每个环节都要自己写一遍。Spring AI Alibaba把这些都抽象好了。它参考了LangChain的设计思路但完全基于Spring Boot原生风格依赖注入、自动装配、配置绑定都跟Spring生态无缝衔接。写起来的感觉是你在写一个普通的Spring Service只不过里面多了一个ChatClient的组件。3. RAG落地把企业知识库变成大模型的记忆3.1 为什么单纯微调解决不了知识库问题在讲RAG之前先聊一下为什么很多人第一反应是微调模型。你的业务场景可能是员工手册、技术文档、产品FAQ这些内容不该让大模型凭空编。微调的做法是用这些数据去训练一个私有模型但问题很明显。第一微调成本高。数据清洗、标注、训练环境、GPU资源一套流程下来周期很长。第二你的文档是动态变化的今天新增一份明天修改一段每次都要重新训练完全不现实。第三微调解决的是输出风格和知识记忆问题但模型仍然会编造不在训练数据里的细节。RAG的思路则是检索加生成。你把文档切片、向量化之后存到向量数据库当用户提问时先把问题也向量化去库里找到最相似的几个片段再把片段作为上下文拼进Prompt里最后让模型基于这些材料生成回答。这个类比很简单微调是让模型背熟一本书RAG是允许模型考试时翻书。对大多数企业内部场景来说RAG是更灵活的方案。3.2 数据准备是RAG成败的第一个关口很多RAG项目效果差的第一个原因不在检索和生成环节而在数据切片环节。切片之前先做清洗。原始文档里常见的噪音包括页眉页脚、目录、重复段落、表格提取后变成的乱序文本。这些噪音会把向量化结果带偏。我建议的处理流程是先去页眉页脚再统一换行格式表格转成Markdown格式或文字描述图片OCR提取说明文字。切片长度怎么定这是RAG调参的第一个关键点。我踩过几个地方的坑最终形成了一套经验值普通技术文档按500到800字切一个片段保留段落边界。如果文档有明确的章节结构优先按章节切一个章节内再分片段。相邻片段之间保留50到100字的重叠避免检索时把完整信息切碎。为什么重叠这么重要举个真实例子。我之前处理一份接口文档一个接口的说明正好被切在两个片段边界处上片段说了接口地址和请求方法下片段说了参数和返回值。用户问这个接口的请求参数是什么检索返回了上片段模型只能看到地址和方法回答必然不完整。有了重叠后两个片段都包含衔接部分检索命中的概率就高了。嵌入式模型的选择也值得说一句。文本向量化的效果直接决定检索质量。Spring AI Alibaba支持DashScope的text-embedding-v2效果对于中文文档来说足够好。如果你处理的是英文为主的技术文档也可以用OpenAI的text-embedding-3-small效果挺稳。向量维度大一点检索精度相对会好一些但存储和计算成本也会上升按实际数据量来取舍。3.3 用Spring AI Alibaba搭建RAG的完整步骤这里直接给可落地的代码不需要绕弯子。第一步引入依赖。在pom.xml里加上DashScope和向量数据库的适配。我用的是Redis作为向量存储一来公司里Redis基本都有现成实例二来Spring AI对Redis的支持比较成熟。第二步配置连接信息。在application.yml里设置DashScope的API Key、模型名和Redis连接。第三步封装文档解析器。从PDF、Word、Markdown里提取文本要做格式清洗和切片。Spring AI的ParagraphPdfReader、PagePdfReader可以直接用但输出是纯文本列表切片逻辑要自己写。第四步编写EmbeddingService和VectorStore。启动时初始化VectorStore然后把切片文本逐条embedding入库。第五步定义问答接口。Controller接收用户问题先用VectorStore去检索相关性最高的TopK片段拼装Prompt再调用ChatModel生成回答。整个流程里我最想强调的是第五步的Prompt组装。有一个常见的反面教材把检索到的所有片段都一股脑塞进Prompt不管跟问题有没有关系。这样做有两个后果一是Prompt过长浪费Token二是无关片段会干扰模型判断甚至让模型基于错误材料回答。我建议的做法是检索返回的TopK片段先做一个重排序或者相关性过滤只保留相似度超过阈值的片段。如果所有片段相似度都低那就不应该直接返回答案而是告诉用户知识库里没有相关内容。这一步做得好的话你的RAG质量会明显不一样。另外回答里最好让模型标注信息出处。你可以在Prompt里要求当回答基于提供的文档内容时请在末尾标注参考的来源编号。这样能够降低模型编造的倾向也方便用户自行核对。3.4 RAG调优的几个关键点第一个是检索方式。深入的检索加上对应的重排策略效果是会好过一个简单向量搜索的。Spring AI Alibaba里可以用Redis执行混合搜索向量相似度加文本相关性也有专门的Rerank配置配合通义千问的text-rerank模型。实测下来混合搜索加Rerank之后问答准确率能提升明显但延迟会多出几百毫秒到一秒需要按业务收益来权衡。第二个是日期时效。模型不知道当前日期如果文档里包含截至2025年3月这种时间信息那用户问最新的政策是什么模型可能分不清新旧文档。我的处理办法是在文档元数据里存发布日期检索排序时除了向量相似度还按时间倒序加权。Spring AI Alibaba的向量存储接口支持MetaData加上这一层功能就很方便了。第三个是对话历史和追问。如果用户在上文提到了这个功能怎么配置下一句问那权限呢你在组装Prompt时如果只考虑当前问题模型会因为没有上下文而答错。这里需要把最近几轮对话的历史摘要注入到Prompt里。4. NL2SQL让大模型帮你写SQL的落地细节4.1 NL2SQL的本质是规范化NL2SQL要解决的问题很直接业务人员不会写SQL但他们会用自然语言问问题。比如上个月华东区的销售额top10的产品是什么。你希望系统能自动转成数据库查询语句去执行再返回结果。这个场景的本质不是让模型学SQL语法模型本来就会SQL。难点在于第一让模型理解你的库表和字段含义第二生成的SQL必须安全、准确第三面对复杂的业务问题模型需要推理出正确的表关联关系和过滤条件。所以NL2SQL的核心工作是Schema描述的编写以及SQL生成后的校验。4.2 Spring AI Alibaba里怎么实现如果你使用DashScope的通义千问模型它本身就具备不错的SQL生成能力。你的工作是把数据库结构、示例数据、业务规则整理好用Prompt把它传进去。我提供一个比较可靠的Prompt模板结构系统角色定义明确告诉模型它是一名数据库专家只能根据提供的表结构和规则生成SQL禁止猜测不存在的字段。数据库结构定义使用建表语句CREATE TABLE来描述每个表的字段、类型、注释和索引。业务规则说明比如订单金额以实际支付金额为准退款订单不计入销售额这类逻辑约束。示例问题对给两到三个典型的问答示例让模型模仿你的输出格式。输出格式约束要求只输出SQL语句本身不要解释不要加Markdown代码块标记。这个模板看起来简单但细节决定成败。建表语句的英文注释、字段命名是否清晰、是否能表达字段的业务含义都会直接影响模型的表现。字段名如果都是pty1、c002这种无意义缩写模型根本无从理解字段含义。4.3 Schema上下文的动态管理有一个很现实的问题一个数据库可能有几十张表但你不可能把全部建表语句都塞进Prompt里Token数量会爆炸模型也会被无关的表结构干扰。我建议的方案是两步走先做表召回。用户提问后先根据问题里的关键词从表名、字段名、字段注释里去匹配相关的表。召回方式可以简单用ES或数据库模糊匹配也可以先向量化表描述之后走向量检索。把召回的相关表的建表语句拼装成Prompt再传给模型生成SQL。如果表关联关系复杂模型可能需要看到所有相关表的schema才能正确地JOIN。这个先召回后生成的架构其实就是把NL2SQL问题做了降维处理。一次只给模型看相关的表定义生成成功率会高很多模型也不容易被无关信息干扰。4.4 安全底线与防注入NL2SQL的安全问题比普通聊天还要重要。模型生成的SQL如果直接扔给数据库执行轻则查询结果不对重则删表、脱库这个后果谁都不想承担。我建议至少做下面几层防护权限控制使用独立的低权限数据库账号只授予SELECT权限禁止更新和删除操作。这是底线中的底线。只读检测在SQL执行前做关键字检查检测到insert、update、delete、drop、alter、truncate等破坏性语句直接拒绝。别指望模型一定不会生成这些语句加上一层保险。超时控制设置查询超时避免生成的SQL是笛卡尔积死循环式的慢查询把连接池拖垮。结果行数限制在SQL外层包一层LIMIT自动限制最大返回行数比如100或者200行。查询结果几十万行的场景即使没坏处也对系统性能有冲击。行级权限如果是多租户系统一定要在SQL里强制拼接租户ID的过滤条件。这个逻辑不能让模型自己决定拼不拼需要系统层面加进去。这几层防护我建议你都做。只做其中一两层是不够的因为每一层都可能被绕过。宁可多几个检查步骤也不要省事换来事故。4.5 实测中常见的质量问题NL2SQL生成的结果在我实际测试中有几类高频问题表名列名幻觉。模型可能生成了库里根本不存在的字段因为上下文里有了表结构幻觉率好很多但遇到表字段命名不规范的情况还是会出错。解决办法是把字段清单做成约束Prompt里明确写禁止使用未在上述表结构中出现的字段。过滤条件遗漏。比如近30天的订单模型可能只看日期范围忘了订单状态过滤。解决办法是在业务规则里把默认过滤条件写清楚。聚合计算错误。比如平均客单价模型可能用SUM(amount)/COUNT(*)而不是SUM(amount)/COUNT(DISTINCT user_id)。这类问题要靠示例对来引导。我的经验是每次上线之前准备一批测试问题集跑完写总结记录哪些Query准确、哪些失败、失败原因是什么。这个回归测试很重要因为你改了Prompt或组织规则以后跑一遍测试集才能知道是不是变好还是变坏。5. Agent开发从工具调用到自主决策5.1 理解Agent和Function Calling的关系在Spring AI Alibaba里开发Agent核心机制是Function Calling工具调用。简单说模型在回答用户问题时遇到自己无法直接回答的内容比如需要查库存、需要调外部接口它不会硬编答案而是输出一个我想调用某个函数的请求框架把这个请求解析出来执行你的Java方法再把执行结果返回给模型模型基于返回结果组织最终回答。这个过程里你有两个角色一个是模型负责决策。它根据用户的意图决定要不要调用工具调用哪个工具参数是什么。一个是你的Java代码负责执行。每个工具就是一个被Tool注解修饰的方法执行后把结果返回给模型。5.2 编写一个简单Agent天气预报库存查询我举个具体例子。假设你要做一个客服助手Agent它需要能查询实时库存和预计发货时间。这两个信息模型自己是不知道的必须通过调用后端接口取得。在Spring AI Alibaba里写法很简洁先定义一个工具类方法上加Tool注解同时给出清晰的中文描述。然后在调用ChatModel时把工具类注册进去。核心技巧在于Tool注解的description。这个描述写得清不清楚直接决定模型在什么时候调用这个工具以及参数传得对不对。一个合格的描述应该说明这个工具是干什么的、什么时候用它、参数分别代表什么。描述模糊的话模型会在不该调用的时候乱调然后在传入参数时也会反复问用户或乱猜。5.3 多工具编排让Agent学会先A后B单工具调用其实不难真正的挑战是多步推理和多工具编排。比如用户问这个商品有库存吗如果有的话帮我创建一个预售订单。这个过程至少涉及两个工具查库存再创建订单。而且创建订单依赖查询到的商品状态。怎么做不需要你写if-else逻辑模型本身能够根据对话历史来决策。你只需要在系统Prompt里写清楚请先检查库存再创建订单。Spring AI Alibaba的ChatClient支持自动维护对话历史和工具调用状态你只需要把工具列表传进去模型可以多轮调用工具直到得出最终答案或者无法继续。这里有一个成本问题需要注意多轮工具调用会消耗更多的Token每次调用都会把历史上下文再传给模型一次。我建议在Agent场景里对历史记录做一个轻量摘要避免上下文无限膨胀。5.4 使用Graph模块编排复杂的Agent流程如果你的Agent流程已经不满足于单串多轮工具调用而是需要分支、循环、条件判断比如先判断用户身份如果是VIP走快速退款通道否则走人工审核那就应该用Graph模块。Spring AI Alibaba的Graph模块允许你定义一系列节点每个节点是一个独立的处理单元节点之间有状态转移关系。它的实现思路类似LangGraph好处是把控制流显式地画出来可控性比较强。不过我也说句实话Graph模块的学习曲线比单纯用Function Calling高不少。如果只是三五个工具线性调用不建议上Graph先把Function Calling和Prompt写好就够用了。Graph适合的是决策路径分叉场景多的复杂Agent。5.5 Agent开发的一个坑模型误解工具参数这类问题特别常见。工具方法定义了一个枚举类型的参数模型却传了不支持的字符串框架在反序列化时就报错了。解决办法有两个一种是在Prompt里明确说清楚参数的枚举值有哪些让模型按规范传参另一种是工具方法内部做容错处理不认识的参数值映射到默认值。另一个高频问题是工具调用结果为空。模型调用了工具但工具返回null或者空列表模型可能会强行编造一个有意义的回答。我建议工具返回结果时就带状态信息比如查无数据和系统异常分开模型看过之后至少不会在回答里硬凑。6. 常见问题与排查技巧实录6.1 模型重复调用同一个工具陷入死循环三个字上下文。模型每一步都会把历史记录带进去如果上一步的工具返回结果没有正确写入消息历史模型就不知道工具已经调用过了会再来一遍。Spring AI Alibaba处理这个做得好的地方是工具调用信息会自动追加到Message列表。但如果你自己组装消息历史就一定要确保工具调用记录和工具返回结果都完整存在于历史中两头缺失任何一处都可能引发重复调用。6.2 Token数超限特别是RAG场景把检索片段全塞进Prompt很快就把上下文窗口打满了。我的建议是把检索片段做摘要压缩后再进Prompt模型先读摘要再按需展开。这招比简单截断效果好。6.3 检索结果不相关排查步骤先看你查询的向量化表现怎么样。把问题和文档单独拿出来比对Embedding结果看在向量相似度上是不是真的有区分度。然后看召回TopK是否合理。你可以临时把K调大一些人工看一眼返回的片段是不是你想要的内容。最后才考虑做全体重排。6.4 响应延迟大RAG链路本身就比纯对话多几步。尽量把向量检索和模型调用做成并行不要让用户等太久。对实时性要求高的场景模型选择考虑一下用qwen-turbo效果和速度折中来看比较合适。6.5 多版本兼容问题Spring AI Alibaba目前还处在快速迭代期API变动比较频繁。我的建议是锁定一个小版本测试通过后不要随便升级。升级前重点看ReleaseNote里关于ChatClient、ToolCalling、VectorStore相关部分的变更说明。7. 最后说点实在话这个系列写到第四篇我自己最大的体会是Spring AI Alibaba已经把接入大模型的门槛降得很低了但真正考验开发者的不再是怎么调API而是怎么设计Prompt、怎么组织数据、怎么控制边界、怎么做安全兜底。RAG、NL2SQL、Agent这三个方向任何一个单独拿出来都可以做成一个颇具规模的系统但它们的共通点也相当一致在模型能力之上拼的还是怎么把业务知识整理好怎么把系统约束控制住怎么设计清晰的工具逻辑。我也建议大家在学习这个框架的过程中多留意官方示例仓库里的代码。更重要的是自己动手把Demo跑起来然后把文档换成自己工作里的资料模型换成自己熟悉的业务数据踩一些真实的坑你会对这些组件的工作方式有更深的理解。希望这篇能帮到你也欢迎交流你在做这一类系统时踩过的坑。