ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Java 工程师的大模型应用开发实战:基于 Spring AI 构建对话、工具调用与 RAG 系统

Java 工程师的大模型应用开发实战:基于 Spring AI 构建对话、工具调用与 RAG 系统 这里写自定义目录标题欢迎使用Markdown编辑器一、为什么要做定制化的大模型开发二、Spring AI 的架构定位与核心组件三、实战第一步搭一个能对话的应用生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注脚注释也是必不可少的KaTeX数学公式新的甘特图功能丰富你的文章UML图表流程图FLowchart流程图导出与导入导出导入欢迎使用Markdown编辑器你好 这是你第一次使用# Java 工程师的大模型应用开发实战基于 Spring AI 构建对话、工具调用与 RAG 系统过去几年大模型应用开发的教程几乎被 Python 生态垄断LangChain、LlamaIndex 这些框架的示例清一色是 Python 代码。对于长期深耕 Java 技术栈的工程师来说这带来一个很现实的痛点为了接入大模型能力要么硬着头皮学习一门新语言要么在现有 Java 系统之外另起一套 Python 服务徒增运维和团队协作成本。Spring AI 的出现正是为了解决这个问题。它借鉴了 LangChain 的模块化设计思路但完全生长在 Spring 生态之上让 Java 工程师可以用熟悉的依赖注入、自动配置和 MVC 模式来构建大模型应用。本文会从零开始围绕对话、Function Calling 工具调用、RAG 知识库三大模块展开给出完整的代码示例和工程落地建议。一、为什么要做定制化的大模型开发很多人会问ChatGPT、DeepSeek 这些产品已经很强大日常对话、文案生成都能胜任为什么还要自己动手开发答案在于大模型的三个天然短板。第一是知识时效性差模型参数在训练时固化无法感知训练之后发生的事件和最新数据问它最近上线的产品规格它只能给出一个模棱两可的旧答案。第二是缺乏私有数据访问能力企业内部的知识库、业务数据库、工单系统对模型来说完全是黑盒模型根本无法知道这家公司的真实业务数据。第三是不可控模型只能说不能做无法真正调用外部 API、查询数据库、写入业务系统更无法在回答之后触发一个实际动作。这三个短板恰好对应了大模型应用开发的三大核心技术RAG 解决知识注入问题让模型知道该知道的Function Calling 解决工具调用问题让模型能做该做的Agent 编排解决复杂任务自动执行问题让多个步骤串联成完整的工作流。理解了这一点就不难理解为什么大模型应用开发的核心工作不是调 API 聊天而是把模型嵌入到真实的业务流程中让它成为系统的一个组件。这也是为什么像 Spring AI 这样能与现有企业架构深度集成的框架价值远大于一个简单的 SDK 封装。二、Spring AI 的架构定位与核心组件Spring AI 的定位非常清晰它是 Spring 生态中面向 AI 应用的抽象层。从架构上看它把大模型应用拆解成几个标准组件每个组件解决一类问题组件之间通过清晰的接口协作。ChatClient 是统一入口。这是 Spring AI 1.0 之后的主推 API采用 Fluent 风格设计开发者可以通过链式调用来构造请求、附加上下文、指定工具、配置顾问Advisor。相比早期的 ChatModel 直接调用ChatClient 更贴近业务语义可读性也更好。所有后续的对话、工具调用、RAG 问答都可以通过这一个入口完成。Model 层做统一抽象。Spring AI 定义了统一的 ChatModel 接口OpenAI、Anthropic、通义千问、DeepSeek、智谱、Ollama 等几十家模型提供商都有对应的实现。切换模型供应商时只需修改配置文件中的 base-url 和 api-key业务代码几乎不用动。这种配置驱动的设计让模型选型变成了一个运维决策而不是代码重构也方便在开发环境用轻量模型、生产环境用商用模型。Prompt 模板是沟通桥梁。Spring AI 支持基于 StringTemplate 的提示词模板可以把系统指令、用户输入、检索到的上下文组织成结构化的 Prompt。模板机制解决了两个问题一是避免在业务代码里拼接长字符串让提示词可维护二是实现提示词的版本管理和复用同一个模板可以搭配不同的参数在多处使用。Advisor 机制是扩展点。这是 Spring AI 非常有特色的设计类似于 Spring MVC 里的拦截器。检索增强RAG、对话记忆、日志记录、安全过滤都可以通过 Advisor 以切面的方式挂载与核心业务逻辑解耦。这种可插拔的设计让复杂能力的组合变得非常优雅。三、实战第一步搭一个能对话的应用假设项目已经通过 Spring Initializr 初始化并引入了 spring-ai-starter 相关依赖。先在 application.yml 中配置模型信息这里以兼容 OpenAI 协议的国产模型为例spring:ai:openai:base-url:https://api.deepseek.comapi-key:${DEEPSEEK_API_KEY}chat:options:model:deepseek-chattemperature:0.7 然后注入 ChatClient写一个最简单的对话接口 java RestController public class ChatController{private final ChatClient chatClient; public ChatController(ChatClient.Builder builder){this.chatClient builder .defaultSystem(你是一位资深的技术顾问回答要简洁、准确、有条理不要编造不确定的信息。) .build();}PostMapping(/chat) public String chat(RequestBody ChatRequest request){return chatClient.prompt() .user(request.message()) .call() .content();}} 到这里一个最基础的大模型对话接口就完成了。但真正的工程挑战不在这里而在下面两个部分——让模型动手和让模型知情。## 四、Function Calling让模型真正动手大模型本身不会执行操作但通过 Function Calling 机制模型可以在回答中声明我需要调用某个函数由应用层负责真正执行再把执行结果回传给模型继续推理。这相当于给模型装上了手让它能基于真实系统状态做出判断和行动。 Spring AI 中注册工具函数非常简单只要定义一个 Java Bean方法上用 Tool 注解标记即可 java Service public class OrderService{Tool(description 根据订单号查询订单状态返回订单的当前处理进度) public String queryOrderStatus(ToolParam(description 订单号如 SO20260905001) String orderNo){// 这里调用真实的订单查询逻辑 return orderRepo.queryStatus(orderNo);}} 然后在构建 ChatClient 时注册这个服务 java this.chatClient builder .defaultSystem(...) .defaultTools(new OrderService()) .build(); 用户问我的订单 SO20260905001 到哪一步了模型会识别出需要调用 queryOrderStatus 工具Spring AI 自动完成参数提取、函数调用、结果回填的完整循环最终生成自然语言回答。整个过程对用户透明对调用方也透明。 这个机制的工程价值被很多人低估。它意味着模型不再是封闭的生成器而是一个会调度的决策器。企业可以把订单、库存、排班、审批、物流等内部能力全部注册为工具让模型基于真实业务数据做推理幻觉问题从根源上被大幅削弱——因为关键事实来自系统本身而不是模型记忆。更进一步多个工具可以组合使用查询订单状态后自动查询物流信息再根据异常情况触发退款申请这就是 Agent 的雏形。 在实践中有几个值得注意的细节。第一工具描述要写得足够具体模型是靠 description 来判断何时调用、如何传参的描述模糊会导致模型乱调或者不调。第二参数定义要精确包括类型、含义、取值范围避免歧义导致参数提取失败。第三对工具调用要做超时和降级处理模型可能连续调用多个工具任何一个失败都要有兜底逻辑比如返回查询失败请稍后重试。第四工具的数量不宜过多一次对话暴露几十个工具会让模型决策质量下降应按场景拆分。## 五、RAG把私有知识注入模型Function Calling 解决的是动作问题RAG 解决的是知识问题。企业内部的海量文档——产品手册、技术规范、历史工单、规章制度——大模型一概不知。RAG 的思路是把文档切片、向量化存入向量数据库用户提问时先检索相关片段再把片段作为上下文拼进 Prompt让模型带着资料答题。 Spring AI 对 RAG 的封装体现在几个环节。文档读取用 DocumentReader支持 PDF、Word、TXT、Markdown 等格式文本切分用 TokenTextSplitter按 token 数切分而不是简单地按字符数切分这样更符合模型的理解粒度向量化用 EmbeddingModel可以复用云厂商的 embedding 接口也可以用本地 BGE 等开源模型存储用 VectorStore 抽象支持 Redis、Milvus、PGVector、Elasticsearch 等。 一个简化但完整的流程如下 java // 1. 读取并切分文档 var reader new PagePdfDocumentReader(classpath:docs/manual.pdf); var splitter new TokenTextSplitter(500,100); // 每片500 token重叠100 var chunks splitter.apply(reader.get()); // 2. 写入向量库 vectorStore.add(chunks); // 3. 构建带检索的问答 String answer chatClient.prompt() .user(电池的质保期是多久) .advisors(QuestionAnswerAdvisor.builder(vectorStore).build()) .call() .content(); QuestionAnswerAdvisor 会自动完成检索 → 拼接上下文 → 生成回答的闭环还会在 Prompt 中注入如果上下文中没有相关信息请明确告知这类约束降低幻觉风险。 从工程角度看RAG 系统的质量瓶颈往往不在模型而在检索链路。切片粒度太大会引入噪声太小会丢失语义完整性向量模型的选择直接影响相似度计算质量检索数量太少可能漏掉关键信息太多则稀释注意力。此外重排序Rerank在检索结果超过十条时几乎是必需品可以让真正相关的片段排到前面。我个人的建议是先跑通基础链路再针对业务场景建立评测集用真实的几十条问题衡量召回率和答案准确率根据数据而不是感觉来调参。RAG 的优化是典型的先测量后优化没有评测集的调参都是在碰运气。## 六、架构层面的思考把以上三块拼在一起一个典型的企业级大模型应用就成型了对话入口负责交互Function Calling 负责操作RAG 负责知识。但落地时还有几个架构问题值得提前想清楚。 一是**状态管理**。对话是多次请求的组合需要把历史消息存起来Redis或数据库每次请求携带完整上下文。Spring AI 提供了 ChatMemory 抽象可以基于内存、JDBC 或 Redis 实现也可以自己定制。需要注意上下文长度上限对话过长时要做摘要压缩或截断否则成本飙升且质量下降。 二是**异步化**。大模型响应动辄数秒同步阻塞会拖垮Web 服务的线程池。生产环境应该用 WebFlux 或消息队列把生成任务异步化前端通过轮询或 SSE 获取结果。对于长文档生成、批量总结这类任务异步是唯一合理的方案。 三是**可观测性**。模型调用的token 消耗、延迟、失败率都必须纳入监控。建议在 ChatClient 外层包一层拦截器记录每次请求的模型、输入输出 token 数、耗时和错误信息为成本控制和问题排查提供数据支撑。没有观测数据的 AI 应用出了问题只能靠猜。 四是**成本治理**。同样的需求提示词写法和上下文长度的差异可能带来数倍的成本差距。要建立Prompt 评审机制控制注入上下文的规模对长文本场景考虑摘要或向量检索代替全量注入。缓存也是降本利器相同或相似的请求可以命中结果缓存。 五是**安全与合规**。面向外部用户的对话系统必须做内容过滤和敏感信息脱敏防止提示词注入攻击——用户可能通过精心构造的输入诱导模型泄露系统指令或执行危险操作。企业数据默认不应发送到境外模型必要时采用私有化部署或本地模型。## 七、总结Spring AI 的价值不在于比 LangChain 功能更全而在于它把大模型能力无缝嵌入了 Java 工程师熟悉的开发范式。对于已经有成熟 Java 技术栈的企业用 Spring AI 构建对话、工具调用和 RAG 能力可以最大程度复用现有的微服务、监控、部署体系把大模型从玩具变成生产力。 从更宏观的视角看大模型应用开发的竞争焦点正在从能不能调通 API转向能不能稳定运行在生产环境。模型能力会不断迭代而工程体系的健壮性、可观测性、成本可控性才是长期壁垒。技术栈的选择从来不是单纯的技术问题而是工程效率和团队能力的综合权衡Java 生态在这方面的积累——成熟的依赖管理、完善的测试体系、丰富的中间件——恰好是大模型应用从 Demo 走向生产最需要的东西。对于 Java 团队而言现在正是把 Spring AI 引入技术栈在真实业务中积累经验的合适时机。**Markdown编辑器**所展示的欢迎页。如果你想学习如何使用Markdown编辑器,可以仔细阅读这篇文章了解一下Markdown的基本语法知识。## 新的改变我们对Markdown编辑器进行了一些功能拓展与语法支持除了标准的Markdown编辑器功能我们增加了如下几点新功能帮助你用它写博客 1.**全新的界面设计**将会带来全新的写作体验 2. 在创作中心设置你喜爱的代码高亮样式Markdown**将代码片显示选择的高亮样式**进行展示 3. 增加了**图片拖拽**功能你可以将本地的图片直接拖拽到编辑区域直接展示 4. 全新的**KaTeX数学公式**语法 5. 增加了支持**甘特图的mermaid语法[^1]**功能 6. 增加了**多屏幕编辑**Markdown文章功能 7. 增加了**焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置**等功能功能按钮位于编辑区域与预览区域中间 8. 增加了**检查列表**功能。[^1]:[mermaid语法说明](https://mermaid.js.org/intro/)## 功能快捷键撤销kbdCtrl/Command/kbd kbdZ/kbd重做kbdCtrl/Command/kbd kbdY/kbd加粗kbdCtrl/Command/kbd kbdB/kbd斜体kbdCtrl/Command/kbd kbdI/kbd标题kbdCtrl/Command/kbd kbdShift/kbd kbdH/kbd无序列表kbdCtrl/Command/kbd kbdShift/kbd kbdU/kbd有序列表kbdCtrl/Command/kbd kbdShift/kbd kbdO/kbd检查列表kbdCtrl/Command/kbd kbdShift/kbd kbdC/kbd插入代码kbdCtrl/Command/kbd kbdShift/kbd kbdK/kbd插入链接kbdCtrl/Command/kbd kbdShift/kbd kbdL/kbd插入图片kbdCtrl/Command/kbd kbdShift/kbd kbdG/kbd查找kbdCtrl/Command/kbd kbdF/kbd替换kbdCtrl/Command/kbd kbdG/kbd## 合理的创建标题有助于目录的生成直接输入1次kbd#/kbd并按下kbdspace/kbd后将生成1级标题。输入2次kbd#/kbd并按下kbdspace/kbd后将生成2级标题。以此类推我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。## 如何改变文本的样式*强调文本*_强调文本_**加粗文本**__加粗文本__ 标记文本 ~~删除文本~~引用文本 H~2~O is是液体。 2^10^ 运算结果是 1024.## 插入链接与图片链接:[link](https://www.csdn.net/).图片:![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw)带尺寸的图片:![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw 30x30)居中的图片:![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw#pic_center)居中并且带尺寸的图片:![Alt](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9hdmF0YXIuY3Nkbi5uZXQvNy83L0IvMV9yYWxmX2h4MTYzY29tLmpwZw#pic_center 30x30)当然我们为了让用户更加便捷我们增加了图片拖拽功能。## 如何插入一段漂亮的代码片去[博客设置](https://mp.csdn.net/console/configBlog)页面选择一款你喜欢的代码片高亮样式下面展示同样高亮的 代码片. javascript // An highlighted block var foo bar;生成一个适合你的列表项目项目项目项目1项目2项目3计划任务完成任务创建一个表格一个简单的表格是这么创建的项目Value电脑$1600手机$12导管$1设定内容居中、居左、居右使用:---------:居中使用:----------居左使用----------:居右第一列第二列第三列第一列文本居中第二列文本居右第三列文本居左SmartyPantsSmartyPants 是一个文本转换工具主要功能是将普通的 ASCII 标点符号自动转换为更美观的印刷体标点符号。例如原始符号转换后说明引号“引号”直引号变弯引号单引号‘单引号’直单引号变弯单引号--–两个连字符变短破折号---—三个连字符变长破折号...…三个点变省略号创建一个自定义列表MarkdownText-to-HTMLconversion toolAuthorsJohnLuke如何创建一个注脚一个具有注脚的文本。1注释也是必不可少的Markdown将文本转换为HTML。KaTeX数学公式您可以使用渲染LaTeX数学表达式 KaTeX:Gamma公式展示Γ ( n ) ( n − 1 ) ! ∀ n ∈ N \Gamma(n) (n-1)!\quad\forall n\in\mathbb NΓ(n)(n−1)!∀n∈N是通过欧拉积分Γ ( z ) ∫ 0 ∞ t z − 1 e − t d t . \Gamma(z) \int_0^\infty t^{z-1}e^{-t}dt\,.Γ(z)∫0∞​tz−1e−tdt.你可以找到更多关于的信息LaTeX数学表达式here.新的甘特图功能丰富你的文章2014-01-072014-01-092014-01-112014-01-132014-01-152014-01-172014-01-192014-01-21已完成进行中计划一计划二现有任务Adding GANTT diagram functionality to mermaid关于甘特图语法参考 这儿,UML图表可以使用UML图表进行渲染例如下面产生的一个序列图王五李四张三王五李四张三李四想了很长时间, 文字太长了不适合放在一行.你好李四, 最近怎么样?你最近怎么样王五我很好谢谢!我很好谢谢!打量着王五...很好... 王五, 你怎么样?关于UML图表语法参考 这儿,流程图链接长方形圆圆角长方形菱形关于Mermaid语法参考 这儿,FLowchart流程图我们依旧会支持flowchart.js的流程图语法Created with Raphaël 2.3.0开始我的操作确认结束yesno关于Flowchart流程图语法参考 这儿.导出与导入导出如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到文章导出生成一个.md文件或者.html文件进行本地保存。导入如果你想加载一篇你写过的.md文件在上方工具栏可以选择导入功能进行对应扩展名的文件导入继续你的创作。注脚的解释 ↩︎
返回列表