欢迎光临
我们一直在努力

Day26-Java核心+Spring生态篇:5行代码接入大模型Spring AI

一、Spring AI 是什么

Spring AI 是 Spring 官方推出的AI应用开发框架,核心做了三件事:

  • 统一抽象层:把OpenAI、通义千问、DeepSeek、Ollama等不同模型提供商的API差异屏蔽掉,你换模型只改配置,不改代码。
  • 工程化封装:Prompt模板、结构化输出、Function Calling、RAG、向量存储……这些AI开发的高频需求,全部有开箱即用的API。
  • Spring原生集成:自动配置、依赖注入、Actuator监控,跟你写了十年的Spring Boot应用无缝融合。
  • 用一张图理解Spring AI的核心架构:

    关键洞察:这跟当年Spring Data JPA统一各种数据库的操作是一模一样的思路。Spring最擅长的事情就是——在混乱的API之上建立统一的抽象。


    二、项目初始化:3分钟跑起来

    2.1 创建项目

    使用 Spring Initializr(https://start.spring.io)创建项目,选择以下依赖:

    • Spring Boot 3.3.x(最低要求3.2+)
    • Spring Web
    • Spring AI DashScope Starter(通义千问)或 Spring AI OpenAI Starter

    如果你用Maven手动创建,核心POM配置如下:

    <!– 父POM:锁定Spring Boot版本 –>
    <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.3.5</version>
    <relativePath/>
    </parent>

    <properties>
    <java.version>17</java.version>
    <!– Spring AI版本(截至2024年底稳定版) –>
    <spring-ai.version>1.0.0-M4</spring-ai.version>
    </properties>

    <dependencies>
    <!– Spring Boot Web –>
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!– Spring AI BOM:统一管理所有AI相关依赖版本 –>
    <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-bom</artifactId>
    <version>${spring-ai.version}</version>
    <type>pom</type>
    <scope>import</scope>
    </dependency>

    <!– 接入通义千问(DashScope) –>
    <dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter</artifactId>
    <version>1.0.0-M3.2</version>
    </dependency>

    <!– 或者接入DeepSeek(兼容OpenAI协议) –>
    <dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
    </dependency>
    </dependencies>

    <!– Spring AI的Milestone仓库(必须加,否则拉不到包) –>
    <repositories>
    <repository>
    <id>spring-milestones</id>
    <name>Spring Milestones</name>
    <url>https://repo.spring.io/milestone</url>
    <snapshots><enabled>false</enabled></snapshots>
    </repository>
    </repositories>

    2.2 配置API Key

    在 application.yml 中配置:

    方式一:接入通义千问(DashScope)

    spring:
    ai:
    dashscope:
    api-key: ${AI_DASHSCOPE_API_KEY} # 从环境变量读取,别硬编码到代码里
    chat:
    options:
    model: qwen-plus # 模型名称:qwen-plus / qwen-turbo / qwen-max
    temperature: 0.7 # 温度:0~1,越高越发散

    方式二:接入DeepSeek(兼容OpenAI协议)

    spring:
    ai:
    openai:
    api-key: ${AI_DEEPSEEK_API_KEY}
    base-url: https://api.deepseek.com # DeepSeek的API地址
    chat:
    options:
    model: deepseek-chat # 或 deepseek-reasoner(R1推理模型)
    temperature: 0.7

    为什么DeepSeek用OpenAI的starter? 因为DeepSeek的API完全兼容OpenAI协议,只需要改 base-url 和 api-key 就行。这也是Spring AI统一抽象的威力——兼容OpenAI协议的模型,统统用同一个starter。

    三、5行代码接入大模型:ChatClient初体验

    3.1 最简版本

    @RestController
    public class ChatController {

    private final ChatClient chatClient;

    // 构造器注入,Spring AI自动配置已经帮你创建好了ChatClient.Builder
    public ChatController(ChatClient.Builder builder) {
    this.chatClient = builder.build();
    }

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
    return chatClient.prompt(message).call().content();
    }
    }

    就这5行核心代码(构造器注入 + 一行调用),启动项目后访问:

    http://localhost:8080/chat?message=用一句话解释什么是依赖注入

    你会收到类似这样的回复:

    依赖注入是一种设计模式,对象不自己创建依赖项,而是由外部容器将所需依赖"注入"进来,从而实现松耦合。

    一个GET请求,接入了大模型。 这就是Spring AI的入门体验。

    3.2 理解调用链路

    别急着往下走,我们先搞懂这行代码背后发生了什么:

    chatClient.prompt(message) // 1. 构建Prompt
    .call() // 2. 发起同步HTTP请求到模型API
    .content(); // 3. 提取文本内容(忽略token使用量等元数据)

    完整链路是这样的:

    你的代码 → ChatClient → ChatModel → 模型API(通义千问/DeepSeek)

    返回 ChatResponse

    .content() 提取纯文本

    ChatClient 是面向开发者的高级API(类似Spring Data的Repository),ChatModel 是底层模型接口。你日常开发90%的场景用 ChatClient 就够了。

    3.3 带系统提示词的对话

    实际业务中,你通常需要设定AI的角色和行为规则。Spring AI用链式API优雅地处理这个:

    @GetMapping("/assistant")
    public String assistant(@RequestParam String question) {
    return chatClient.prompt()
    // system消息:设定AI角色和行为约束
    .system("你是一个资深的Java技术顾问,回答简洁专业," +
    "每次回答不超过200字,代码示例用Java语言。")
    // user消息:用户实际输入
    .user(question)
    // 调用配置
    .call()
    // 获取结构化结果(包含内容、token用量等)
    .chatResponse()
    .getResult()
    .getOutput()
    .getContent();
    }

    对比一下如果你自己拼接OpenAI的JSON请求体,需要手动构造 messages 数组、区分 system 和 user 角色、解析返回的JSON……Spring AI把这些全封装了。

    3.4 多模型切换:只改配置不改代码

    这是Spring AI最值钱的设计。你写好的ChatController代码,切换模型只需要改配置:

    # 开发环境用通义千问(便宜)
    spring:
    ai:
    dashscope:
    api-key: ${AI_DASHSCOPE_API_KEY}
    chat:
    options:
    model: qwen-turbo

    # 生产环境切到DeepSeek(性价比高)
    # spring:
    # ai:
    # openai:
    # api-key: ${AI_DEEPSEEK_API_KEY}
    # base-url: https://api.deepseek.com
    # chat:
    # options:
    # model: deepseek-chat

    当然,如果你想在运行时动态切换模型(比如简单问题用turbo、复杂问题用max),可以通过注入多个 ChatModel 实现:

    @Service
    public class SmartChatService {

    private final ChatClient qwenClient; // 通义千问
    private final ChatClient deepseekClient; // DeepSeek

    public SmartChatService(
    @Qualifier("dashScopeChatModel") ChatModel qwenModel,
    @Qualifier("openAiChatModel") ChatModel deepseekModel) {
    this.qwenClient = ChatClient.builder(qwenModel).build();
    this.deepseekClient = ChatClient.builder(deepseekModel).build();
    }

    public String chat(String question, String model) {
    ChatClient client = "deepseek".equalsIgnoreCase(model)
    ? deepseekClient : qwenClient;
    return client.prompt(question).call().content();
    }
    }

    这种模型路由的思路在后面Day 89的综合项目里会详细展开,这里先埋个种子。


    四、进阶:结构化输出——让AI返回Java对象

    大模型默认返回的是自然语言文本,但在工程中,我们经常需要AI返回结构化数据(比如JSON),直接映射成Java对象。

    4.1 场景:智能情感分析

    假设你有一个用户评论分析系统,需要AI判断评论的情感倾向并返回结构化结果:

    // 定义输出结构(Java Record,JDK 16+)
    public record SentimentResult(
    String sentiment, // POSITIVE / NEGATIVE / NEUTRAL
    double confidence, // 置信度 0~1
    String summary // 一句话摘要
    ) {}

    @RestController
    public class SentimentController {

    private final ChatClient chatClient;

    public SentimentController(ChatClient.Builder builder) {
    this.chatClient = builder.build();
    }

    @PostMapping("/analyze")
    public SentimentResult analyze(@RequestBody Map<String, String> body) {
    String review = body.get("review");

    return chatClient.prompt()
    .system("你是一个情感分析专家。分析用户评论的情感倾向," +
    "返回POSITIVE/NEGATIVE/NEUTRAL,给出置信度和摘要。")
    .user("请分析以下评论:\\n" + review)
    .call()
    // 核心魔法:entity()方法自动将AI输出转换为指定类型的Java对象
    .entity(SentimentResult.class);
    }
    }

    调用示例:

    curl -X POST http://localhost:8080/analyze \\
    -H "Content-Type: application/json" \\
    -d '{"review": "这个ERP系统太好用了,库存管理清晰,报表功能强大,强烈推荐!"}'

    返回结果:

    {
    "sentiment": "POSITIVE",
    "confidence": 0.95,
    "summary": "用户对ERP系统的库存管理和报表功能给予高度评价。"
    }

    原理:entity() 方法底层使用了 BeanOutputConverter,它做了三件事:

  • 根据目标Java类型自动生成JSON Schema,追加到Prompt中告诉模型输出格式
  • 调用模型获取文本响应
  • 将文本解析为JSON并反序列化为Java对象
  • 这意味着你不需要写任何JSON解析代码,Spring AI帮你全搞定了。

    4.2 对比传统方案的痛点

    如果没有Spring AI,你需要:

    // 传统做法:手动拼接 + 手动解析
    String prompt = "分析以下评论的情感,返回JSON格式:" +
    "{\\"sentiment\\":\\"POSITIVE/NEGATIVE/NEUTRAL\\"," +
    "\\"confidence\\":0.0,\\"summary\\":\\"\\"}\\n" + review;

    String response = callLlmApi(prompt); // 自己封装HTTP调用

    // 手动解析JSON(还可能遇到模型不按格式返回的问题)
    ObjectMapper mapper = new ObjectMapper();
    SentimentResult result = mapper.readValue(response, SentimentResult.class);

    痛点很明显:Prompt和代码耦合、JSON格式不稳定、没有类型安全、换模型要改HTTP调用代码。Spring AI把这些问题全解决了。


    五、模型选型与成本对比

    很多开发者问我"该选哪个模型",我的建议是根据业务场景和预算来:

    维度通义千问 (qwen-plus)DeepSeek (deepseek-chat)GPT-4o
    中文能力 优秀 优秀 良好
    代码能力 良好 优秀(性价比之王) 优秀
    输入价格 ¥0.008/1K tokens ¥0.001/1K tokens ¥0.035/1K tokens
    输出价格 ¥0.02/1K tokens ¥0.002/1K tokens ¥0.07/1K tokens
    免费额度 有(新用户) 有(限时)
    上下文窗口 128K 128K 128K
    合规性 国内合规 国内合规 需评估

    价格数据为2024年底公开价格,实际以官方为准。

    我的选型建议:

    • 内部工具/原型验证:DeepSeek,便宜到几乎不花钱
    • 面向用户的中文场景:通义千问,中文表达自然
    • 复杂代码推理:DeepSeek-R1(推理模型)或GPT-4o
    • 数据敏感场景:后面Day 61会讲Ollama本地部署

    六、建议

    建议1:API Key永远不要硬编码

    // ❌ 错误:硬编码到代码里,一旦提交到Git就完蛋
    @Value("${spring.ai.dashscope.api-key}")
    private String apiKey = "sk-xxxxxxx";

    // ✅ 正确:用环境变量注入
    // application.yml中写 ${AI_DASHSCOPE_API_KEY}
    // 启动时:export AI_DASHSCOPE_API_KEY=sk-xxxxxxx

    更进一步,生产环境用Nacos配置中心或Vault管理密钥,不要放在代码仓库里。

    建议2:永远设置超时和重试

    大模型API的延迟远高于传统接口(通常2~10秒),不设超时会拖垮你的线程池:

    yaml

    复制

    spring:
    ai:
    dashscope:
    chat:
    options:
    model: qwen-plus
    # Spring AI的retry配置(M4+版本支持)
    retry:
    max-attempts: 3 # 最多重试3次
    backoff:
    initial-interval: 1000 # 首次重试等待1秒
    multiplier: 2 # 指数退避:1s → 2s → 4s

    同时,你的HTTP客户端(Tomcat/Undertow)也要确保超时时间大于模型的最大响应时间:

    server:
    tomcat:
    connection-timeout: 30000 # 30秒超时
    threads:
    max: 200 # 根据并发量调整

    建议3:从简单场景开始,不要一上来就RAG

    我见过太多团队一上来就搞RAG知识库,结果文档切分、向量检索、重排序一堆问题,项目延期两个月。

    正确的节奏是:

    第一步:ChatClient跑通基础对话(今天这篇)

    第二步:Prompt工程,让AI按你的规则输出(Day 27)

    第三步:结构化输出 + Function Calling,让AI调你的业务接口

    第四步:RAG,给AI喂你的私有知识(Day 71+)

    每一步都要确保上一步能稳定运行。AI应用开发的坑不在框架,而在调优和边界处理。


    下篇预告:Day 27 —— 《Spring AI Prompt工程:PromptTemplate/FewShot/角色系统》,我们将深入Prompt模板化管理,把"调教AI"这件事工程化、可版本管理地做起来。

    赞(0)
    未经允许不得转载:171主机测评 » Day26-Java核心+Spring生态篇:5行代码接入大模型Spring AI
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址