Spring AI 入门分享:它和“直接调 API“到底差在哪

技术栈:Spring Boot 3.4.5 + Spring AI 1.0.0(GA)+ 通义千问(OpenAI 兼容接口)


一、先抛个问题

很多人第一次看 Spring AI 的 demo 会懵:

java 复制代码
@GetMapping("/chat")
public String chat(@RequestParam String message) {
    return chatClient.prompt().user(message).call().content();
}

"这不就是调了个通义千问的 API 吗?有什么意义?"

这个质疑是对的。 就"发一句、收一句"这种场景,Spring AI 跟你自己用 RestTemplate/OkHttp 调一次 HTTP 几乎没区别。

它的价值不在"调一次 API",而在于:真实 AI 应用很快会超出一问一答,那时候自己手写会非常痛苦。 下面用能跑的例子说明。


二、核心区别:一张表

能力 直接调 API(裸 HTTP) Spring AI
基础对话 自己拼 JSON 请求、解析响应 .call().content()
多轮对话记忆 自己维护消息列表、估算 token、裁剪历史 ChatMemory 接入
结构化输出(回复转 Java 对象) 自己写 prompt 要 JSON、ObjectMapper 解析、处理格式错乱 .entity(Xxx.class)
工具调用(让 AI 调你的代码) 手动解析 tool_calls、执行、回填、再请求一轮 @Tool + .tools(...)
RAG 知识库 自己做文档切分、向量化、检索、拼 prompt QuestionAnswerAdvisor
流式输出(打字机效果) 自己处理 SSE 流式解析 .stream()
切换模型(通义 → OpenAI → 本地 Ollama) 改一堆请求代码和参数 application.yml
日志 / 监控 / 重试 全部自己实现 内置(Observability)

一句话:"调 API" 是地基,Spring AI 的价值是在地基上能快速盖楼。


三、Demo 1:结构化输出 ------ 省掉解析的活

接口 :GET /study-plan?topic=Spring AI

java 复制代码
@GetMapping("/study-plan")
public StudyPlan studyPlan(@RequestParam String topic) {
    return chatClient.prompt()
            .user(u -> u.text("为「{topic}」这个主题,生成一个适合初学者的学习计划")
                    .param("topic", topic))
            .call()
            .entity(StudyPlan.class);   // ← 关键就这一行
}
java 复制代码
public record StudyPlan(String topic, List<String> steps, String summary) {}

返回的直接是对象,不是文本:

json 复制代码
{
  "topic": "Spring AI",
  "steps": ["搭建 Spring Boot 项目", "接入 ChatClient", "..."],
  "summary": "..."
}

裸调 API 你得自己干 :写 prompt 要求返回 JSON → ObjectMapper 解析 → 处理模型偶尔不守格式的情况。

Spring AI 干了:自动注入"请按这个格式返回"的说明 + 解析 + 映射成对象。


四、Demo 2:工具调用 ------ 让 AI 反过来调你的代码

接口 :GET /assistant?message=现在几点了?顺便查下 iPhone 库存

java 复制代码
@GetMapping("/assistant")
public String assistant(@RequestParam String message) {
    return chatClient.prompt()
            .user(message)
            .tools(new AssistantTools())   // ← 把工具交给模型
            .call()
            .content();
}
java 复制代码
public class AssistantTools {

    @Tool(description = "获取服务器当前的日期和时间")
    public String getCurrentDateTime() {
        return LocalDateTime.now().format(...);
    }

    @Tool(description = "查询指定商品的当前库存数量")
    public String getStock(@ToolParam(description = "商品名称") String product) {
        // 这里可以查真实数据库
    }
}

执行流程(模型本身不知道时间、更不知道你的库存):

  1. 模型看到你提供的两个工具;
  2. 自己决定 要调用它们,并从用户的话里提取参数 iPhone;
  3. Spring AI 真正执行你的 Java 方法,把结果回填给模型;
  4. 模型用真实数据组织回答:「现在是 2026-06-09 14:30,iPhone 当前库存 42 件」。

这就是 AI 能"操作你系统"的入口 ------ 查库、下单、发邮件都靠它。

这也是裸调 API 最繁琐的部分:要手动解析 tool_calls、回填、再发一轮请求。


五、Demo 3:对话记忆 ------ 让 AI 记住上下文

接口 :GET /memory-chat?conversationId=u1&message=...

java 复制代码
public ChatMemoryController(ChatClient.Builder builder) {
    ChatMemory chatMemory = MessageWindowChatMemory.builder()
            .maxMessages(20)   // 只保留最近 20 条,防止历史无限增长把 token 撑爆
            .build();
    this.chatClient = builder
            .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
            .build();
}

@GetMapping("/memory-chat")
public String chat(@RequestParam(defaultValue = "default") String conversationId,
                   @RequestParam String message) {
    return chatClient.prompt()
            .user(message)
            .advisors(a -> a.param(ChatMemory.CONVERSATION_ID, conversationId))  // ← 用会话 id 隔离
            .call()
            .content();
}

演示"它记住了" (同一个 conversationId=u1):

  1. ?conversationId=u1&message=我叫小明 → 好的,小明
  2. ?conversationId=u1&message=我叫什么名字?你叫小明(记住了上一轮)
  3. ?conversationId=u2&message=我叫什么名字? → 我不知道(另一段会话,互不干扰)
关键点 作用
MessageWindowChatMemory 只保留最近 N 条,防止 token 爆掉
MessageChatMemoryAdvisor 每次请求自动把历史拼进 prompt、再把本轮问答存回去
conversationId 多用户/多会话隔离的钥匙(真实项目常用 userId / sessionId)

裸调 API:得自己维护消息 List、自己决定留几条、每次手动拼进请求体。

⚠️ 默认内存存储,重启即清空 ;生产换 JdbcChatMemoryRepository / Redis,接口不变。


六、Demo 4:RAG 知识库 ------ 让 AI 回答它本不知道的私有知识

接口 :GET /rag?question=...

第一步,把"私有知识"灌进向量库(启动时自动执行):

java 复制代码
@Component
public class KnowledgeBaseLoader implements ApplicationRunner {
    private final VectorStore vectorStore;   // 构造注入
    public void run(ApplicationArguments args) {
        vectorStore.add(List.of(
            new Document("光途科技的员工每年享有 15 天带薪年假,入职满 3 年后增加到 20 天。"),
            new Document("光途科技的报销:登录 OA 提交发票,审批后 3 个工作日到账。")
            // ...更多文档
        ));   // add 时自动调 embedding 模型把文本转成向量
    }
}

第二步,提问时挂上 QuestionAnswerAdvisor:

java 复制代码
@GetMapping("/rag")
public String rag(@RequestParam String question) {
    return chatClient.prompt()
            .advisors(QuestionAnswerAdvisor.builder(vectorStore).build())  // ← RAG 入口
            .user(question)
            .call()
            .content();
}

对照演示(故意用虚构公司"光途科技",模型训练时不可能知道它):

  • GET /chat?message=光途科技的年假有几天?瞎编
  • GET /rag?question=光途科技的年假有几天?15 天,满 3 年 20 天(从知识库检索到了)

RAG 四步:

  1. 启动时:文档 → 调 embedding 转成向量 → 存进向量库
  2. 提问时:把问题也转成向量,在库里找语义最相近的几条
  3. 把检索到的资料拼进 prompt:参考以下资料回答:{资料}\n问题:{问题}
  4. 模型基于真实资料作答 ------ 第 2~4 步全由 QuestionAnswerAdvisor 自动完成

⚠️ 踩坑提醒:QuestionAnswerAdvisor独立依赖 spring-ai-advisors-vector-store 里,必须单独引,否则 import 标红、编译不过。

demo 用的 SimpleVectorStore 是内存版、重启清空;生产换 PGVector / Redis / Milvus,只换 VectorStore 这个 Bean,/rag 一行不用动。

一句话:RAG = 检索你的私有资料 + 喂给模型"开卷考试",从而回答它本不知道的领域知识,还能减少瞎编。


七、学习步骤(建议路线)

阶段 0:环境准备

  • JDK 17+,Spring Boot 3.x
  • 申请一个模型 API Key(通义千问 / 智谱 / DeepSeek 都行,都有 OpenAI 兼容接口)
  • API Key 用环境变量,别写死在配置里

阶段 1:跑通基础对话

  • 引入 spring-ai-bom + spring-ai-starter-model-openai
  • application.yml(base-url 指向兼容端点 + model)
  • 写一个 /chat 接口,理解 ChatClientprompt().user().call().content()

阶段 2:结构化输出

  • 定义一个 record,用 .entity(Xxx.class) 拿到对象
  • 理解"AI 输出 → 程序数据"这一步的价值

阶段 3:工具调用(Function Calling)

  • 写带 @Tool 的方法,用 .tools(...) 注册
  • 理解"AI → 反向调用你的代码"

阶段 4:多轮对话记忆

  • 接入 ChatMemory,让对话能记住上下文
  • 理解 token 与历史裁剪

阶段 5:RAG 知识库

  • 文档切分 → 向量化 → 存向量库(如 PGVector / Redis)
  • QuestionAnswerAdvisor 让 AI 基于你的资料回答

阶段 6:进阶

  • 流式输出(SSE)、Prompt 模板、Advisor 机制、可观测性、多模型编排

八、一句话总结

直接调 API 解决的是"怎么把一句话发给模型 ";

Spring AI 解决的是"怎么把模型变成应用的一部分" ------ 让它的输出能被程序使用,让它能调用程序的能力。

前者是 HTTP 客户端,后者是 AI 应用开发框架。

相关推荐
品牌测评1 小时前
2026年AI声音克隆工具深度实测:声线APP领衔,解锁声音创作全场景新范式
人工智能
只说证事1 小时前
2026 大专可以考哪些金融行业证书
人工智能
自律懒人1 小时前
2026年4大AI编程CLI工具横评:Claude Code、Codex、Gemini CLI、OpenCode,实测30天差距有多大?
人工智能·ai编程
水如烟2 小时前
孤能子视角:从大模型图像识别看“实体”与“关系”
人工智能
晨之清风2 小时前
Codex常用命令
人工智能
hsg772 小时前
简述:2026年中考一地作文题目 :接纳无解,向阳求索
人工智能·机器学习
北京耐用通信2 小时前
国产化替代优选!耐达讯自动化NY-HUB6完美兼容替代PB-HUB6\GL
人工智能·科技·网络协议·自动化·信息与通信
宸丶一2 小时前
Day 10:LangGraph - Agent 的图执行引擎
java·windows·python
hikktn2 小时前
Excel 导出 OOM 预防实战:30 万行从堆溢出到 50MB 的演进
java·excel·easyexcel