
思否收录 · Java工程师转型AI实战系列
2026年,一个让Java开发者既兴奋又焦虑的事实正在发生:
Java生态的AI工具链,在2025年底实现了"跨越式"成熟。
这意味着什么?
意味着Java工程师不再需要"转行Python"才能做AI开发。你现有的Spring Boot、微服务、高并发经验,本身就是AI Agent开发中的差异化竞争力——尤其是需要与企业现有系统集成时。
但另一个现实是:大量Java开发者对AI开发的认知还停留在"调API"层面。而真正高薪的岗位,要求的是构建生产级AI Agent系统的能力——包括工具调用、记忆管理、多Agent协作、可观测性等。
本文将从Java工程师的视角出发,系统讲解如何利用Java生态构建生产级AI Agent应用,全程实战、无Python依赖、可直接落地。全文约8000字,覆盖从环境搭建到生产部署的全链路。
维度 | Python AI生态 | Java AI生态(2026) | 结论 |
|---|---|---|---|
框架成熟度 | 成熟但碎片化 | Spring AI + LangChain4j 双雄并立 | Java正在追赶 |
性能 | GIL限制并发 | 虚拟线程 + 原生编译 | Java胜出 |
企业集成 | 需要额外适配层 | 原生Spring生态 | Java胜出 |
模型支持 | 最全 | 主流模型全部支持 | 持平 |
学习曲线 | 平缓(语法简单) | 陡峭(需要Spring基础) | Python略优 |
生产部署 | 需要大量运维 | 标准Java部署流程 | Java胜出 |
高薪岗位 | 多(但竞争激烈) | 快速增加(供给不足) | Java机会更大 |
优势一:与现有系统的"无缝焊接"
大多数企业已有Spring Boot微服务架构。在Java生态中构建AI Agent,天然可以:
优势二:虚拟线程彻底改变并发模型
// Java 21+ 虚拟线程让高并发AI调用变得极其简单
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var futures = tasks.stream()
.map(task -> executor.submit(() -> callAI(task)))
.toList();
// 可以轻松处理数万个并发AI请求
var results = futures.stream()
.map(Future::get)
.toList();
}优势三:GraalVM原生编译
2026年,Spring Boot 3.x + GraalVM已经成熟,可以将Agent应用编译为原生镜像,启动时间从秒级降至毫秒级,内存占用降低60%+。
┌─────────────────────────────────────────────────────────────────────┐
│ Java AI Agent 技术栈 │
│ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 应用层(业务代码) │ │
│ │ Controller / Service / Repository │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ AI Agent 框架层 │ │
│ │ ┌──────────────┐ ┌──────────────┐ │ │
│ │ │ LangChain4j │ │ Spring AI │ │ │
│ │ │ (Apache顶级) │ │ (Spring官方) │ │ │
│ │ └──────────────┘ └──────────────┘ │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 模型接入层 │ │
│ │ OpenAI / 通义千问 / DeepSeek / Ollama / HuggingFace │ │
│ └─────────────────────────────────────────────────────────────┘ │
│ │ │
│ ┌─────────────────────────────────────────────────────────────┐ │
│ │ 基础设施层 │ │
│ │ Redis(缓存) / Milvus/PGVector(向量) / 对象存储 │ │
│ └─────────────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────────────┘组件 | 版本要求 | 说明 |
|---|---|---|
JDK | 21+ | Virtual Threads必需 |
Maven | 3.9+ | 或Gradle 8.5+ |
Spring Boot | 3.3.x+ | AI集成的基石 |
IDE | IDEA 2024+ / VS Code | 推荐IDEA |
<!-- pom.xml 核心依赖 -->
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
</parent>
<groupId>com.ai.agent</groupId>
<artifactId>java-ai-agent-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>21</java.version>
<langchain4j.version>0.36.0</langchain4j.version>
<spring-ai.version>1.0.0-M5</spring-ai.version>
</properties>
<dependencies>
<!-- Spring Boot 基础 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- LangChain4j 核心 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- OpenAI / 通义千问 集成 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-open-ai</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- 通义千问(国内推荐) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-dashscope</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- 工具调用支持 -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-tools</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- 向量存储(内存版 + PGVector) -->
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-embeddings</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-pgvector</artifactId>
<version>${langchain4j.version}</version>
</dependency>
<!-- Redis 缓存 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 可观测性 -->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project># application.yml
spring:
application:
name: java-ai-agent
# Redis配置(用于缓存和状态管理)
data:
redis:
host: localhost
port: 6379
timeout: 5000ms
# 虚拟线程配置(Spring Boot 3.2+)
threads:
virtual:
enabled: true
# AI模型配置
ai:
llm:
# 主模型(生产环境使用通义千问或DeepSeek)
provider: qwen
qwen:
api-key: ${DASHSCOPE_API_KEY}
model: qwen-max
temperature: 0.3
max-tokens: 2000
# 备用模型(降级用)
fallback:
provider: openai
openai:
api-key: ${OPENAI_API_KEY}
model: gpt-4o-mini
# Agent配置
agent:
max-iterations: 10 # 最大推理轮数
memory:
vector-store: pgvector # pgvector / chroma / memory
max-history: 20 # 保留最近20条对话
summary-size: 5 # 保留5条摘要
# 监控配置
management:
endpoints:
web:
exposure:
include: health, metrics, prometheus
metrics:
export:
prometheus:
enabled: trueReAct = Reasoning(推理) + Acting(行动)。Agent在每一步交替进行"思考"和"行动",直到完成任务。
用户: "帮我查一下明天的天气,并据此推荐出行方案"
Agent推理: "我需要先获取天气信息,再基于天气推荐出行方案"
│
▼
Agent行动: 调用天气查询工具
│
▼
工具返回: "明天晴,25-32°C,微风"
│
▼
Agent推理: "天气晴好,适合户外活动,推荐..."
│
▼
Agent行动: 输出最终回答// ReActAgentService.java - ReAct模式Agent核心实现
package com.ai.agent.service;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.agent.tool.ToolExecutionRequest;
import dev.langchain4j.agent.tool.ToolSpecification;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.SystemMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.openai.OpenAiChatModel;
import dev.langchain4j.model.qianfan.QianfanChatModel;
import dev.langchain4j.service.AiServices;
import dev.langchain4j.tool.ToolExecutor;
import dev.langchain4j.tool.ToolSpecifications;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import jakarta.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
@RequiredArgsConstructor
public class ReActAgentService {
private ChatLanguageModel model;
private final Map<String, ChatMemory> sessionMemories = new ConcurrentHashMap<>();
private final Map<String, Integer> sessionIterations = new ConcurrentHashMap<>();
private static final int MAX_ITERATIONS = 10;
@PostConstruct
public void init() {
// 初始化模型(使用通义千问)
this.model = QianfanChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-max")
.temperature(0.3)
.maxTokens(2000)
.build();
log.info("ReAct Agent 初始化完成");
}
// ========== 1. 定义工具 ==========
@Tool("获取当前时间,不需要参数")
public String getCurrentTime() {
return LocalDateTime.now()
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
}
@Tool("获取当前天气信息,参数为城市名称")
public String getWeather(String city) {
// 模拟天气查询,实际可接入和风天气API
log.info("查询天气: {}", city);
// 这里模拟返回数据,生产环境替换为真实API调用
return String.format("""
🌤️ %s 天气情况:
温度: 26-33°C
天气: 晴转多云
风力: 3-4级
湿度: 65%%
空气质量: 良
温馨提示: 适合户外活动,注意防晒。
""", city);
}
@Tool("执行数学计算,输入数学表达式,返回计算结果")
public String calculate(String expression) {
try {
// 使用Java内置的ScriptEngine,或者更安全的表达式计算库
var engine = new javax.script.ScriptEngineManager()
.getEngineByName("JavaScript");
var result = engine.eval(expression);
return String.format("计算结果: %s = %s", expression, result);
} catch (Exception e) {
return String.format("计算错误: %s", e.getMessage());
}
}
// ========== 2. 获取工具定义 ==========
private List<ToolSpecification> getToolSpecifications() {
return ToolSpecifications.toolSpecificationsFrom(
this.getClass()
);
}
// ========== 3. Agent执行 ==========
public String execute(String sessionId, String userInput) {
log.info("执行Agent: session={}, input={}", sessionId, userInput);
// 获取或创建会话记忆
ChatMemory memory = sessionMemories.computeIfAbsent(
sessionId,
k -> MessageWindowChatMemory.builder()
.maxMessages(20)
.build()
);
// 重置迭代计数
sessionIterations.put(sessionId, 0);
// 添加用户消息
memory.add(UserMessage.from(userInput));
// 系统提示词(定义Agent的角色和行为)
String systemPrompt = """
你是一个智能助手(Agent),可以帮助用户完成各种任务。
你可以使用以下工具:
1. getCurrentTime - 获取当前时间
2. getWeather(city) - 查询指定城市的天气
3. calculate(expression) - 执行数学计算
工作流程:
1. 分析用户的问题,判断是否需要使用工具
2. 如果需要,选择并调用合适的工具
3. 根据工具返回的结果,给出最终回答
重要规则:
- 每次只能调用一个工具
- 如果不需要工具,直接回答问题
- 必须使用中文回答
- 如果工具调用失败,尝试其他方法或告知用户
""";
// 执行ReAct循环
int iterations = 0;
while (iterations < MAX_ITERATIONS) {
iterations++;
sessionIterations.put(sessionId, iterations);
log.info("Agent迭代: {}", iterations);
// 构建消息列表
var messages = memory.messages();
// 调用LLM进行推理
var response = model.generate(
List.of(SystemMessage.from(systemPrompt)),
messages
);
var aiMessage = response.content();
memory.add(aiMessage);
// 检查是否有工具调用请求
if (aiMessage.hasToolExecutionRequests()) {
var toolRequests = aiMessage.toolExecutionRequests();
for (ToolExecutionRequest request : toolRequests) {
log.info("调用工具: {}", request.name());
// 执行工具
String toolResult = executeTool(request);
// 将工具结果添加到记忆
memory.add(dev.langchain4j.data.message.ToolExecutionResultMessage.from(
request.id(),
toolResult
));
}
// 继续循环,让模型基于工具结果继续推理
continue;
}
// 没有工具调用,返回最终答案
String finalAnswer = aiMessage.text();
log.info("Agent完成,迭代次数: {}", iterations);
return finalAnswer;
}
// 达到最大迭代次数
return "任务复杂,需要更多步骤。请简化您的问题或稍后重试。";
}
// ========== 4. 工具执行 ==========
private String executeTool(ToolExecutionRequest request) {
String toolName = request.name();
Map<String, Object> args = request.arguments();
try {
return switch (toolName) {
case "getCurrentTime" -> getCurrentTime();
case "getWeather" -> {
String city = (String) args.get("city");
yield getWeather(city);
}
case "calculate" -> {
String expression = (String) args.get("expression");
yield calculate(expression);
}
default -> "未知工具: " + toolName;
};
} catch (Exception e) {
log.error("工具执行失败: {}", toolName, e);
return "工具执行失败: " + e.getMessage();
}
}
// ========== 5. 清理会话 ==========
public void clearSession(String sessionId) {
sessionMemories.remove(sessionId);
sessionIterations.remove(sessionId);
log.info("会话已清理: {}", sessionId);
}
}// AgentController.java - Agent HTTP接口
package com.ai.agent.controller;
import com.ai.agent.service.ReActAgentService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import java.util.UUID;
@Slf4j
@RestController
@RequestMapping("/api/agent")
@RequiredArgsConstructor
public class AgentController {
private final ReActAgentService agentService;
/**
* 执行Agent任务
*/
@PostMapping("/execute")
public Map<String, Object> execute(@RequestBody AgentRequest request) {
String sessionId = request.getSessionId();
if (sessionId == null || sessionId.isBlank()) {
sessionId = UUID.randomUUID().toString();
}
long startTime = System.currentTimeMillis();
String result = agentService.execute(sessionId, request.getQuery());
long duration = System.currentTimeMillis() - startTime;
log.info("Agent执行完成: session={}, duration={}ms", sessionId, duration);
return Map.of(
"sessionId", sessionId,
"answer", result,
"durationMs", duration
);
}
/**
* 清理会话
*/
@DeleteMapping("/session/{sessionId}")
public Map<String, String> clearSession(@PathVariable String sessionId) {
agentService.clearSession(sessionId);
return Map.of("status", "cleared", "sessionId", sessionId);
}
/**
* 获取会话状态
*/
@GetMapping("/session/{sessionId}/status")
public Map<String, Object> getStatus(@PathVariable String sessionId) {
// 实际需要暴露更多状态信息
return Map.of(
"sessionId", sessionId,
"active", true
);
}
}
// 请求DTO
@Data
public class AgentRequest {
private String sessionId;
private String query;
}# 启动服务
mvn spring-boot:run
# 测试Agent
curl -X POST http://localhost:8080/api/agent/execute \
-H "Content-Type: application/json" \
-d '{"query": "帮我查一下杭州明天的天气,然后计算一下我如果带伞的话,需要多带多少重量。假设一把伞重500克。"}'
# 预期输出:Agent会依次调用 getWeather("杭州") → calculate("500") → 综合回答LangChain4j负责Agent核心逻辑,Spring AI负责模型抽象和工具管理。
// HybridAgentConfig.java - 混合架构配置
package com.ai.agent.config;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.model.chat.ChatLanguageModel;
import dev.langchain4j.model.dashscope.QwenChatModel;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
@Configuration
public class HybridAgentConfig {
/**
* LangChain4j模型(Agent核心推理)
*/
@Bean
@Primary
public ChatLanguageModel langChainModel() {
return QwenChatModel.builder()
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.modelName("qwen-max")
.temperature(0.3)
.build();
}
/**
* Spring AI模型(特定场景使用)
*/
@Bean
public OpenAiChatModel springAiModel() {
return OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.model("gpt-4o")
.temperature(0.2)
.build();
}
/**
* Spring AI ChatClient(流式输出支持)
*/
@Bean
public ChatClient chatClient(OpenAiChatModel model) {
return ChatClient.builder(model).build();
}
/**
* 默认会话记忆配置
*/
@Bean
public ChatMemory defaultChatMemory() {
return MessageWindowChatMemory.builder()
.maxMessages(30)
.build();
}
}// TaskOrchestrator.java - 任务编排引擎
package com.ai.agent.orchestrator;
import dev.langchain4j.agent.tool.Tool;
import dev.langchain4j.data.message.AiMessage;
import dev.langchain4j.data.message.UserMessage;
import dev.langchain4j.model.chat.ChatLanguageModel;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@Slf4j
@Component
@RequiredArgsConstructor
public class TaskOrchestrator {
private final ChatLanguageModel model;
// 任务计划缓存
private final Map<String, TaskPlan> taskPlans = new ConcurrentHashMap<>();
/**
* 任务计划结构
*/
public record TaskPlan(
String goal, // 任务目标
List<SubTask> tasks, // 子任务列表
boolean parallel // 是否并行执行
) {}
public record SubTask(
String id,
String description,
String tool,
Map<String, Object> params,
String dependsOn // 依赖的子任务ID
) {}
/**
* 执行复杂任务
*/
public String executeComplexTask(String taskId, String goal) {
log.info("开始执行复杂任务: {}", goal);
// 1. 任务拆解(使用LLM生成任务计划)
TaskPlan plan = decomposeTask(goal);
taskPlans.put(taskId, plan);
// 2. 执行任务
if (plan.parallel()) {
return executeParallel(taskId, plan);
} else {
return executeSequential(taskId, plan);
}
}
/**
* 使用LLM进行任务拆解
*/
private TaskPlan decomposeTask(String goal) {
String prompt = String.format("""
请将以下任务拆解为可执行的子任务列表。
任务: %s
可用工具:
- search_web(query): 搜索互联网信息
- get_weather(city): 查询天气
- calculate(expression): 数学计算
- get_stock_price(symbol): 查询股票价格
- send_email(to, subject, body): 发送邮件
请以JSON格式输出:
{
"goal": "任务目标",
"parallel": false,
"tasks": [
{"id": "t1", "description": "描述", "tool": "工具名", "params": {"key": "value"}, "dependsOn": null}
]
}
""", goal);
var response = model.generate(
List.of(UserMessage.from(prompt))
);
// 解析LLM返回的JSON(实际代码需要健壮的JSON解析)
return parseTaskPlan(response.content().text());
}
/**
* 顺序执行
*/
private String executeSequential(String taskId, TaskPlan plan) {
var results = new ConcurrentHashMap<String, Object>();
var sb = new StringBuilder();
for (SubTask task : plan.tasks()) {
log.info("执行子任务: {} - {}", task.id(), task.description());
// 检查依赖是否完成
if (task.dependsOn() != null && !results.containsKey(task.dependsOn())) {
log.warn("依赖任务未完成: {}", task.dependsOn());
continue;
}
// 执行工具
String result = executeTool(task.tool(), task.params());
results.put(task.id(), result);
sb.append(task.description()).append(": ").append(result).append("\n");
}
// 汇总结果
return generateFinalAnswer(plan.goal(), sb.toString());
}
/**
* 并行执行(使用虚拟线程)
*/
private String executeParallel(String taskId, TaskPlan plan) {
var results = new ConcurrentHashMap<String, Object>();
try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
var futures = new ArrayList<Future<?>>();
for (SubTask task : plan.tasks()) {
var future = executor.submit(() -> {
String result = executeTool(task.tool(), task.params());
results.put(task.id(), result);
log.info("并行任务完成: {}", task.id());
});
futures.add(future);
}
// 等待所有任务完成
for (var future : futures) {
try {
future.get();
} catch (Exception e) {
log.error("并行任务执行失败", e);
}
}
}
// 汇总结果
var sb = new StringBuilder();
for (SubTask task : plan.tasks()) {
sb.append(task.description()).append(": ")
.append(results.getOrDefault(task.id(), "执行失败"))
.append("\n");
}
return generateFinalAnswer(plan.goal(), sb.toString());
}
/**
* 工具执行(简化版)
*/
private String executeTool(String toolName, Map<String, Object> params) {
// 实际实现:通过反射或工具注册表调用
return switch (toolName) {
case "get_weather" -> "杭州: 晴, 26-33°C";
case "calculate" -> {
String expr = (String) params.get("expression");
yield "计算结果: " + expr + " = 42"; // 简化
}
case "search_web" -> "搜索结果: 相关文章3篇...";
default -> "工具执行结果 (模拟)";
};
}
/**
* 生成最终答案
*/
private String generateFinalAnswer(String goal, String results) {
String prompt = String.format("""
任务目标: %s
子任务执行结果:
%s
请基于以上结果,生成一个完整、友好的最终回答。
""", goal, results);
var response = model.generate(List.of(UserMessage.from(prompt)));
return response.content().text();
}
/**
* 解析任务计划(实际需要健壮的JSON解析)
*/
private TaskPlan parseTaskPlan(String json) {
// 简化实现,生产环境使用Jackson
return new TaskPlan("任务目标", List.of(), false);
}
}// PersistentMemoryService.java - 持久化记忆服务
package com.ai.agent.memory;
import dev.langchain4j.data.message.ChatMessage;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.memory.ChatMemory;
import dev.langchain4j.memory.chat.MessageWindowChatMemory;
import dev.langchain4j.store.embedding.EmbeddingStore;
import dev.langchain4j.store.embedding.pgvector.PgVectorEmbeddingStore;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@Slf4j
@Service
public class PersistentMemoryService {
private final RedisTemplate<String, Object> redisTemplate;
private final EmbeddingStore<TextSegment> embeddingStore;
// 会话的短期记忆(对话上下文)
private final ConcurrentHashMap<String, ChatMemory> shortTermMemory = new ConcurrentHashMap<>();
// 会话的长期记忆(向量存储)
private final ConcurrentHashMap<String, List<String>> longTermMemoryIndex = new ConcurrentHashMap<>();
public PersistentMemoryService(
RedisTemplate<String, Object> redisTemplate,
PgVectorEmbeddingStore embeddingStore) {
this.redisTemplate = redisTemplate;
this.embeddingStore = embeddingStore;
}
/**
* 获取或创建会话记忆
*/
public ChatMemory getMemory(String sessionId) {
return shortTermMemory.computeIfAbsent(sessionId, id -> {
// 尝试从Redis恢复
List<ChatMessage> saved = loadFromRedis(id);
if (saved != null && !saved.isEmpty()) {
log.info("从Redis恢复会话记忆: {}", id);
var memory = MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
saved.forEach(memory::add);
return memory;
}
// 新建记忆
return MessageWindowChatMemory.builder()
.maxMessages(20)
.build();
});
}
/**
* 保存对话到长期记忆(向量存储)
*/
public void saveToLongTermMemory(String sessionId, String userMessage, String agentResponse) {
String memoryId = UUID.randomUUID().toString();
// 生成摘要
String summary = String.format("用户: %s | 助手: %s",
userMessage.length() > 100 ? userMessage.substring(0, 100) + "..." : userMessage,
agentResponse.length() > 100 ? agentResponse.substring(0, 100) + "..." : agentResponse
);
// 存储向量
embeddingStore.add(
TextSegment.from(
summary,
java.util.Map.of(
"sessionId", sessionId,
"memoryId", memoryId,
"timestamp", System.currentTimeMillis()
)
)
);
// 更新索引
longTermMemoryIndex.computeIfAbsent(sessionId, k -> new ArrayList<>())
.add(memoryId);
log.info("已保存长期记忆: session={}, id={}", sessionId, memoryId);
}
/**
* 检索相关长期记忆
*/
public List<String> retrieveMemories(String sessionId, String query, int maxResults) {
// 使用向量检索
var relevant = embeddingStore.findRelevant(query, maxResults);
return relevant.stream()
.filter(match -> match.embedded().metadata()
.getOrDefault("sessionId", "").equals(sessionId))
.map(match -> match.embedded().text())
.toList();
}
/**
* 保存到Redis(短期持久化)
*/
private void saveToRedis(String sessionId, List<ChatMessage> messages) {
String key = "agent:memory:" + sessionId;
redisTemplate.opsForValue().set(
key,
messages,
Duration.ofHours(24) // 保留24小时
);
}
/**
* 从Redis加载
*/
@SuppressWarnings("unchecked")
private List<ChatMessage> loadFromRedis(String sessionId) {
String key = "agent:memory:" + sessionId;
Object value = redisTemplate.opsForValue().get(key);
if (value instanceof List<?>) {
return (List<ChatMessage>) value;
}
return null;
}
/**
* 清理会话
*/
public void clearSession(String sessionId) {
shortTermMemory.remove(sessionId);
longTermMemoryIndex.remove(sessionId);
redisTemplate.delete("agent:memory:" + sessionId);
log.info("会话已清理: {}", sessionId);
}
}// 在ReActAgentService中集成记忆系统
@Service
public class EnhancedReActAgentService {
private final PersistentMemoryService memoryService;
// 修改execute方法,加入记忆检索
public String executeWithMemory(String sessionId, String userInput) {
// 1. 检索相关长期记忆
var relevantMemories = memoryService.retrieveMemories(
sessionId,
userInput,
3
);
// 2. 构建增强提示词
String memoryContext = relevantMemories.isEmpty()
? ""
: "相关历史记忆:\n" + String.join("\n", relevantMemories);
// 3. 执行Agent推理
String response = execute(sessionId,
memoryContext + "\n\n当前问题: " + userInput
);
// 4. 保存到长期记忆
memoryService.saveToLongTermMemory(sessionId, userInput, response);
return response;
}
}// AgentMetrics.java - 监控指标
package com.ai.agent.monitoring;
import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import lombok.Getter;
import org.springframework.stereotype.Component;
import jakarta.annotation.PostConstruct;
@Component
public class AgentMetrics {
private final MeterRegistry registry;
@Getter
private Counter totalRequests;
@Getter
private Counter successRequests;
@Getter
private Counter errorRequests;
@Getter
private Counter toolCalls;
@Getter
private Timer requestTimer;
@Getter
private Timer llmCallTimer;
public AgentMetrics(MeterRegistry registry) {
this.registry = registry;
}
@PostConstruct
public void init() {
this.totalRequests = Counter.builder("agent.requests.total")
.description("Total Agent requests")
.register(registry);
this.successRequests = Counter.builder("agent.requests.success")
.description("Successful Agent requests")
.register(registry);
this.errorRequests = Counter.builder("agent.requests.error")
.description("Failed Agent requests")
.register(registry);
this.toolCalls = Counter.builder("agent.tool.calls")
.description("Total tool calls")
.register(registry);
this.requestTimer = Timer.builder("agent.request.duration")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
this.llmCallTimer = Timer.builder("agent.llm.duration")
.publishPercentiles(0.5, 0.95, 0.99)
.register(registry);
}
public void recordSuccess() {
successRequests.increment();
}
public void recordError() {
errorRequests.increment();
}
public void recordToolCall() {
toolCalls.increment();
}
public <T> T recordRequest(Timer.Sample sample) {
sample.stop(requestTimer);
return null;
}
public <T> T recordLLMCall(Timer.Sample sample) {
sample.stop(llmCallTimer);
return null;
}
}# logback-spring.xml - 添加traceId支持
<configuration>
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] [%X{traceId}] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="CONSOLE"/>
</root>
</configuration>// TraceInterceptor.java - 链路追踪拦截器
@Component
public class TraceInterceptor implements HandlerInterceptor {
private static final String TRACE_ID_HEADER = "X-Trace-Id";
private static final String TRACE_ID_KEY = "traceId";
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
String traceId = request.getHeader(TRACE_ID_HEADER);
if (traceId == null || traceId.isBlank()) {
traceId = UUID.randomUUID().toString();
}
MDC.put(TRACE_ID_KEY, traceId);
return true;
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
MDC.remove(TRACE_ID_KEY);
}
}# Dockerfile
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
# 复制jar包
COPY target/java-ai-agent-*.jar app.jar
# 健康检查
HEALTHCHECK --interval=30s --timeout=3s CMD wget --spider http://localhost:8080/actuator/health || exit 1
# 启动参数:GraalVM原生优化
ENTRYPOINT ["java", \
"-Xmx2g", \
"-XX:+UseZGC", \
"-XX:+EnableDynamicAgentLoading", \
"-jar", "app.jar"]# application-prod.yml
server:
tomcat:
threads:
max: 200
min-spare: 20
connection-timeout: 60s
spring:
ai:
retry:
max-attempts: 3
backoff:
initial-interval: 2s
multiplier: 2
max-interval: 30s
ai:
llm:
timeout: 30s
retry-on-rate-limit: true
agent:
timeout: 60s
max-iterations: 8
parallel-execution: true
logging:
level:
com.ai.agent: DEBUG Spring Boot + Java
★★★★★
│
Agent框架 ★★★★┼★★★ 大模型API
│
向量数据库 ★★★★┼★★★ 提示词工程
│
多Agent协作 ★★★┼★★★ 可观测性
│
性能优化 ★★★┼★★★ 安全与合规
★★★★★
企业集成能力阶段 | 目标 | 学习内容 | 标志性产出 |
|---|---|---|---|
第1-2周 | 基础认知 | Spring AI快速入门、LangChain4j基础 | 跑通第一个AI调用接口 |
第3-4周 | Agent入门 | ReAct模式实现、工具定义与调用 | 完成一个多工具Agent |
第5-6周 | 进阶能力 | 记忆系统设计、RAG集成 | 带记忆的客服Agent |
第7-8周 | 复杂场景 | 多Agent协作、任务编排 | 多角色协作系统 |
第9-12周 | 生产化 | 监控、性能优化、部署 | 生产级Agent服务上线 |
方向 | 薪资区间 | 核心要求 |
|---|---|---|
企业AI Agent平台开发 | 50-100万 | Spring生态 + LangChain4j + 高并发 |
垂直领域Agent解决方案 | 60-120万 | 领域知识 + Agent架构设计 |
Agent基础设施(工具链/框架) | 70-150万 | 框架源码阅读 + 中间件开发 |
Java工程师转型AI Agent开发,不是"重学一门语言",而是用你已有的Java生态优势,切入一个高增长的新领域。
Spring生态的成熟、虚拟线程的性能优势、GraalVM的部署优化——这些Java独有的能力,正是构建生产级AI Agent系统的核心竞争力。
未来的高薪岗位,属于"懂Java、懂AI、懂系统"的复合型人才。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。