过去两年Java 后端工程师最常被问到的问题是你接触过大模型应用吗不少写惯了 Spring Boot CRUD 的同学开始焦虑担心手里的 Java 技能会贬值担心错过 AI 时代的机会。但我的判断很直接Java 后端转 AI 应用开发不是推倒重来而是把存量工程能力嫁接到新的技术底座上。模型训练看算法深度AI 应用开发看工程能力后者恰恰是 Java 后端的强项。AI 应用开发要解决的核心事情是把大模型提供的对话、理解、问答、推理能力封装成产品能力。面对一个AI 客服或智能知识库用户看到的仍然是一个接口、一个页面、一套权限和日志。落到代码层面依然是接口设计、数据存取、并发控制、缓存、监控这些后端基本功。区别在于你的系统中多了一个不可控的依赖——大模型很多传统的确定性思维需要调整。这篇文章会先回答Java 后端哪些技能可以带过来、哪些认知必须换掉然后给出 AI 应用开发新增的知识地图接着用四个实战示例把大模型对话接口、SSE 流式输出、RAG 知识库、Function Calling 这四块最常见的 AI 工程能力跑通最后给出一份三个月的转型学习路线和避坑清单。1. 转型的第一性原理AI 应用开发仍然是一种后端开发很多人一提到AI 开发立刻想到数学公式、Transformer、损失函数觉得自己基础不够就不敢迈步。这是对 AI 岗位的误解。AI 领域可以粗略分成三个方向方向核心工作对 Java 后端的要求算法研究模型结构、训练方法、论文复现低需要数学和算法功底模型训练与调优数据清洗、分布式训练、微调中需要 Python 和 GPU 运维经验AI 应用开发把大模型能力集成进业务系统高后端工程能力直接复用Java 后端转型应该瞄准第三个方向。从实际招聘情况看自研大模型的公司很少大部分中小自研公司需要的不是训练模型的人而是能把模型 API 接进现有业务的人。这些岗位往往就叫AI 应用开发或大模型应用开发要求里写得最多的是 Java、Spring Boot、REST API而不是 PyTorch。换句话说你在后端领域积累的接口能力、数据库设计能力、分布式系统经验在 AI 应用开发中不仅不会浪费反而变成了竞争优势。真正需要改变的是你对大模型的理解方式从前端调用一个数据库到后端调用一个概率模型工程确定性的边界变了。2. 存量技能迁移Java 后端的哪些能力可以直接复用先给结论大部分存量技能可以平移少部分思维方式需要修正。2.1 直接复用的工程能力已有技能在 AI 应用开发中的位置Spring Boot / Spring MVC继续承担 AI 服务的 API 层、鉴权层、网关层数据库设计 / MyBatis / JPA存储对话记录、用户配置、知识库元数据Redis 缓存缓存 Prompt 模板、模型结果、Embedding 结果消息队列异步化耗时的 AIGC 任务防止接口阻塞异常处理与日志跟踪模型调用失败、Token 消耗、链路耗时CI/CD 与自动化测试对模型输出做回归测试避免改动引发质量事故权限与安全API Key 管理、越权访问控制、敏感信息脱敏你完全可以继续用熟悉的 Spring Boot 搭建 AI 应用的后端而不是为了够潮去全面切换技术栈。2.2 必须修正的旧认知传统后端调用 MySQL 或 Redis结果几乎是确定的查询失败也有成熟的降级方案。大模型最大的不同是输出不确定。同一个 Prompt 问两次答案可能不同。输入稍微变化输出可能完全偏离预期。模型可能输出看似合理但实际错误的内容也就是幻觉。Token 数量不仅影响成本还决定请求是否会被拒绝。所以做 AI 应用开发不能只写调用完返回结果必须设计好输入输出规范、容错机制和人工兜底。这就是为什么很多 AI 应用开发岗位强调工程经验而不是算法能力。一个写过评分规则、幂等设计、降级策略的后端能天然意识到模型挂了我该怎么办。3. 新增知识地图AI 应用开发需要你补什么如果说后端工程能力是存量那下面这几个概念就是转型必须补齐的新增量。3.1 大模型 API 的基本形态目前主流大模型服务商提供的 API 大多基于同一种交互范式你提交一个消息列表模型返回一个补全文本。{ model: your-model-name, messages: [ {role: system, content: 你是一个 Java 开发助手}, {role: user, content: 用 Java 实现一个连接池} ], temperature: 0.7, stream: false }这里最简单也最重要的概念是消息角色system里是给模型的系统指令决定了它扮演什么角色user是用户输入后续还有可能出现的assistant代表模型历史回复。多轮对话的本质就是不断把这个消息列表追加变长。3.2 Embedding 与向量检索Embedding 是把文本转成一组浮点数向量的过程。文本语义越接近向量在空间里的距离越近。它解决的一个经典问题是用户提问的措辞和知识库原文不一样怎么找到相关内容传统 SQL 的LIKE只能做关键词匹配很难理解同义表达。向量检索可以做到意思相近就能查到。这一能力是 RAG 的基础。3.3 RAG检索增强生成RAG 全称 Retrieval-Augmented Generation核心思路是让模型回答问题之前先从一个外部知识库里检索相关资料把资料拼进 Prompt再让模型基于资料生成回答。它能缓解模型知识陈旧、专业领域回答不准、幻觉严重的问题。3.4 Function Calling 与 AgentFunction Calling 让模型在回答问题的过程中输出一个结构化的函数调用请求由后端去执行真实函数再把结果回传给模型生成最终回答。它把大模型从一个只能说话的系统变成了能调用工具的系统。Agent 可以理解为一个循环模型决定调用哪个工具后端执行工具把结果返回给模型模型继续推理。这是当前 AI 应用开发的进阶方向比如让模型查天气、查库存、操作数据库。3.5 Token 与成本Token 是大模型处理文本的最小单位一个汉字通常对应一个或两个 Token。请求和响应里的每个 Token 都要计费而且输入 Token 和输出 Token 价格不同。后端工程师必须养成估算 Token 的习惯否则一个不限制历史长度的多轮对话能在几天内烧掉大量费用。4. 实战一Spring Boot 调通大模型对话接口先跑通最小闭环不扯复杂架构。假设你已经有一个 Spring Boot 工程基础依赖已经按常规方式引入Web、Validation、Jackson。4.1 配置文件在src/main/resources/application.yml中增加大模型相关配置llm: api-key: ${LLM_API_KEY:your-api-key} base-url: https://your-llm-provider.example.com/v1 model: your-model-name max-tokens: 2048 temperature: 0.7说明不同服务商的base-url可能不同有些还需要额外传递X-DashScope、appid之类的字段。文中以通用的Bearer Token鉴权为例你只需要把api-key、base-url、model替换成实际服务商的值。4.2 配置读取类src/main/java/com/example/ai/config/LLMProperties.javapackage com.example.ai.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; Component ConfigurationProperties(prefix llm) public class LLMProperties { private String apiKey; private String baseUrl; private String model; private Integer maxTokens 2048; private Double temperature 0.7; public String getApiKey() { return apiKey; } public void setApiKey(String apiKey) { this.apiKey apiKey; } public String getBaseUrl() { return baseUrl; } public void setBaseUrl(String baseUrl) { this.baseUrl baseUrl; } public String getModel() { return model; } public void setModel(String model) { this.model model; } public Integer getMaxTokens() { return maxTokens; } public void setMaxTokens(Integer maxTokens) { this.maxTokens maxTokens; } public Double getTemperature() { return temperature; } public void setTemperature(Double temperature) { this.temperature temperature; } }4.3 对话服务src/main/java/com/example/ai/chat/ChatService.javapackage com.example.ai.chat; import com.example.ai.config.LLMProperties; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.*; Service public class ChatService { private final RestTemplate restTemplate new RestTemplate(); private final ObjectMapper objectMapper new ObjectMapper(); private final LLMProperties props; public ChatService(LLMProperties props) { this.props props; } public String chat(String userMessage) throws Exception { MapString, Object body new HashMap(); body.put(model, props.getModel()); body.put(messages, List.of( Map.of(role, system, content, 你是一个 Java 开发助手回答要简洁、准确。), Map.of(role, user, content, userMessage) )); body.put(temperature, props.getTemperature()); body.put(max_tokens, props.getMaxTokens()); HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(props.getApiKey()); HttpEntityMapString, Object request new HttpEntity(body, headers); ResponseEntityJsonNode response restTemplate.exchange( props.getBaseUrl() /chat/completions, HttpMethod.POST, request, JsonNode.class ); return response.getBody() .path(choices) .path(0) .path(message) .path(content) .asText(); } }这里用最标准的方式完成了一次非流式对话请求。核心是构造model、messages、temperature三个字段通过Authorization: Bearer xxx鉴权然后从返回结构choices[0].message.content中取出模型回答。4.4 测试接口src/main/java/com/example/ai/chat/ChatController.javapackage com.example.ai.chat; import org.springframework.web.bind.annotation.*; import java.util.Map; RestController RequestMapping(/api/chat) public class ChatController { private final ChatService chatService; public ChatController(ChatService chatService) { this.chatService chatService; } PostMapping public MapString, String chat(RequestBody MapString, String req) throws Exception { String answer chatService.chat(req.get(message)); return Map.of(answer, answer); } }启动服务后用 curl 验证mvn spring-boot:runcurl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 用 Java 写一个死锁例子}预期返回一段 JSONanswer字段里是模型生成的 Java 代码和解释。走到这一步你已经完成了 Java 后端接入大模型的最小闭环。5. 实战二SSE 流式输出实现打字机效果非流式请求会等模型生成完所有内容才返回用户体验很差。生产环境更常用的做法是流式输出模型生成一小段服务器就往浏览器推一小段效果类似 ChatGPT 的逐字回复。5.1 流式请求的核心原理大模型 API 在streamtrue时会通过 Server-Sent EventsSSE连续返回多行文本。每行格式通常是data: {choices:[{delta:{content:你}}]} data: {choices:[{delta:{content:好}}]} data: [DONE]后端要做的事情就是读取这些data:开头的行解析其中的内容再通过类似 Spring MVC 的SseEmitter转发给前端。5.2 StreamChatService 实现为了让原理更清晰这里不引入 WebFlux直接用 JDK 11 自带的java.net.http.HttpClient读取 SSE 流并用 Project Reactor 的Flux做响应式封装。如果你对响应式不熟也没关系整个模式很直观。package com.example.ai.stream; import com.example.ai.config.LLMProperties; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.stereotype.Service; import reactor.core.publisher.Flux; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.util.List; import java.util.Map; import java.util.stream.Stream; Service public class StreamChatService { private final LLMProperties props; private final HttpClient httpClient HttpClient.newHttpClient(); private final ObjectMapper objectMapper new ObjectMapper(); public StreamChatService(LLMProperties props) { this.props props; } public FluxString streamChat(String userMessage) { return Flux.create(sink - { new Thread(() - { try { MapString, Object body Map.of( model, props.getModel(), messages, List.of( Map.of(role, user, content, userMessage) ), stream, true ); HttpRequest request HttpRequest.newBuilder() .uri(URI.create(props.getBaseUrl() /chat/completions)) .header(Authorization, Bearer props.getApiKey()) .header(Content-Type, application/json) .POST(HttpRequest.BodyPublishers.ofString( objectMapper.writeValueAsString(body))) .build(); HttpResponseStreamString response httpClient.send(request, HttpResponse.BodyHandlers.ofLines()); response.body() .filter(line - line.startsWith(data:)) .forEach(line - { String data line.substring(5).trim(); if ([DONE].equals(data)) { sink.complete(); } else { sink.next(data); } }); } catch (Exception e) { sink.error(e); } }).start(); }); } }这样做的原因是HttpResponse.BodyHandlers.ofLines()返回一个延迟读取的StreamString流式响应体中的每一行都会在迭代时逐个到达。把整个读取过程放在新线程中是为了不让阻塞式读取卡住主题线程。5.3 Controller 层用 SseEmitter 转发src/main/java/com/example/ai/chat/StreamChatController.javapackage com.example.ai.chat; import com.example.ai.stream.StreamChatService; import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; RestController RequestMapping(/api/chat) public class StreamChatController { private final StreamChatService streamChatService; public StreamChatController(StreamChatService streamChatService) { this.streamChatService streamChatService; } GetMapping(value /stream, produces MediaType.TEXT_EVENT_STREAM_VALUE) public SseEmitter stream(RequestParam String message) { SseEmitter emitter new SseEmitter(120000L); streamChatService.streamChat(message).subscribe( data - { try { emitter.send(data); } catch (Exception e) { emitter.completeWithError(e); } }, emitter::completeWithError, emitter::complete ); return emitter; } }验证方式curl -N http://localhost:8080/api/chat/stream?message用Java写一个冒泡排序-N表示不等待响应结束实时打印。如果看到内容一段一段输出说明 SSE 链路已经通。6. 实战三从零实现最小 RAG 知识库助手RAG 是 Java 后端最容易上手、也最容易引起业绩效果的 AI 应用本质是解决模型不知道你公司内部资料的问题。6.1 RAG 的整体链路环节做什么对应组件离线索引切分文档、向量化、存入向量库文本切分 Embedding API在线检索把用户问题向量化在库里找相似片段Embedding API 向量检索生成回答把检索到的资料拼入 Prompt再让模型总结大模型对话 API6.2 文本切分原始文档不能整篇塞进 Prompt因为 Token 有限且成本高。实现一个最简单的按字符长度切分器带重叠避免把上下文切断package com.example.ai.rag; import java.util.ArrayList; import java.util.List; public class TextSplitter { public static ListString split(String text, int chunkSize, int overlap) { ListString chunks new ArrayList(); int start 0; while (start text.length()) { int end Math.min(start chunkSize, text.length()); chunks.add(text.substring(start, end)); start end - overlap; } return chunks; } }6.3 Embedding 客户端调用大模型服务商的/embeddings接口把文本向量化package com.example.ai.rag; import com.example.ai.config.LLMProperties; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate; import java.util.List; import java.util.Map; Service public class EmbeddingClient { private final LLMProperties props; private final RestTemplate restTemplate new RestTemplate(); private final ObjectMapper objectMapper new ObjectMapper(); public EmbeddingClient(LLMProperties props) { this.props props; } public float[] embed(String text) throws Exception { MapString, Object body Map.of( model, props.getModel(), input, text ); HttpHeaders headers new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); headers.setBearerAuth(props.getApiKey()); HttpEntityMapString, Object request new HttpEntity(body, headers); ResponseEntityJsonNode response restTemplate.exchange( props.getBaseUrl() /embeddings, HttpMethod.POST, request, JsonNode.class ); JsonNode array response.getBody() .path(data) .path(0) .path(embedding); float[] result new float[array.size()]; for (int i 0; i array.size(); i) { result[i] (float) array.get(i).asDouble(); } return result; } }注意很多服务商对 Embedding 会单独提供模型未必与对话模型是同一个生产环境可以在配置里增加llm.embedding-model单独设置。6.4 向量存储与检索为了不引入额外中间件先用一个内存版向量库演示核心思路。它保存文本片段和对应的向量检索时计算余弦相似度package com.example.ai.rag; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; public class SimpleVectorStore { public record DocumentChunk(String content, float[] embedding) {} private record ScoredChunk(DocumentChunk chunk, double score) {} private final ListDocumentChunk chunks new CopyOnWriteArrayList(); public void add(String content, float[] embedding) { chunks.add(new DocumentChunk(content, embedding)); } public ListDocumentChunk search(float[] queryEmbedding, int topK) { return chunks.stream() .map(chunk - new ScoredChunk( chunk, cosineSimilarity(queryEmbedding, chunk.embedding()))) .sorted(Comparator.comparingDouble(ScoredChunk::score).reversed()) .limit(topK) .map(ScoredChunk::chunk) .toList(); } private double cosineSimilarity(float[] a, float[] b) { double dot 0, normA 0, normB 0; for (int i 0; i a.length; i) { dot a[i] * b[i]; normA a[i] * a[i]; normB b[i] * b[i]; } return dot / (Math.sqrt(normA) * Math.sqrt(normB)); } }生产环境要换成 Milvus、Redis Stack、Elasticsearch、PGVector 这类有持久化能力的向量存储方案但上面的相似度检索逻辑是一致的。6.5 组装 RAG 服务package com.example.ai.rag; import com.example.ai.chat.ChatService; import org.springframework.stereotype.Service; import java.util.List; import java.util.stream.Collectors; Service public class RagService { private final ChatService chatService; private final EmbeddingClient embeddingClient; private final SimpleVectorStore vectorStore; public RagService(ChatService chatService, EmbeddingClient embeddingClient, SimpleVectorStore vectorStore) { this.chatService chatService; this.embeddingClient embeddingClient; this.vectorStore vectorStore; } public void ingest(String text) throws Exception { ListString chunks TextSplitter.split(text, 500, 50); for (String chunk : chunks) { vectorStore.add(chunk, embeddingClient.embed(chunk)); } } public String answer(String question) throws Exception { float[] queryEmbedding embeddingClient.embed(question); ListSimpleVectorStore.DocumentChunk docs vectorStore.search(queryEmbedding, 3); String context docs.stream() .map(SimpleVectorStore.DocumentChunk::content) .collect(Collectors.joining(\n\n)); String prompt 请根据下面提供的资料回答用户问题。 如果资料中没有相关信息请直接说明“未找到相关资料”不要编造。 【资料】 %s 【问题】 %s .formatted(context, question); return chatService.chat(prompt); } }这个示例清晰说明了 RAG 的本质先检索后增强再生成。很多号称私有大模型问答系统的产品底子就是这套链路。你需要的工程能力是文档切分策略、向量库选型、检索质量评估而不是自己去训练模型。7. 实战四Function Calling 与最小 Agent 循环7.1 一次工具调用的完整过程用户问北京今天适合穿什么模型无法实时获取天气但它可以输出一个函数调用意图{ choices: [{ message: { role: assistant, content: null, tool_calls: [{ id: call_123, function: { name: getWeather, arguments: {\city\:\北京\} } }] }, finish_reason: tool_calls }] }后端拿到这个结构化结果后执行真实的天气查询函数再把结果以roletool的消息回传模型会根据真实结果生成最终回答。这就是 Agent 循环的最小形态。7.2 工具定义把下面 JSON 放在请求体的tools数组里{ type: function, function: { name: getWeather, description: 查询指定城市的天气情况, parameters: { type: object, properties: { city: { type: string, description: 城市名例如北京 } }, required: [city] } } }工具定义的关键是写清楚函数用途和参数含义模型根据描述决定是否调用。参数描述越明确模型越不容易猜错。7.3 Agent 循环核心逻辑public String chatWithTool(String userMessage) throws Exception { ListMapString, Object messages new ArrayList(); messages.add(Map.of(role, user, content, userMessage)); // Agent 循环最大轮数防止模型反复调用工具不收敛 for (int step 0; step 3; step) { // 调用大模型带上 tools 定义 ChatResponse response chatClient.chatWithTools(messages, List.of(getWeatherToolDefinition())); MapString, Object assistantMsg response.getChoiceMessage(); messages.add(assistantMsg); ListToolCall toolCalls response.getToolCalls(); if (toolCalls.isEmpty()) { return response.getContent(); } // 执行工具并回传结果 for (ToolCall call : toolCalls) { String result executeTool(call.getName(), call.getArguments()); messages.add(Map.of( role, tool, tool_call_id, call.getId(), content, result )); } } return Agent 调用次数超限请简化问题; }这里的ChatResponse、ToolCall是按响应格式做的一层 Java 封装实际请求还是复用第 4 章的 HTTP 调用方法只需要把tools和tool_choiceauto加进请求体就行。executeTool方法里要做三件事解析工具参数、执行真实业务逻辑、返回字符串结果。这个模式在生产项目中非常有用比如让模型查询订单、计算库存、操作工单系统。但要注意工具函数一旦涉及数据修改或删除必须做权限校验和操作审计不能因为模型只是调接口就跳过后端安全控制。8. 转型期常见问题与排查思路Java 后端接入大模型时遇到的绝大多数问题都不是算法问题而是工程问题。下面总结最常踩的几类坑。问题现象可能原因排查方式解决方案API 请求超时模型推理慢或网络链路不稳定查看接口耗时和错误日志设置合理超时时间并重试优先改用流式返回 400 错误请求体字段错误或 Token 超限打印完整请求 JSON对照服务商文档检查 model、messages 结构Token 超长被拒多轮对话积累了过多历史消息统计请求体 Token 数量实现历史裁剪、摘要压缩限制 max_tokensJSON 解析失败模型输出不规则或响应结构被截断打印原始响应体改用函数调用或结构化输出校验响应完整性流式输出中途断开客户端关闭、网关超时、模型侧中断查看服务端异常和 SseEmitter 完成状态增加心跳、缩短空闲超时、做好错误补偿回答不稳定temperature 设置过高固定一组测试输入反复调用降低 temperature增加 Few-shot 示例成本快速上涨没有限制 Token 和调用频率查看日志中的 token usage增加缓存、限流、每日预算告警排查路径写一个通用顺序先看请求是否能复现再看原始响应最后看日志里的耗时和 Token 用量。很多问题通过打印请求 JSON 和响应 JSON 就能定位不要一上来就怀疑模型能力。9. 从 Java 后端到 AI 应用开发的三个月学习路线很多后端同学问我应该先学 Python 吗我的建议是如果你专注 AI 应用开发可以把 Python 放到第二位先把 Java 侧的大模型 API 调用、RAG、Function Calling 跑起来。下面这份路线不需要辞职利用工作日晚上的两小时和周末半天即可完成。9.1 第一阶段跑通最小链路第 1 到第 2 周选择一个国内可稳定访问的大模型 API 服务商注册账号拿到 API Key。用 Postman 或 curl 直接调通/chat/completions接口理解请求和响应结构。按第 4 章的示例在 Spring Boot 工程里完成对话接口。记录下来你踩过的每个坑包括鉴权、编码、超时、JSON 结构变化。验收标准本地起服务POST 一条消息能得到模型回答。9.2 第二阶段补齐 AI 工程核心能力第 3 到第 8 周实现 SSE 流式输出把第 5 章的代码整合进你的 Controller。选一份自己的文档实现第 6 章的 RAG 流程体验检索质量对最终答案的影响。实现第 7 章的 Function Calling 和最小 Agent 循环做一个能查天气或查时间的工具示例。学习向量数据库的基本用法了解切分粒度、重叠大小、topK 对效果的影响。验收标准你有三个可演示的 Demo流式对话、文档问答、带工具调用的 Agent。9.3 第三阶段生产化与业务落地第 9 到第 12 周把某一个 AI 能力接进当前团队的业务系统比如工单自动分类、文档搜索助手、周报生成。重点解决成本问题加缓存、做历史裁剪、埋 Token 用量日志、设置告警。建立模型输出评测集准备几十条固定输入记录输出质量和耗时每次调整 Prompt 后都做回归对比。关注安全边界Prompt 注入防护、API Key 管理、低权限工具函数、敏感信息脱敏。验收标准有一个真实用户可用的 AI 功能上线你能说清楚它的成本、QPS 和失败兜底方案。10. 写在最后Java 后端转 AI 应用开发最忌讳的是把大量时间花在学算法理论上结果做个 Demo 都还要查文档。真正值钱的是把大模型能力稳定地嵌入业务系统的工程能力。模型会变API 会变但接口设计、数据存储、缓存、监控、成本控制、安全边界这些底层工程思维不会变。从今天开始建议你只做三件事第一注册一个大模型 API 账号用现有 Spring Boot 项目调通一次第二挑选一个团队里最耗时的内容型任务尝试用 RAG 或 Agent 去做一个高成本原型第三把结果记录成一篇技术文档或博客输出是最好的学习方式。如果你正拿着这份路线犹豫不用等到准备好再开始。大模型时代最大的红利不是追新模型而是让普通后端工程师也能低成本地拥有智能能力。趁现在把你手里那个最熟悉的 Spring Boot 工程接入你的第一个大模型接口。