Java工程师转型AI工程师:2026完整学习路径与实战指南
Java工程师转型AI工程师:2026完整学习路径与实战指南
适读人群:有1-5年Java开发经验,想向AI工程师方向转型的开发者
阅读时长:约25分钟
文章价值:完整转型路线 + 可运行代码Demo + 避坑指南
先说一件真实的事
去年年底,我认识一个在某银行做了4年Java后端的工程师,叫老李。
他所在的团队接到了一个新项目——给行内客服系统接入AI能力。老板找到他:「你负责,两个月上线。」
老李慌了。他不会Python,看到「大模型」「RAG」「向量数据库」这些词就头大。他花了两周时间学Python,结果发现公司的基础设施全是Java栈,Spring Boot,MySQL,他写的Python代码根本没法融入进去。
一个月后,项目延期。老李背了锅。
后来他找到我,问有没有用Java做AI应用的方案。我把Spring AI的文档发给他,他看了半小时回我:「这不就是我熟悉的那套Spring嘛,为什么没人告诉我可以用Java做AI?」
这件事让我下决心写这篇文章。
为什么是现在?
2026年,AI工程师的需求已经从「加分项」变成「标配」。根据各大招聘平台数据,标注「AI」相关能力的Java岗位薪资溢价达到 40%-80%。
更重要的是:Java生态在AI领域的基础设施已经完善了。
- Spring AI 1.0 GA 已发布
- LangChain4J 持续迭代
- Spring AI Alibaba 对国内大模型做了完整适配
Java程序员转型AI工程师的门槛,前所未有地低。
但大多数人转型失败的原因是什么?
- 盲目学Python:花了3个月写爬虫和pandas,结果公司全是Java栈,白学了
- 只看理论不写代码:看完100篇RAG原理文章,打开IDEA不知道从哪开始
- 学习路径不清晰:今天看RAG,明天看Agent,东一榔头西一棒槌,无法形成体系
本文给出一条针对Java工程师的定制化转型路径,每个阶段都有可运行的代码。
转型全景图
第一阶段:LLM核心基础(4周)
你需要哪些理论?
作为Java工程师,你不需要从零学深度学习,不需要推导反向传播,不需要搞懂所有的数学公式。
你需要的是够用的理论 + 扎实的工程实践。
| 知识点 | 深度要求 | 推荐资源 |
|---|---|---|
| Transformer架构 | 理解原理,能讲清楚注意力机制 | Attention Is All You Need |
| Tokenization | 了解BPE、WordPiece,知道Token计费规则 | HuggingFace Tokenizers文档 |
| Fine-tuning vs RAG | 深度理解,能给出选型建议 | OpenAI Fine-tuning指南 |
| Prompt Engineering | 熟练掌握CoT、Few-shot | OpenAI Cookbook实战手册 |
| Embedding原理 | 理解向量空间语义,知道余弦相似度 | OpenAI Embeddings文档 |
Transformer注意力机制:用Java工程师的视角理解
很多文章把注意力机制讲得很玄乎。其实用工程师的视角理解很简单:
把注意力机制想象成一个Map查找:
- Query(Q)= 你要查的「key」
- Key(K)= Map里存的「key」
- Value(V)= Map里存的「value」
- 区别是:传统Map是精确匹配,注意力机制是「模糊加权匹配」
// 传统Map查找(精确匹配)
Map<String, String> map = new HashMap<>();
String result = map.get("java"); // 要么有要么没有
// 注意力机制(加权匹配,伪代码)
// 不是找「最像的那个」,而是「对所有Key算相似度,然后加权求和Value」
float[] similarities = computeSimilarity(query, allKeys); // Q·K^T / sqrt(d_k)
float[] weights = softmax(similarities); // 归一化成概率
float[] output = weightedSum(weights, allValues); // 加权求和Value一个关键的理解误区:
注意力机制不是「找最相似的Token」,而是「对所有Token加权聚合信息」。
这解释了为什么长文本会「遗忘」早期内容——不是真的遗忘,而是早期Token的权重被稀释了。这也是为什么RAG比长上下文更经济:与其把10万字全塞给模型,不如只检索最相关的1000字。
代码实战 Level 1:Hello World
先跑起来,再说别的。
Step 1:添加依赖
<!-- pom.xml -->
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 使用OpenAI,国内可替换为DeepSeek/通义千问 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
</dependencies>Step 2:配置
# application.yml
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com # 如用DeepSeek,改为 https://api.deepseek.com
chat:
options:
model: gpt-4o-mini # 省钱,调试够用
temperature: 0.7Step 3:最简单的调用
// 这是你的第一个AI调用,就这么简单
@RestController
@RequiredArgsConstructor
public class HelloAiController {
private final ChatClient chatClient;
@GetMapping("/ask")
public String ask(@RequestParam String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}
// 启动后访问:http://localhost:8080/ask?question=你好跑起来了吗? 如果报错,90%是API Key或网络问题,检查环境变量是否正确设置。
代码实战 Level 2:流式输出 + 角色设定
用户等一个答案等10秒,和看着文字一个一个蹦出来,体验是天差地别的。
@Service
public class ChatService {
private final ChatClient chatClient;
// 注意这里在构造时就设置了系统角色
public ChatService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("""
你是一位专业的Java技术顾问,有10年以上的Java开发经验。
回答时请结合具体的代码示例,语言简洁专业。
如果问题超出Java技术范畴,请礼貌地说明。
""")
.build();
}
// 普通调用(适合短回答)
public String chat(String message) {
return chatClient.prompt()
.user(message)
.call()
.content();
}
// 流式调用(适合长回答,用户体验更好)
public Flux<String> chatStream(String message) {
return chatClient.prompt()
.user(message)
.stream()
.content();
}
}@RestController
@RequiredArgsConstructor
public class ChatController {
private final ChatService chatService;
// 普通接口
@PostMapping("/chat")
public String chat(@RequestBody ChatRequest request) {
return chatService.chat(request.message());
}
// 流式接口 - 注意produces类型
@PostMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestBody ChatRequest request) {
return chatService.chatStream(request.message());
}
record ChatRequest(String message) {}
}代码实战 Level 3:多轮对话(有记忆的聊天)
前面两个Level是「无状态的」——每次调用都是全新的,AI不记得上次聊了什么。
真实的聊天应用需要「有记忆」:
@Service
public class ConversationService {
private final ChatClient chatClient;
// 用Map模拟会话存储,生产环境用Redis
private final Map<String, List<Message>> sessions = new ConcurrentHashMap<>();
public ConversationService(ChatClient.Builder builder) {
this.chatClient = builder
.defaultSystem("你是一位耐心的Java技术助手,能够记住对话上下文。")
.build();
}
public String chat(String sessionId, String userMessage) {
// 1. 获取或创建会话历史
List<Message> history = sessions.computeIfAbsent(sessionId, k -> new ArrayList<>());
// 2. 加入用户消息
history.add(new UserMessage(userMessage));
// 3. 调用AI,携带完整历史
String response = chatClient.prompt()
.messages(history) // 关键:传入历史消息
.call()
.content();
// 4. 保存AI回复到历史
history.add(new AssistantMessage(response));
// 5. 控制历史长度,避免Token超限(保留最近20条)
if (history.size() > 20) {
history.subList(0, history.size() - 20).clear();
}
return response;
}
// 清除会话
public void clearSession(String sessionId) {
sessions.remove(sessionId);
}
}测试一下记忆效果:
# 第一轮
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{"sessionId": "user-001", "message": "我叫老李,是一名Java工程师"}'
# 第二轮(AI应该记得你叫老李)
curl -X POST http://localhost:8080/chat \
-H "Content-Type: application/json" \
-d '{"sessionId": "user-001", "message": "你还记得我叫什么吗?"}'第二阶段:Spring AI核心技能(4周)
Spring AI是Java工程师进入AI领域最自然的入口。它用你熟悉的Spring生态封装了所有复杂的AI调用逻辑。
Spring AI核心架构
Spring AI最核心的设计思想是抽象与实现分离——你的代码只依赖接口,底层换什么大模型都不需要改业务逻辑。
// 好的写法:依赖抽象
private final ChatModel chatModel; // 接口,不关心底层是OpenAI还是DeepSeek
// 不好的写法:依赖具体实现
private final OpenAiChatModel openAiChatModel; // 绑死了,换模型要改代码4周学习计划
| 周次 | 学习内容 | 实践项目 | 验收标准 |
|---|---|---|---|
| 第1周 | ChatClient、流式输出、多轮对话 | 搭建本地AI问答Demo | 能实现带记忆的聊天接口 |
| 第2周 | EmbeddingModel、VectorStore | 实现简单语义搜索 | 能把文本转向量并检索 |
| 第3周 | RAG完整流水线、文档加载 | 构建文档问答系统 | 能回答上传文档中的问题 |
| 第4周 | Function Calling、Advisor | 工具调用天气查询Bot | 能让AI自动调用外部API |
Function Calling:让AI主动调用你的Java方法
这个功能很多人不知道,但非常实用。你可以把Java方法「告诉」AI,AI会在需要的时候自动调用它。
// 定义工具类
@Component
public class WeatherTools {
// 用注解告诉AI这个方法是什么、什么时候用
@Tool(description = "根据城市名查询今日天气信息,包括温度、湿度、天气状况")
public WeatherInfo getWeather(
@ToolParam(description = "城市名,如:北京、上海、深圳") String city) {
// 实际项目里调用天气API,这里用mock数据
return new WeatherInfo(city, "晴", 28, 65);
}
@Tool(description = "查询指定城市未来7天的天气预报")
public List<WeatherInfo> getWeatherForecast(String city) {
// mock 7天预报
return IntStream.range(0, 7)
.mapToObj(i -> new WeatherInfo(city, i % 2 == 0 ? "晴" : "多云", 25 + i, 60))
.toList();
}
record WeatherInfo(String city, String condition, int temperature, int humidity) {}
}@Service
@RequiredArgsConstructor
public class WeatherBotService {
private final ChatClient.Builder chatClientBuilder;
private final WeatherTools weatherTools;
public String ask(String question) {
return chatClientBuilder.build()
.prompt()
.system("你是一个天气助手,可以查询城市天气。用中文回答,语气友好。")
.user(question)
.tools(weatherTools) // 注入工具
.call()
.content();
}
}测试效果:
问:北京今天天气怎么样?适合出门跑步吗?
AI自动调用 getWeather("北京"),然后回答:
「北京今天天气晴,气温28℃,湿度65%,非常适合户外跑步!
建议早上7-9点或傍晚6-8点出门,避开正午高温时段。」第三阶段:RAG系统开发(4周)
RAG(检索增强生成)是目前企业AI应用最主流的技术方案,也是面试必考点。
为什么需要RAG?
大模型的知识有截止日期,也不知道你公司内部的业务知识。RAG的思路是:「先从知识库里找到相关内容,再让AI基于这些内容回答」——就像开卷考试。
RAG完整架构
代码实战 Level 1:最简单的RAG
先把流程跑通,再优化细节。
<!-- 需要额外添加的依赖 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
<!-- 或者用内存版(不需要数据库,适合快速验证)-->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-simple-vector-store</artifactId>
</dependency>@Service
@RequiredArgsConstructor
public class SimpleRagService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
// 入库:把文档存入向量数据库
public void ingest(String content, String source) {
Document doc = new Document(content, Map.of("source", source));
vectorStore.add(List.of(doc));
System.out.println("已入库:" + source);
}
// 检索:根据问题找相关文档,然后让AI回答
public String query(String question) {
// 1. 向量检索
List<Document> docs = vectorStore.similaritySearch(
SearchRequest.query(question).withTopK(3)
);
if (docs.isEmpty()) {
return "知识库中没有相关信息,请换个问题试试。";
}
// 2. 拼接上下文
String context = docs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
// 3. 让AI回答
return chatClient.prompt()
.system("你是知识库助手,只根据提供的资料回答问题,不要编造信息。")
.user("参考资料:\n" + context + "\n\n问题:" + question)
.call()
.content();
}
}代码实战 Level 2:生产级RAG
简单版本跑通后,你会遇到这些真实问题:
- 检索质量差:向量相似度高,但语义不匹配
- 答案不准:检索到了内容,但AI乱发挥
- Token超限:文档太大塞不进上下文
生产级版本需要处理这些问题:
@Service
@RequiredArgsConstructor
@Slf4j
public class ProductionRagService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
private final EmbeddingModel embeddingModel;
private static final String STRICT_PROMPT = """
你是专业的技术助手。请严格基于以下参考资料回答用户问题。
【参考资料】
{context}
【用户问题】
{question}
【回答要求】
1. 只基于参考资料中明确提到的内容回答
2. 如果资料中没有相关信息,明确说"根据现有资料,无法回答此问题"
3. 引用具体内容时,注明来自哪个资料
4. 不要推断、猜测或补充资料之外的信息
""";
public RagResponse query(String userQuestion) {
// 1. 检索相关文档(提高TopK,后面再过滤)
List<Document> candidates = vectorStore.similaritySearch(
SearchRequest.query(userQuestion)
.withTopK(10)
.withSimilarityThreshold(0.6) // 过滤低相关性文档
);
if (candidates.isEmpty()) {
log.warn("未检索到相关文档,问题:{}", userQuestion);
return RagResponse.noResult("知识库中暂无相关信息");
}
// 2. 按相关性分数排序,取前5条(避免Token超限)
List<Document> topDocs = candidates.stream()
.sorted(Comparator.comparingDouble(
doc -> -((Double) doc.getMetadata().getOrDefault("distance", 0.0))
))
.limit(5)
.toList();
// 3. 构建结构化上下文(带来源信息)
String context = IntStream.range(0, topDocs.size())
.mapToObj(i -> {
Document doc = topDocs.get(i);
String source = (String) doc.getMetadata().getOrDefault("source", "未知来源");
return String.format("[资料%d - 来源:%s]\n%s", i + 1, source, doc.getContent());
})
.collect(Collectors.joining("\n\n---\n\n"));
// 4. 调用LLM
String answer = chatClient.prompt()
.user(u -> u.text(STRICT_PROMPT)
.param("context", context)
.param("question", userQuestion))
.call()
.content();
// 5. 返回答案 + 来源(便于用户验证)
List<String> sources = topDocs.stream()
.map(doc -> (String) doc.getMetadata().getOrDefault("source", "未知"))
.distinct()
.toList();
return RagResponse.success(answer, sources);
}
// 文档入库(支持自动分块)
public void ingestDocument(String filePath) {
// 加载文档
Resource resource = new FileSystemResource(filePath);
TikaDocumentReader reader = new TikaDocumentReader(resource);
List<Document> documents = reader.get();
// 分块(每块500字,重叠50字)
TokenTextSplitter splitter = new TokenTextSplitter(500, 50, 5, 10000, true);
List<Document> chunks = splitter.apply(documents);
// 加入来源元数据
chunks.forEach(doc -> doc.getMetadata().put("source", filePath));
// 批量入库
vectorStore.add(chunks);
log.info("文档入库完成:{},共{}个分块", filePath, chunks.size());
}
public record RagResponse(boolean success, String answer, List<String> sources, String error) {
static RagResponse success(String answer, List<String> sources) {
return new RagResponse(true, answer, sources, null);
}
static RagResponse noResult(String message) {
return new RagResponse(false, null, List.of(), message);
}
}
}一个容易踩的坑:分块策略
// 坏的分块方式:按固定字符数切,会把一句话切断
// "我是一名Java工" | "程师,有5年经验"
// 语义被破坏,检索质量很差
// 好的分块方式:按语义分割(段落、标题)
// 段落1: "我是一名Java工程师,有5年经验。"
// 段落2: "主要负责后端微服务开发。"
// Spring AI的TokenTextSplitter会尽量在句子边界切分
TokenTextSplitter splitter = new TokenTextSplitter(
500, // 每块最大Token数
50, // 相邻块重叠Token数(避免边界信息丢失)
5, // 最小块Token数
10000, // 最大块Token数
true // 是否保留分隔符
);第四阶段:AI Agent开发(4周)
Agent是AI应用的未来。如果说RAG是「查资料回答问题」,那Agent就是「自主规划并完成复杂任务」。
一个直觉性的理解:
- 传统程序 = 按固定剧本执行
- RAG = 带着参考书做题
- Agent = 自己决定做什么、怎么做、用哪些工具
ReAct Agent执行流程
代码实战 Level 1:单工具Agent
@Component
public class SearchTool {
@Tool(description = "搜索互联网上的最新信息,适用于需要实时数据的问题")
public String search(
@ToolParam(description = "搜索关键词") String query) {
// 实际项目接入 Bing Search API 或 Tavily API
// 这里用mock数据演示
return mockSearch(query);
}
private String mockSearch(String query) {
// 模拟搜索结果
return String.format("搜索「%s」的结果:[模拟数据] 相关内容...", query);
}
}@Service
@RequiredArgsConstructor
public class SimpleAgentService {
private final ChatClient.Builder chatClientBuilder;
private final SearchTool searchTool;
public String run(String task) {
return chatClientBuilder.build()
.prompt()
.system("""
你是一位专业的技术研究助手。
当需要查询实时信息时,使用search工具。
思考过程:先分析任务 -> 决定是否需要搜索 -> 整合信息 -> 给出答案。
""")
.user(task)
.tools(searchTool)
.call()
.content();
}
}代码实战 Level 2:多工具复合Agent
真实的Agent往往需要多个工具协作:
@Component
public class CodeAnalysisTool {
@Tool(description = "分析Java代码,返回代码质量评分和改进建议")
public CodeAnalysisResult analyzeCode(
@ToolParam(description = "要分析的Java代码片段") String code) {
// 实际可以集成SonarQube API或调用另一个AI模型
int score = calculateScore(code);
List<String> suggestions = generateSuggestions(code);
return new CodeAnalysisResult(score, suggestions);
}
@Tool(description = "执行Java代码并返回运行结果(沙箱环境)")
public String executeCode(String code) {
// 注意:生产环境必须用沙箱隔离!
// 这里只是演示,实际用 Docker 或 JVM 沙箱
return "代码执行结果:[沙箱输出]";
}
private int calculateScore(String code) { return 85; }
private List<String> generateSuggestions(String code) {
return List.of("建议添加异常处理", "变量命名可以更清晰");
}
public record CodeAnalysisResult(int score, List<String> suggestions) {}
}@Service
public class TechAdvisorAgent {
private final ChatClient chatClient;
public TechAdvisorAgent(
ChatClient.Builder builder,
SearchTool searchTool,
CodeAnalysisTool codeAnalysisTool,
WeatherTools weatherTools) {
this.chatClient = builder
.defaultSystem("""
你是一位资深的Java技术顾问,拥有以下能力:
1. 搜索最新技术资讯(search工具)
2. 分析Java代码质量(analyzeCode工具)
3. 执行代码验证想法(executeCode工具)
解决问题的原则:
- 先理解问题,再决定用什么工具
- 可以串联多个工具完成复杂任务
- 最终给出清晰、可操作的建议
""")
.defaultTools(searchTool, codeAnalysisTool)
.build();
}
public String consult(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}测试一个复杂任务:
问:这段代码有什么问题?
public String getUserById(int id) {
String sql = "SELECT * FROM user WHERE id = " + id;
return jdbcTemplate.queryForObject(sql, String.class);
}
Agent执行链:
1. analyzeCode(代码) → 发现SQL注入风险,评分60
2. search("Spring JDBC SQL注入防范") → 找到正确写法
3. 整合信息 → 给出完整的修复建议和安全代码示例完整项目Demo:企业级智能客服系统
理论学完了,来看一个真实的项目是怎么把这些拼在一起的。
这是一个「基于产品文档的智能客服」,集成了前面所有阶段的知识:
项目结构:
customer-service-ai/
├── src/main/java/
│ ├── config/
│ │ ├── AiConfig.java # AI配置
│ │ └── VectorStoreConfig.java # 向量存储配置
│ ├── service/
│ │ ├── DocumentService.java # 文档管理
│ │ ├── RagService.java # RAG检索
│ │ └── CustomerServiceBot.java # 客服机器人
│ ├── controller/
│ │ ├── AdminController.java # 管理员接口(文档上传)
│ │ └── ChatController.java # 用户聊天接口
│ └── tools/
│ ├── OrderQueryTool.java # 查询订单工具
│ └── TicketTool.java # 创建工单工具// 核心的客服Bot,整合RAG + Agent能力
@Service
@RequiredArgsConstructor
@Slf4j
public class CustomerServiceBot {
private final ChatClient.Builder chatClientBuilder;
private final RagService ragService;
private final OrderQueryTool orderQueryTool;
private final TicketTool ticketTool;
// 每个会话独立的ChatClient(带记忆)
private final Map<String, ChatClient> sessionClients = new ConcurrentHashMap<>();
public String chat(String sessionId, String userId, String message) {
// 1. 从知识库检索相关信息
String knowledgeContext = ragService.retrieveContext(message);
// 2. 获取或创建带记忆的ChatClient
ChatClient client = sessionClients.computeIfAbsent(sessionId, id ->
chatClientBuilder
.defaultSystem(buildSystemPrompt(knowledgeContext))
.defaultTools(orderQueryTool, ticketTool)
.defaultAdvisors(
new MessageChatMemoryAdvisor(new InMemoryChatMemory())
)
.build()
);
// 3. 携带用户身份信息
return client.prompt()
.user(u -> u.text("用户ID:{userId}\n问题:{message}")
.param("userId", userId)
.param("message", message))
.call()
.content();
}
private String buildSystemPrompt(String knowledgeContext) {
return String.format("""
你是「产品助手小智」,负责解答用户关于产品使用的问题。
【产品知识】
%s
【服务原则】
1. 优先基于产品知识回答,不确定时坦诚说明
2. 如需查询订单,使用queryOrder工具
3. 问题复杂无法解决时,使用createTicket工具创建人工工单
4. 语气友好专业,用中文回答
""", knowledgeContext);
}
}// 订单查询工具
@Component
@RequiredArgsConstructor
public class OrderQueryTool {
private final OrderRepository orderRepository;
@Tool(description = "根据用户ID和订单号查询订单状态和详情")
public OrderInfo queryOrder(
@ToolParam(description = "用户ID") String userId,
@ToolParam(description = "订单号,如:ORD-2026-001") String orderId) {
log.info("查询订单:userId={}, orderId={}", userId, orderId);
return orderRepository.findByUserIdAndOrderId(userId, orderId)
.map(order -> new OrderInfo(
order.getOrderId(),
order.getStatus().getDescription(),
order.getTotalAmount(),
order.getCreateTime().toString()
))
.orElse(new OrderInfo(orderId, "订单不存在", null, null));
}
@Tool(description = "查询用户最近的订单列表,默认返回最近5条")
public List<OrderInfo> queryRecentOrders(
@ToolParam(description = "用户ID") String userId) {
return orderRepository.findTop5ByUserIdOrderByCreateTimeDesc(userId)
.stream()
.map(o -> new OrderInfo(o.getOrderId(), o.getStatus().getDescription(),
o.getTotalAmount(), o.getCreateTime().toString()))
.toList();
}
public record OrderInfo(String orderId, String status, BigDecimal amount, String createTime) {}
}转型时间轴总览
避坑指南:转型中最容易踩的10个坑
技术选型类:
不要用Python重写你的Java项目:Spring AI生态已经足够完善,用Java就能做到Python能做的所有AI工程工作。除非团队已全面转Python,否则坚守Java栈。
不要一开始就搞模型微调:Fine-tuning贵(算力成本)、慢(训练周期)、难维护(数据飞轮问题)。90%的企业场景用RAG就能解决,先把RAG做好。
不要忽视向量数据库选型:开发用SimpleVectorStore(内存)没问题,但生产环境要提前考虑规模:
- 小规模(< 100万向量):PGVector(你已有PostgreSQL就用这个)
- 中规模(百万级):Qdrant、Weaviate
- 大规模(亿级):Milvus
工程实践类:
不要忽视Prompt Engineering:70%的AI应用问题都出在Prompt上。一个好的System Prompt比换一个更好的模型更有效。
不要忽视可观测性:生产环境没有日志,出了问题根本不知道AI在想什么。推荐集成LangSmith或自建链路追踪。
不要低估Token成本:一次RAG查询可能消耗2000+ Token(系统提示 + 检索内容 + 问题 + 回答)。1000次查询/天 × 2000 Token = 200万Token,算清楚再上线。
不要用单线程处理AI调用:LLM响应时间长(2-10秒),必须用异步/响应式处理,否则线程池很快被打满。
面试准备类:
不要只会调用API,要理解原理:面试一定会问「为什么要分块?块大小怎么选?」「Embedding模型和Chat模型有什么区别?」这些问题不掌握原理答不上来。
不要没有真实项目经验:面试官会追问「你的RAG系统召回率是多少?怎么评估的?遇到过什么问题?」没有实战经验的回答会漏馅。
不要低估测试的重要性:AI应用的测试比传统应用更难——答案不是确定性的。要学会用评估集(Eval Set)来量化AI应用质量。
推荐学习资源
| 资源类型 | 推荐内容 |
|---|---|
| 官方文档 | Spring AI官方文档(必读) |
| GitHub | spring-projects/spring-ai 源码 |
| 论文 | Attention Is All You Need |
| 实战手册 | OpenAI Cookbook |
| 向量数据库 | PGVector官方文档 |
| 国内大模型 | 通义千问Spring AI Alibaba |
推荐阅读
如果这篇文章对你有帮助,这几篇是进阶的必读内容:
Spring AI实战:从零搭建企业级RAG系统(附完整代码)
文章连载中,欢迎关注公众号第一时间获取向量数据库选型指南:PGVector vs Qdrant vs Milvus,Java工程师该怎么选?
对比三种主流向量数据库的使用场景和性能差异AI Agent开发避坑:我在生产环境踩过的那些坑
从真实项目中总结的Agent开发经验LLM成本控制实战:如何把AI应用的Token消耗降低60%
Prompt优化、缓存策略、模型选型的完整方法论
以上文章持续更新中,在公众号「老张聊AI转型」回复关键词可获取最新链接。
写在最后
开头说的老李,后来怎么样了?
他用了3周时间,把Spring AI跑通,用Java把那个客服系统做出来了。项目上线后,人工客服的重复问题量下降了60%。他也因此在团队里建立了「AI工程师」的标签,年底涨薪20%。
转型AI工程师不是「从零开始」,而是「在你已有的Java基础上,叠加AI工程能力」。你掌握的并发编程、分布式系统、数据库优化……这些知识在构建可靠的AI系统时都是宝贵财富。
AI时代不会淘汰优秀的工程师,只会淘汰不愿进化的工程师。
