目录

前言

1. 一个聊天接口,为什么最后写了一百多行?

2. Spring AI 是如何把这些代码隐藏起来的?

3. 真正减少的,不是 90 行代码

4. 企业项目为什么一定会走向 Spring AI?

5. 我的项目是如何一步步演进的?

5.1 第一阶段:HTTP 调用

5.2 第二阶段:Spring AI

5.3 第三阶段:流式输出

5.4 第四阶段:Memory

5.5 第五阶段:RAG

5.6 第六阶段:Agent

6. 自己封装 HTTP 不好吗?

7. 总结


前言

上一篇文章,我们完成了框架选型。

如果还没有看过上一篇,建议先阅读:

📖 《Java转AI第二课:为什么我最终选择Spring AI,而不是LangChain4j?》

上一篇主要回答了一个问题:

为什么最终选择了 Spring AI?

而这一篇,我们来聊另一个更实际的问题。

Spring AI 到底帮我们解决了什么?

很多刚开始学习 AI 的 Java 开发都会有这样的疑问:

调用大模型,不就是发一个 HTTP 请求吗?为什么还要学习一个新的框架?

事实上,我一开始也是这么认为的。

在我的第一个 AI Demo 中,没有使用任何 AI 框架,而是直接通过 RestTemplate 调用 DeepSeek 接口。

聊天功能成功跑起来的时候,我甚至觉得:

"Spring AI 好像也没什么特别的,不就是把 HTTP 封装了一层吗?"

直到后来开始真正做企业项目。

随着功能越来越多,我才发现:

真正复杂的,从来不是调用一次模型。

而是后续不断增加的新能力。

例如:

  • 多轮对话(Memory)
  • 流式输出(SSE)
  • RAG 企业知识库
  • Tool Calling
  • Agent
  • Workflow

这些能力几乎都离不开大模型调用。

如果继续自己维护 HTTP 请求,代码会越来越臃肿。

而 Spring AI 恰恰帮我解决了这个问题。

今天,就结合自己的项目,说说我是如何从一百多行 HTTP 调用,迁移到 Spring AI 的。


1. 一个聊天接口,为什么最后写了一百多行?

很多人第一次调用大模型时,都会觉得非常简单。

无非就是:

用户提问
↓
HTTP 请求
↓
DeepSeek
↓
返回答案

确实如此。

真正的业务代码,其实只有一句:

restTemplate.postForObject(...)

但为了让这一句话真正跑起来,我们还需要完成很多准备工作。

例如:

  • 创建 Header
  • 配置 Bearer Token
  • 设置 Content-Type
  • 拼接请求 JSON
  • 构建 Message 数组
  • 创建请求对象
  • 解析返回结果
  • 提取真正的回答内容
  • 处理异常
  • 编写 DTO

下面是一段我最开始调用 DeepSeek 时的核心代码(已做简化)。

RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.setBearerAuth(apiKey);
headers.setContentType(MediaType.APPLICATION_JSON);

Map<String, Object> body = new HashMap<>();
body.put("model", "deepseek-chat");

List<Map<String, String>> messages = 
                            List.of(Map.of("role", "user", "content", question));

body.put("messages", messages);

HttpEntity<Map<String, Object>> request = new HttpEntity<>(body, headers);

DeepSeekResponse response = 
                restTemplate.postForObject(url, request, DeepSeekResponse.class);

String answer = response.getChoices()
                        .get(0)                
                        .getMessage() 
                        .getContent();

这只是核心调用。

实际项目中,还需要:

  • 请求 DTO
  • 响应 DTO
  • 配置类
  • 日志
  • 超时处理
  • 异常处理
  • 配置读取

全部加起来,一个简单聊天接口就已经接近一百行代码。

当时我觉得:

调第三方接口,不就是这样写的吗?

直到后面不断增加新功能,我才意识到真正的问题。

假设今天增加一个 System Prompt。

我要修改 JSON。

明天增加多轮对话。

我要重新维护 Message。

后天增加流式输出。

整个 HTTP 调用又要重新封装。

真正变化的是业务。

而我却一直在维护同一套 HTTP 请求代码。

慢慢地,我开始意识到:

我需要的已经不是一个 HTTP 工具类,而是一套专门面向 AI 开发的框架。


2. Spring AI 是如何把这些代码隐藏起来的?

后来,我把项目迁移到了 Spring AI。

第一次写代码时,我最大的感受只有两个字:

舒服。

以前,一个聊天接口需要准备各种请求对象。

现在,只需要下面几行代码。

String answer = chatClient        
                        .prompt()        
                        .user(question)        
                        .call()        
                        .content();

第一次看到的时候,我甚至怀疑:

真的就这么简单?

后来才发现,Spring AI 已经帮我们完成了大量重复工作。

例如:

传统 HTTP 调用 Spring AI
创建 Header 自动完成
设置 Bearer Token 自动读取配置
拼接 JSON Prompt API
创建 Message 框架封装
发送 HTTP 请求 自动处理
解析 Response content() 获取结果
异常转换 统一处理

也就是说。

我们终于不用再关心:

  • Header 怎么写?
  • JSON 怎么拼?
  • Message 怎么组织?
  • 返回结果怎么解析?

开发者真正关心的,只剩下一件事。

我要给模型什么 Prompt。

这一点,也是我认为 Spring AI 最大的价值。


3. 真正减少的,不是 90 行代码

很多文章介绍 Spring AI 时,都喜欢说一句话:

Spring AI 可以把一百多行代码缩减到十几行。

这句话当然没错。

但真正做完项目之后,我越来越觉得:

真正减少的,并不是代码。

而是以后所有重复的开发工作。

例如。

如果我要增加 System Prompt。

以前,需要重新修改请求 JSON。

现在。

String answer = chatClient.prompt()        
                          .system(systemPrompt)        
                          .user(question)        
                          .call()        
                          .content();

如果增加历史消息。

String answer = chatClient.prompt()        
                            .messages(messages)        
                            .call()        
                            .content();

如果以后需要切换模型。

以前可能需要重新维护不同厂商的 HTTP 请求。

而现在,只需要切换对应的 ChatModelChatClient 配置即可,业务代码几乎不需要修改。

这也是 Spring AI 与普通 HTTP 封装最大的区别。

它不仅封装了一次调用。

更重要的是,它统一了整个 AI 应用的开发方式。

4. 企业项目为什么一定会走向 Spring AI?

如果我的项目始终只有一个聊天接口。

说实话,我可能一直都会使用 HTTP 调用。

因为它足够简单,也没有什么维护成本。

真正让我决定迁移到 Spring AI,是项目开始不断增加新的能力。

最开始,我的调用链只有这样:

Controller    
    │    
    ▼
ChatService    
    │    
    ▼
HTTP 工具类    
    │    
    ▼
DeepSeek

后来,项目开始陆续增加更多 AI 能力:

  • SSE 流式输出
  • 多轮会话 Memory
  • RAG 企业知识库
  • Tool Calling
  • Agent
  • Workflow

这时候,我发现问题来了。

这些能力虽然看起来完全不同,但最终都离不开一次大模型调用。

如果继续维护自己的 HTTP 工具类,就意味着每增加一个能力,都要继续扩展底层调用逻辑。

随着项目越来越复杂,HTTP 工具类也会越来越庞大。

迁移到 Spring AI 后,整个调用链变成了下面这样。

Controller    
    │    
    ▼
ChatService    
    │    
    ▼
ChatClient    
│    
├── Prompt    
├── Memory    
├── Advisor    
├── Tool Calling    
├── RAG    
└── ChatModel            
    │            
    ▼    
DeepSeek / OpenAI / Qwen

可以看到。

无论增加什么能力,业务层始终只需要调用 ChatClient

真正发生变化的是调用链,而不是业务代码。

这也是 Spring AI 最吸引我的地方。

Spring AI在企业项目中的调用链


5. 我的项目是如何一步步演进的?

很多文章介绍 Spring AI,都是直接讲 API。

但真正做项目的时候,并不是一开始就会上 Agent、RAG。

至少我是一步一步走过来的。

整个项目大概经历了下面几个阶段。

5.1 第一阶段:HTTP 调用

只有一个聊天接口。

HTTP
↓
LLM

5.2 第二阶段:Spring AI

迁移到 ChatClient

ChatClient
↓
LLM

5.3 第三阶段:流式输出

开始支持 SSE。

ChatClient
↓
Stream
↓
SSE

5.4 第四阶段:Memory

增加多轮对话。

chatClient.prompt()        
          .advisors(new MessageChatMemoryAdvisor(chatMemory))        
          .user(question)        
          .call()        
          .content();

聊天接口没有变化。

只是增加了一个 Advisor。


5.5 第五阶段:RAG

增加企业知识库。

chatClient.prompt()        
          .advisors(new QuestionAnswerAdvisor(vectorStore))        
          .user(question)        
          .call()        
          .content();

同样。

业务代码没有变化。

只是增加了另一个 Advisor。


5.6 第六阶段:Agent

开始接入 Tool Calling。

chatClient.prompt()
        .tools(caseTool, timelineTool)
        .user(question)
        .call()
        .content();

直到这里,我才真正理解 Spring AI 的设计理念。

它不是针对某一个功能设计的。

而是希望所有 AI 能力,都能通过同一种开发方式不断扩展。

对于开发者来说,这种一致性非常重要。

SpringAI能力


6. 自己封装 HTTP 不好吗?

当然不是。

事实上,我非常建议每个 Java 开发者,在学习 Spring AI 之前,先自己调用一次大模型接口。

原因很简单。

只有真正写过一次 HTTP 请求,你才知道:

  • Prompt 最终会被封装成什么样的 JSON?
  • Message 是如何组织的?
  • 为什么会有 System、User、Assistant 三种角色?
  • 模型返回的数据结构是什么?

理解这些之后,再去学习 Spring AI,会轻松很多。

所以,我一直认为:

学习阶段,可以自己写 HTTP。

真正进入企业项目后,再把这些重复工作交给框架。

这样既理解了底层原理,也能享受到框架带来的开发效率。


7. 总结

回过头来看,我觉得 Spring AI 最大的价值,并不是把一百多行代码缩减成十几行。

真正改变我开发方式的,是它提供了一套统一的 AI 编程模型。

无论是聊天、流式输出、Memory、RAG,还是 Agent,都围绕同一个 ChatClient 展开。

业务不断演进,调用方式却保持一致。

对于企业项目来说,这种可扩展性,远比少写几十行代码更重要。

如果让我用一句话总结这一篇,我会这样说:

Spring AI 真正减少的,不是几十行 HTTP 代码,而是未来不断重复的开发成本。


下一篇预告

完成了基础调用之后,我们正式进入 AI 项目中最常见的能力之一——流式输出(SSE)

下一篇我们会结合项目源码,实现一个真正的流式聊天接口,并重点聊四个问题:

  • 为什么 AI 聊天几乎都采用流式输出?
  • SSE 和 WebSocket 有什么区别?
  • Spring AI 如何实现流式输出?
  • 企业项目为什么更推荐 SSE?

AI学习路线


📚 推荐专栏

🤖 AI 技术专栏

从 0 到 1 学习 AI 应用开发,持续分享 Spring AI、RAG、Agent、企业级 AI 项目实战。

👉 https://blog.csdn.net/qupengkun/category_13184360.html

🚀 AI 转型日记

记录一名 10 年 Java 开发者从传统后端转向 AI 应用开发的全过程。

👉 https://blog.csdn.net/qupengkun/category_13183497.html


👨‍💻 关于作者

QCoding

专注 AI 应用开发与 Java 技术实践。

持续分享 Spring AI、RAG、Agent、企业级 AI 项目实战、架构设计与职业成长。

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐