Spring Boot 3 + AI大模型整合全攻略
技术架构
2026年,Java开发者必须拥抱AI大模型
Python训练模型,Java应用模型——这是2026年AI工程化的黄金分工。
一个事实:全球500强企业中,72%的后端系统运行在JVM上,但AI应用的原型开发90%用Python。问题来了——模型训练完了,谁来扛生产流量? 答案是Java。
为什么Java是AI应用的最佳载体
| 维度 | Python | Java (Spring Boot) |
|---|---|---|
| 并发处理 | GIL限制,单线程 | 多线程 + 虚拟线程(Loom),万级并发 |
| 企业集成 | 需大量胶水代码 | Spring生态一站式集成 |
| 安全合规 | 动态类型,运行时才报错 | 强类型 + 编译期检查 |
| 运维成熟度 | Gunicorn/uWSGI | Spring Boot Actuator + K8s |
| 团队技能 | AI研究员 | 企业后端工程师 |
| 部署一致性 | 依赖地狱 | Fat JAR,一次打包到处运行 |
Java + AI的三大场景
┌──────────────────────────────────────────────────────────┐
│ Java + AI 应用全景 │
├──────────────┬──────────────┬────────────────────────────┤
│ AI增强应用 │ AI原生应用 │ AI Agent │
│ │ │ │
│ 智能客服 │ ChatBot │ 自主决策Agent │
│ 文档问答 │ Code Copilot │ Planner→Executor→Evaluator │
│ 数据分析助手 │ RAG知识库 │ 多工具编排Agent │
│ 智能推荐 │ 内容生成 │ 工作流自动化Agent │
├──────────────┴──────────────┴────────────────────────────┤
│ Spring Boot 3 + Spring AI │
│ 统一编程模型 · 声明式配置 · 生产级可靠性 │
└──────────────────────────────────────────────────────────┘
Spring AI vs LangChain4j:框架选型
2026年Java AI生态两大主流框架,各有侧重。
核心定位对比
| 维度 | Spring AI | LangChain4j |
|---|---|---|
| 设计哲学 | Spring风格,声明式 | LangChain移植,链式调用 |
| 核心团队 | Spring官方(VMware) | 独立开源社区 |
| 配置方式 | application.yml + Bean | Builder模式 + 代码配置 |
| 模型支持 | OpenAI/Azure/Ollama/通义 | OpenAI/Azure/Ollama/通义/智谱 |
| 向量库 | PGVector/Chroma/Milvus | PGVector/Chroma/Milvus/Weaviate |
| RAG支持 | 内置ETL Pipeline | 内置RAG模块 |
| Function Calling | 自动注册Spring Bean | 手动注册@Tool方法 |
| Spring集成 | 原生,零配置 | 需spring-boot-starter |
| 流式响应 | Flux | TokenStream |
| 社区活跃度 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 学习曲线 | Spring开发者零门槛 | 需学LangChain概念 |
| 版本(2026) | 1.0.0 GA | 1.0.0 GA |
代码风格对比
// Spring AI — 声明式,Spring风格
@Configuration
public class AiConfig {
@Bean
public ChatClient chatClient(ChatClient.Builder builder) {
return builder
.defaultSystem("你是一个专业的Java技术顾问")
.defaultAdvisors(new SimpleLoggerAdvisor())
.build();
}
}
// 调用:一行搞定
String response = chatClient.prompt()
.user("解释Spring Boot 3的虚拟线程")
.call()
.content();
// LangChain4j — 链式,Builder风格
ChatLanguageModel model = OpenAiChatModel.builder()
.apiKey(System.getenv("OPENAI_API_KEY"))
.modelName("gpt-4o")
.temperature(0.7)
.build();
// 调用:显式构造
Response<AiMessage> response = model.generate(
SystemMessage.from("你是一个专业的Java技术顾问"),
UserMessage.from("解释Spring Boot 3的虚拟线程")
);
选型建议
| 场景 | 推荐 | 理由 |
|---|---|---|
| Spring项目加AI能力 | Spring AI | 零学习成本,原生集成 |
| 全新AI原生项目 | 均可 | 看团队技术栈偏好 |
| 复杂Agent编排 | LangChain4j | Chain/Agent抽象更成熟 |
| 企业合规要求 | Spring AI | Actuator + Security集成 |
| 快速原型 | LangChain4j | Builder模式更直观 |
本文以Spring AI为主线,因为它与Spring Boot 3的整合最为自然,也是2026年企业级Java AI应用的主流选择。
Spring Boot 3 + Spring AI快速入门
第一步:Maven依赖
<?xml version="1.0" encoding="UTF-8"?>
<project>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.4.0</version>
</parent>
<properties>
<java.version>21</java.version>
<spring-ai.version>1.0.0</spring-ai.version>
</properties>
<dependencies>
<!-- Spring AI OpenAI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Spring AI Vector Store (PGVector) -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-pgvector-store-spring-boot-starter</artifactId>
</dependency>
<!-- Redis for Chat Memory -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-chat-memory-redis</artifactId>
</dependency>
<!-- Spring Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>${spring-ai.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
第二步:YAML配置
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
base-url: https://api.openai.com
chat:
options:
model: gpt-4o
temperature: 0.7
max-tokens: 4096
top-p: 0.9
embedding:
options:
model: text-embedding-3-large
vectorstore:
pgvector:
index-type: HNSW
dimensions: 3072
distance-type: COSINE
chat:
memory:
redis:
host: localhost
port: 6379
ttl: 3600
server:
port: 8080
management:
endpoints:
web:
exposure:
include: health,info,metrics,prometheus
第三步:ChatController
@RestController
@RequestMapping("/api/chat")
public class ChatController {
private final ChatClient chatClient;
public ChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@PostMapping
public String chat(@RequestBody ChatRequest request) {
return chatClient.prompt()
.user(request.message())
.call()
.content();
}
@PostMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> chatStream(@RequestBody ChatRequest request) {
return chatClient.prompt()
.user(request.message())
.stream()
.content();
}
@PostMapping("/system")
public String chatWithSystem(
@RequestBody ChatRequest request,
@RequestParam String role) {
return chatClient.prompt()
.system(role)
.user(request.message())
.call()
.content();
}
public record ChatRequest(String message) {}
}
第四步:启动与验证
# 设置API Key
export OPENAI_API_KEY=sk-xxxxx
# 启动应用
mvn spring-boot:run
# 测试
curl -X POST http://localhost:8080/api/chat \
-H "Content-Type: application/json" \
-d '{"message": "用Java写一个快速排序"}'
# 流式测试
curl -X POST http://localhost:8080/api/chat/stream \
-H "Content-Type: application/json" \
-d '{"message": "解释JVM内存模型"}'
┌──────────────────────────────────────────────────────┐
│ Spring Boot 3 + Spring AI 启动流程 │
├──────────────────────────────────────────────────────┤
│ │
│ main() → @SpringBootApplication │
│ ├── 自动配置 spring-ai-auto-configuration │
│ ├── 注册 OpenAiChatModel Bean │
│ ├── 注册 OpenAiEmbeddingModel Bean │
│ ├── 注册 PgVectorStore Bean │
│ ├── 注册 ChatClient Bean │
│ └── 注册 ChatMemory Bean (Redis) │
│ │
│ ChatClient.prompt() │
│ ├── .user() → 构造UserMessage │
│ ├── .system() → 构造SystemMessage │
│ ├── .advisors() → 注入Advisor链 │
│ ├── .call() → 同步调用 → String │
│ └── .stream() → 流式调用 → Flux<String> │
│ │
└──────────────────────────────────────────────────────┘
企业级Chat应用:多轮对话与Redis持久化记忆
没有记忆的AI就像金鱼——每轮对话都是全新的,无法理解上下文。企业级Chat应用必须有持久化记忆。
对话记忆架构
┌──────────────────────────────────────────────────────────┐
│ 多轮对话记忆架构 │
├──────────────────────────────────────────────────────────┤
│ │
│ 用户消息 ──→ ChatMemoryAdvisor ──→ ChatClient │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ │
│ │ Redis │ │ OpenAI │ │
│ │ ChatStore│ │ API │ │
│ └──────────┘ └──────────┘ │
│ ▲ │ │
│ │ ▼ │
│ 写回记忆 ←── AI响应 ←── 模型推理 │
│ │
│ Memory结构: │
│ conversation:{userId} → [ │
│ {role: "system", content: "..."}, │
│ {role: "user", content: "..."}, │
│ {role: "assistant", content: "..."}, │
│ ... │
│ ] │
└──────────────────────────────────────────────────────────┘
配置ChatMemory
@Configuration
public class ChatMemoryConfig {
@Bean
public ChatClient chatClientWithMemory(
ChatClient.Builder builder,
ChatMemoryRepository memoryRepository) {
ChatMemory chatMemory = new RedisChatMemory(memoryRepository);
return builder
.defaultSystem("""
你是工具库网站的AI助手,专注于Java和Spring技术。
回答要专业、准确、有代码示例。
如果不确定,请诚实说明。
""")
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory)
.conversationIdExpression("userId")
.maxMessages(20)
.build(),
new SimpleLoggerAdvisor()
)
.build();
}
}
ChatController with Memory
@RestController
@RequestMapping("/api/chat")
public class EnterpriseChatController {
private final ChatClient chatClient;
public EnterpriseChatController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@PostMapping("/conversation")
public ChatResponse conversation(
@RequestBody ConversationRequest request,
@RequestHeader("X-User-Id") String userId) {
String content = chatClient.prompt()
.user(request.message())
.advisors(a -> a.param("userId", userId))
.call()
.content();
return new ChatResponse(content, userId, Instant.now());
}
@PostMapping("/conversation/stream")
public Flux<ServerSentEvent<String>> conversationStream(
@RequestBody ConversationRequest request,
@RequestHeader("X-User-Id") String userId) {
return chatClient.prompt()
.user(request.message())
.advisors(a -> a.param("userId", userId))
.stream()
.content()
.map(chunk -> ServerSentEvent.<String>builder()
.data(chunk)
.build());
}
@DeleteMapping("/conversation/{userId}")
public ResponseEntity<Void> clearMemory(@PathVariable String userId) {
// 清除用户对话记忆
return ResponseEntity.noContent().build();
}
public record ConversationRequest(String message) {}
public record ChatResponse(String content, String userId, Instant timestamp) {}
}
Redis记忆存储实现
@Component
public class RedisChatMemoryRepository implements ChatMemoryRepository {
private final StringRedisTemplate redisTemplate;
private final ObjectMapper objectMapper;
private static final String KEY_PREFIX = "chat:memory:";
private static final Duration TTL = Duration.ofHours(2);
public RedisChatMemoryRepository(
StringRedisTemplate redisTemplate,
ObjectMapper objectMapper) {
this.redisTemplate = redisTemplate;
this.objectMapper = objectMapper;
}
@Override
public List<Message> findByConversationId(String conversationId) {
String json = redisTemplate.opsForValue().get(KEY_PREFIX + conversationId);
if (json == null) {
return new ArrayList<>();
}
try {
return objectMapper.readValue(json,
new TypeReference<List<Message>>() {});
} catch (JsonProcessingException e) {
throw new ChatMemoryException("Failed to read memory", e);
}
}
@Override
public void saveAll(String conversationId, List<Message> messages) {
try {
String json = objectMapper.writeValueAsString(messages);
redisTemplate.opsForValue()
.set(KEY_PREFIX + conversationId, json, TTL);
} catch (JsonProcessingException e) {
throw new ChatMemoryException("Failed to save memory", e);
}
}
}
记忆策略对比
| 策略 | 实现 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|
| 窗口记忆 | 保留最近N轮 | 简单,token可控 | 丢失早期上下文 | 通用对话 |
| 摘要记忆 | 旧对话压缩为摘要 | 保留全局语义 | 摘要可能丢细节 | 长对话 |
| 混合记忆 | 摘要 + 最近N轮 | 兼顾全局与细节 | 实现复杂 | 企业客服 |
| 向量记忆 | 按语义检索相关片段 | 无限上下文 | 检索延迟 | 知识密集型 |
RAG实战:文档ETL Pipeline + 向量检索 + 上下文注入
RAG(检索增强生成)是让大模型拥有企业私有知识的核心技术。
RAG完整架构
┌──────────────────────────────────────────────────────────────┐
│ RAG 完整 Pipeline │
├──────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 文档源 │───→│ ETL │───→│ 向量库 │ │
│ │ PDF/MD │ │ Pipeline │ │ PGVector │ │
│ │ DOCX │ │ │ │ │ │
│ └─────────┘ └──────────┘ └──────────┘ │
│ │ ▲ │
│ ▼ │ │
│ ┌──────────────┐ │ │
│ │ Embedding │──────┘ │
│ │ text-embed-3│ │
│ └──────────────┘ │
│ │
│ ┌─────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 用户提问 │───→│ 检索 │───→│ 生成 │ │
│ │ │ │ 相似度TopK│ │ 上下文+Q │ │
│ └─────────┘ └──────────┘ └──────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────┐ ┌──────────────┐ │
│ │ RRF重排 │ │ 答案+引用 │ │
│ └──────────────┘ └──────────────┘ │
└──────────────────────────────────────────────────────────────┘
文档ETL Pipeline
@Service
public class DocumentEtlService {
private final VectorStore vectorStore;
private final DocumentReader documentReader;
private final DocumentTransformer documentTransformer;
public DocumentEtlService(
VectorStore vectorStore,
DocumentReader documentReader,
DocumentTransformer documentTransformer) {
this.vectorStore = vectorStore;
this.documentReader = documentReader;
this.documentTransformer = documentTransformer;
}
public void ingestDocuments(String directoryPath) {
// Step 1: Read — 读取文档
List<Document> documents = documentReader.read(directoryPath);
// Step 2: Transform — 分块 + 添加元数据
List<Document> chunks = documentTransformer.apply(documents);
// Step 3: Load — Embedding + 写入向量库
vectorStore.add(chunks);
log.info("Ingested {} documents, {} chunks into vector store",
documents.size(), chunks.size());
}
}
文档读取器
@Component
public class SmartDocumentReader implements DocumentReader {
private final TikaDocumentReader tikaReader;
@Override
public List<Document> read(String path) {
return switch (getFileExtension(path)) {
case "pdf" -> readPdf(path);
case "md" -> readMarkdown(path);
case "docx" -> readDocx(path);
default -> throw new UnsupportedDocumentException(path);
};
}
private List<Document> readPdf(String path) {
var reader = new PagePdfDocumentReader(
path,
PdfDocumentReaderConfig.builder()
.pagesPerDocument(1)
.build()
);
return reader.get();
}
private List<Document> readMarkdown(String path) {
var reader = new MarkdownDocumentReader(
path,
MarkdownDocumentReaderConfig.builder()
.includeCodeBlocks(true)
.build()
);
return reader.get();
}
}
文档分块与元数据增强
@Component
public class SmartDocumentTransformer implements DocumentTransformer {
private final TokenTextSplitter splitter;
@Override
public List<Document> apply(List<Document> documents) {
return documents.stream()
.flatMap(doc -> splitter.split(doc).stream())
.peek(this::enrichMetadata)
.toList();
}
private void enrichMetadata(Document chunk) {
Map<String, Object> metadata = chunk.getMetadata();
metadata.put("chunkId", UUID.randomUUID().toString());
metadata.put("createdAt", Instant.now().toString());
metadata.put("tokenCount", estimateTokenCount(chunk.getContent()));
metadata.put("version", "2026-Q2");
}
private int estimateTokenCount(String text) {
return text.length() / 4;
}
}
RAG检索服务
@Service
public class RagService {
private final ChatClient chatClient;
private final VectorStore vectorStore;
public RagService(ChatClient chatClient, VectorStore vectorStore) {
this.chatClient = chatClient;
this.vectorStore = vectorStore;
}
public String query(String question) {
// Step 1: 向量检索
List<Document> relevantDocs = vectorStore.similaritySearch(
SearchRequest.builder()
.query(question)
.topK(5)
.similarityThreshold(0.7)
.build()
);
// Step 2: 构造上下文
String context = relevantDocs.stream()
.map(doc -> """
[来源: %s]
%s
""".formatted(
doc.getMetadata().get("source"),
doc.getContent()))
.collect(Collectors.joining("\n---\n"));
// Step 3: 注入上下文生成回答
return chatClient.prompt()
.system("""
基于以下参考文档回答用户问题。
如果文档中没有相关信息,请说明"根据现有文档无法回答"。
回答时请标注引用来源。
参考文档:
{context}
""")
.user(question)
.call()
.content();
}
}
RAG检索增强Advisor
@Component
public class RetrievalAugmentationAdvisor implements CallAdvisor {
private final VectorStore vectorStore;
private final QueryExpander queryExpander;
@Override
public AdvisedResponse adviseCall(AdvisedRequest request, Map<String, Object> context) {
String query = request.userText();
// 查询扩展(HyDE)
List<String> expandedQueries = queryExpander.expand(query);
// 多查询检索
List<Document> allDocs = expandedQueries.stream()
.flatMap(q -> vectorStore.similaritySearch(
SearchRequest.builder()
.query(q)
.topK(3)
.build()
).stream())
.toList();
// 去重 + 重排
List<Document> ranked = rerank(allDocs, query, 5);
// 注入上下文
String augmentedUserText = """
参考文档:
%s
用户问题:%s
""".formatted(formatDocuments(ranked), query);
return new AdvisedResponse(
request.mutate().userText(augmentedUserText).build(),
context
);
}
}
RAG性能优化清单
| 优化项 | 方法 | 效果 |
|---|---|---|
| 分块大小 | 512-1024 tokens,overlap 64-128 | 平衡语义完整性与检索精度 |
| 查询扩展 | HyDE + 同义词扩展 | 召回率提升15-30% |
| 混合检索 | 向量 + BM25关键词,RRF融合 | 精确匹配+语义匹配互补 |
| 重排序 | Cross-Encoder / Cohere Rerank | Top5精度提升20% |
| 元数据过滤 | 按部门/版本/日期过滤 | 减少无关文档干扰 |
| 缓存 | 相似查询缓存结果 | 重复查询延迟降低90% |
Function Calling:让大模型调用Java方法
Function Calling是大模型与外部世界交互的桥梁——模型决定"何时调用",你定义"调用什么"。
Function Calling工作流程
┌──────────────────────────────────────────────────────────┐
│ Function Calling 工作流程 │
├──────────────────────────────────────────────────────────┤
│ │
│ 用户: "查询订单ORD-20260601的物流状态" │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ 大模型 │ → 分析意图 → 选择函数: queryLogistics │
│ └──────────┘ 参数: {orderId: "ORD-20260601"} │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ Java方法 │ → 调用queryLogistics("ORD-20260601") │
│ └──────────┘ 返回: {status: "运输中", location: "..."} │
│ │ │
│ ▼ │
│ ┌──────────┐ │
│ │ 大模型 │ → 结合函数结果生成自然语言回答 │
│ └──────────┘ │
│ │ │
│ ▼ │
│ "订单ORD-20260601目前正在运输中,预计明天到达..." │
│ │
└──────────────────────────────────────────────────────────┘
订单查询Function
@Configuration
public class OrderFunctions {
@Bean
@Description("根据订单号查询订单详情,包括商品、金额、状态")
public Function<OrderQuery, OrderInfo> queryOrder(OrderService orderService) {
return query -> orderService.getOrderInfo(query.orderId());
}
@Bean
@Description("根据订单号查询物流状态,包括当前位置和预计到达时间")
public Function<LogisticsQuery, LogisticsInfo> queryLogistics(
LogisticsService logisticsService) {
return query -> logisticsService.getLogisticsInfo(query.orderId());
}
@Bean
@Description("为订单发起退款申请,需要提供订单号和退款原因")
public Function<RefundRequest, RefundResult> requestRefund(
RefundService refundService) {
return request -> refundService.processRefund(
request.orderId(), request.reason());
}
public record OrderQuery(String orderId) {}
public record LogisticsQuery(String orderId) {}
public record RefundRequest(String orderId, String reason) {}
public record OrderInfo(
String orderId,
String productName,
BigDecimal amount,
String status,
LocalDateTime orderTime
) {}
public record LogisticsInfo(
String orderId,
String status,
String currentLocation,
LocalDateTime estimatedArrival
) {}
public record RefundResult(
String refundId,
String orderId,
BigDecimal refundAmount,
String status
) {}
}
Function Calling Controller
@RestController
@RequestMapping("/api/assistant")
public class AiAssistantController {
private final ChatClient chatClient;
public AiAssistantController(ChatClient chatClient) {
this.chatClient = chatClient;
}
@PostMapping
public String assist(@RequestBody AssistRequest request) {
return chatClient.prompt()
.system("""
你是一个电商智能客服助手。你可以:
1. 查询订单详情
2. 查询物流状态
3. 处理退款申请
请根据用户问题选择合适的操作。
回答要友好、专业,包含具体信息。
""")
.user(request.message())
.functions("queryOrder", "queryLogistics", "requestRefund")
.call()
.content();
}
public record AssistRequest(String message) {}
}
实际对话示例
用户: "我的订单ORD-20260601到哪了?"
AI内部流程:
1. 意图识别 → 查询物流
2. 调用 queryLogistics({orderId: "ORD-20260601"})
3. 函数返回: {status: "运输中", currentLocation: "杭州转运中心", estimatedArrival: "2026-06-07"}
4. 生成回答:
AI: "您的订单ORD-20260601目前正在运输中,已到达杭州转运中心,预计2026年6月7日送达。"
用户: "这个订单我不想要了,帮我退款"
AI内部流程:
1. 意图识别 → 先查订单再退款
2. 调用 queryOrder({orderId: "ORD-20260601"})
3. 函数返回: {productName: "MacBook Pro", amount: 14999, status: "已发货"}
4. 调用 requestRefund({orderId: "ORD-20260601", reason: "用户不想要"})
5. 函数返回: {refundId: "REF-20260605", refundAmount: 14999, status: "处理中"}
6. 生成回答:
AI: "已为您提交退款申请。订单ORD-20260601(MacBook Pro,¥14,999)的退款正在处理中,
退款编号REF-20260605。由于商品已发货,退款将在商品退回后3-5个工作日内到账。"
从Chat到Agent:构建Java版AI Agent
Chat是"你问我答",Agent是"你提目标,我自主完成"。
Agent核心循环
┌──────────────────────────────────────────────────────────┐
│ AI Agent 核心循环 │
│ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Planner │────→│ Executor │────→│Evaluator │ │
│ │ 规划器 │ │ 执行器 │ │ 评估器 │ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ ▲ │ │
│ │ 不满意 │ │
│ └───────────────────────────────────┘ │
│ │
│ Planner: 将目标分解为可执行的步骤列表 │
│ Executor: 逐步执行,每步可调用工具(函数) │
│ Evaluator: 检查结果是否满足目标,决定是否重试 │
│ │
│ 终止条件: 评估通过 / 达到最大重试次数 / 用户确认 │
└──────────────────────────────────────────────────────────┘
Agent核心实现
@Service
public class AiAgentService {
private final ChatClient chatClient;
private final List<AgentTool> tools;
public AiAgentService(ChatClient chatClient, List<AgentTool> tools) {
this.chatClient = chatClient;
this.tools = tools;
}
public AgentResult execute(String goal) {
int maxIterations = 5;
// Phase 1: Plan — 规划
Plan plan = plan(goal);
// Phase 2: Execute — 执行
List<StepResult> results = new ArrayList<>();
for (Step step : plan.steps()) {
StepResult result = executeStep(step, results);
results.add(result);
}
// Phase 3: Evaluate — 评估
Evaluation evaluation = evaluate(goal, results);
// 如果不满意,重新规划
int iteration = 1;
while (!evaluation.satisfied() && iteration < maxIterations) {
plan = replan(goal, results, evaluation.feedback());
results.clear();
for (Step step : plan.steps()) {
results.add(executeStep(step, results));
}
evaluation = evaluate(goal, results);
iteration++;
}
return new AgentResult(results, evaluation, iteration);
}
private Plan plan(String goal) {
String planJson = chatClient.prompt()
.system("""
你是一个任务规划器。将用户目标分解为可执行的步骤。
每个步骤必须包含: action(动作), tool(使用的工具), params(参数)。
可用工具: %s
输出JSON格式的步骤列表。
""".formatted(getToolDescriptions()))
.user("目标: " + goal)
.call()
.content();
return parsePlan(planJson);
}
private StepResult executeStep(Step step, List<StepResult> previousResults) {
AgentTool tool = findTool(step.tool());
Object result = tool.execute(step.params());
return new StepResult(step, result, Instant.now());
}
private Evaluation evaluate(String goal, List<StepResult> results) {
String evalJson = chatClient.prompt()
.system("""
评估执行结果是否满足目标。
输出: { "satisfied": boolean, "score": 0-100, "feedback": "改进建议" }
""")
.user("""
目标: %s
执行结果:
%s
""".formatted(goal, formatResults(results)))
.call()
.content();
return parseEvaluation(evalJson);
}
public record Plan(List<Step> steps) {}
public record Step(String action, String tool, Map<String, Object> params) {}
public record StepResult(Step step, Object result, Instant timestamp) {}
public record Evaluation(boolean satisfied, int score, String feedback) {}
public record AgentResult(List<StepResult> results, Evaluation evaluation, int iterations) {}
}
Agent工具注册
public interface AgentTool {
String name();
String description();
Object execute(Map<String, Object> params);
}
@Component
public class DatabaseQueryTool implements AgentTool {
private final JdbcTemplate jdbcTemplate;
@Override
public String name() { return "database_query"; }
@Override
public String description() { return "执行SQL查询数据库,仅支持SELECT语句"; }
@Override
public Object execute(Map<String, Object> params) {
String sql = (String) params.get("sql");
if (!sql.trim().toUpperCase().startsWith("SELECT")) {
throw new SecurityException("Only SELECT queries are allowed");
}
return jdbcTemplate.queryForList(sql);
}
}
@Component
public class HttpApiTool implements AgentTool {
private final RestClient restClient;
@Override
public String name() { return "http_api"; }
@Override
public String description() { return "调用外部HTTP API获取数据"; }
@Override
public Object execute(Map<String, Object> params) {
String url = (String) params.get("url");
String method = (String) params.getOrDefault("method", "GET");
return restClient.get().uri(url).retrieve().body(String.class);
}
}
@Component
public class FileWriteTool implements AgentTool {
@Override
public String name() { return "file_write"; }
@Override
public String description() { return "将内容写入指定文件"; }
@Override
public Object execute(Map<String, Object> params) {
String path = (String) params.get("path");
String content = (String) params.get("content");
try {
Files.writeString(Path.of(path), content);
return "File written successfully: " + path;
} catch (IOException e) {
return "Failed to write file: " + e.getMessage();
}
}
}
Agent执行示例
用户目标: "分析Q1销售数据,生成报告并发送给管理层"
Agent执行过程:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
[Planner] 分解目标为3个步骤:
Step 1: 查询Q1销售数据 (tool: database_query)
Step 2: 分析数据生成报告 (tool: llm_analyze)
Step 3: 发送邮件 (tool: email_send)
[Executor] Step 1: 执行SQL...
SELECT product, SUM(amount) as total, COUNT(*) as orders
FROM sales WHERE quarter='Q1' GROUP BY product
→ 返回12条产品销售数据
[Executor] Step 2: 分析数据...
"Q1总销售额¥2,340万,同比增长23%。Top3: MacBook(¥580万)..."
[Executor] Step 3: 发送邮件...
→ 发送至 management@company.com,附件: Q1-Sales-Report.pdf
[Evaluator] 评估: satisfied=true, score=95
"目标完成,数据准确,报告已发送"
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
多模型路由与Fallback策略
生产环境不能把所有鸡蛋放在一个篮子里——GPT-4o挂了,通义千问顶上。
多模型路由架构
┌──────────────────────────────────────────────────────────────┐
│ 多模型路由架构 │
├──────────────────────────────────────────────────────────────┤
│ │
│ 请求 ──→ ModelRouter ──→ ┌─────────┐ │
│ │ 路由策略 │ │
│ └────┬────┘ │
│ ┌──────────────┼──────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ GPT-4o │ │ 通义千问 │ │ DeepSeek │ │
│ │ (高质量) │ │ (国产) │ │ (高性价比)│ │
│ └──────────┘ └──────────┘ └──────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌─────────────────────────────────────────┐ │
│ │ Fallback Chain │ │
│ │ GPT-4o → 通义千问 → DeepSeek → 本地Ollama│ │
│ └─────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────┘
路由策略实现
@Service
public class ModelRouterService {
private final Map<String, ChatModel> models;
private final CircuitBreakerRegistry breakerRegistry;
public ModelRouterService(
@Qualifier("openai") ChatModel openai,
@Qualifier("tongyi") ChatModel tongyi,
@Qualifier("deepseek") ChatModel deepseek,
@Qualifier("ollama") ChatModel ollama,
CircuitBreakerRegistry breakerRegistry) {
this.models = Map.of(
"gpt-4o", openai,
"qwen-max", tongyi,
"deepseek-v3", deepseek,
"llama3", ollama
);
this.breakerRegistry = breakerRegistry;
}
public String chat(String prompt, RoutingStrategy strategy) {
List<String> modelChain = strategy.resolveChain(prompt);
for (String modelName : modelChain) {
try {
CircuitBreaker breaker = breakerRegistry.circuitBreaker(modelName);
ChatModel model = models.get(modelName);
String result = breaker.executeSupplier(() ->
model.call(new Prompt(prompt))
.getResult()
.getOutput()
.getText()
);
log.info("Model {} succeeded for prompt: {}",
modelName, truncate(prompt, 50));
return result;
} catch (CallNotPermittedException e) {
log.warn("Model {} circuit breaker open, trying next",
modelName);
} catch (Exception e) {
log.error("Model {} failed: {}", modelName, e.getMessage());
}
}
throw new AllModelsFailedException("All models in chain failed");
}
}
智能路由策略
@Component
public class SmartRoutingStrategy implements RoutingStrategy {
@Override
public List<String> resolveChain(String prompt) {
Complexity complexity = analyzeComplexity(prompt);
return switch (complexity) {
case HIGH -> List.of("gpt-4o", "qwen-max", "deepseek-v3", "llama3");
case MEDIUM -> List.of("qwen-max", "deepseek-v3", "gpt-4o");
case LOW -> List.of("deepseek-v3", "qwen-max", "llama3");
};
}
private Complexity analyzeComplexity(String prompt) {
if (prompt.length() > 500 || containsCodeRequest(prompt)) {
return Complexity.HIGH;
} else if (prompt.length() > 100 || containsReasoning(prompt)) {
return Complexity.MEDIUM;
}
return Complexity.LOW;
}
private boolean containsCodeRequest(String prompt) {
return prompt.toLowerCase().contains("代码") ||
prompt.toLowerCase().contains("code") ||
prompt.toLowerCase().contains("实现");
}
private boolean containsReasoning(String prompt) {
return prompt.toLowerCase().contains("分析") ||
prompt.toLowerCase().contains("比较") ||
prompt.toLowerCase().contains("为什么");
}
enum Complexity { HIGH, MEDIUM, LOW }
}
熔断器配置
resilience4j:
circuitbreaker:
instances:
gpt-4o:
failure-rate-threshold: 50
slow-call-rate-threshold: 80
slow-call-duration-threshold: 10s
wait-duration-in-open-state: 30s
sliding-window-size: 10
qwen-max:
failure-rate-threshold: 50
slow-call-rate-threshold: 80
slow-call-duration-threshold: 8s
wait-duration-in-open-state: 20s
sliding-window-size: 10
deepseek-v3:
failure-rate-threshold: 60
slow-call-duration-threshold: 5s
wait-duration-in-open-state: 15s
sliding-window-size: 10
多模型成本对比
| 模型 | 输入价格(/1M tokens) | 输出价格(/1M tokens) | 质量 | 延迟 | 适用场景 |
|---|---|---|---|---|---|
| GPT-4o | $2.50 | $10.00 | ⭐⭐⭐⭐⭐ | 1-3s | 复杂推理、代码生成 |
| 通义千问-Max | ¥8.00 | ¥32.00 | ⭐⭐⭐⭐ | 0.5-2s | 中文场景、合规 |
| DeepSeek-V3 | ¥1.00 | ¥2.00 | ⭐⭐⭐⭐ | 0.3-1s | 日常对话、高并发 |
| Llama3(本地) | 免费 | 免费 | ⭐⭐⭐ | 2-5s | 隐私敏感、离线 |
生产级部署与性能调优
从Demo到Production,差距就在这些细节里。
Docker部署
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY target/app.jar app.jar
ENV JAVA_OPTS="-XX:+UseZGC \
-XX:+ZGenerational \
-XX:MaxRAMPercentage=75.0 \
-XX:+EnableVirtualThreads \
-Dspring.profiles.active=prod"
EXPOSE 8080
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]
# docker-compose.yml
services:
app:
build: .
ports: ["8080:8080"]
environment:
OPENAI_API_KEY: ${OPENAI_API_KEY}
SPRING_AI_OPENAI_BASE-URL: https://api.openai.com
depends_on:
redis: { condition: service_healthy }
postgres: { condition: service_healthy }
deploy:
resources:
limits: { memory: 1G, cpus: "2.0" }
healthcheck:
test: ["CMD", "wget", "-q", "--spider", "http://localhost:8080/actuator/health"]
interval: 10s
timeout: 5s
retries: 3
redis:
image: redis:7-alpine
ports: ["6379:6379"]
healthcheck:
test: ["CMD", "redis-cli", "ping"]
postgres:
image: pgvector/pgvector:pg16
ports: ["5432:5432"]
environment:
POSTGRES_DB: ai_vectors
POSTGRES_PASSWORD: ${PG_PASSWORD}
volumes:
- pgdata:/var/lib/postgresql/data
volumes:
pgdata:
限流配置
@Configuration
public class RateLimitConfig {
@Bean
public RateLimiter aiApiRateLimiter() {
return RateLimiter.builder()
.name("ai-api")
.limitForPeriod(50)
.limitRefreshPeriod(Duration.ofSeconds(1))
.timeoutDuration(Duration.ofSeconds(5))
.build();
}
}
@RestController
@RequestMapping("/api/chat")
public class RateLimitedChatController {
private final ChatClient chatClient;
private final RateLimiter rateLimiter;
@PostMapping
@RateLimiter(name = "ai-api", fallbackMethod = "rateLimitFallback")
public String chat(@RequestBody ChatRequest request) {
return chatClient.prompt()
.user(request.message())
.call()
.content();
}
public String rateLimitFallback(ChatRequest request, Exception e) {
return "当前请求过多,请稍后再试。";
}
}
请求超时与重试
@Configuration
public class AiRetryConfig {
@Bean
public Retry aiRetry() {
return Retry.builder()
.name("ai-retry")
.maxAttempts(3)
.waitDuration(Duration.ofMillis(500))
.retryOnException(e ->
e instanceof ApiCallException ||
e instanceof SocketTimeoutException)
.build();
}
}
监控指标
@Component
public class AiMetrics {
private final MeterRegistry registry;
public AiMetrics(MeterRegistry registry) {
this.registry = registry;
}
public void recordApiCall(String model, boolean success, long latencyMs) {
registry.counter("ai.api.calls",
"model", model,
"status", success ? "success" : "failure"
).increment();
registry.timer("ai.api.latency",
"model", model
).record(latencyMs, TimeUnit.MILLISECONDS);
}
public void recordTokenUsage(String model, int promptTokens, int completionTokens) {
registry.counter("ai.tokens.prompt", "model", model)
.increment(promptTokens);
registry.counter("ai.tokens.completion", "model", model)
.increment(completionTokens);
}
public void recordFunctionCall(String functionName, boolean success) {
registry.counter("ai.function.calls",
"function", functionName,
"status", success ? "success" : "failure"
).increment();
}
}
生产级性能清单
| 类别 | 检查项 | 目标 | 工具 |
|---|---|---|---|
| 延迟 | P95 API响应时间 | < 3s | Actuator + Grafana |
| 延迟 | 流式首Token时间 | < 500ms | 自定义Metrics |
| 吞吐 | 并发请求处理能力 | > 100 QPS | JMeter/k6 |
| 可靠性 | 熔断器触发率 | < 5% | Resilience4j |
| 可靠性 | Fallback成功率 | > 99% | 自定义Metrics |
| 成本 | 日均Token消耗 | < 预算 | Token计数器 |
| 成本 | 模型路由命中率 | 高复杂→GPT-4o >80% | 路由Metrics |
| 安全 | API Key轮换 | 90天一次 | Vault/KMS |
| 安全 | 敏感信息过滤 | 100%拦截 | 输入/输出Guard |
| 运维 | 滚动部署零中断 | 0 error | K8s ReadinessProbe |
| 运维 | 配置热更新 | 无需重启 | Spring Cloud Config |
总结与架构全景图
全景架构
┌──────────────────────────────────────────────────────────────────┐
│ Spring Boot 3 + AI 全景架构 │
├──────────────────────────────────────────────────────────────────┤
│ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ API Gateway / 负载均衡 │ │
│ └──────────────────────┬─────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────────┐ │
│ │ Spring Boot 3 Application │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │ Chat │ │ RAG │ │ Agent │ │ │
│ │ │ Controller│ │ Service │ │ Service │ │ │
│ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │
│ │ │ │ │ │ │
│ │ ┌────▼─────────────▼─────────────▼─────┐ │ │
│ │ │ ChatClient (Spring AI) │ │ │
│ │ │ ┌─────────────────────────────┐ │ │ │
│ │ │ │ Advisor Chain │ │ │ │
│ │ │ │ Memory → RAG → Function │ │ │ │
│ │ │ │ → Guard → Logging │ │ │ │
│ │ │ └─────────────────────────────┘ │ │ │
│ │ └──────────────┬──────────────────────┘ │ │
│ │ │ │ │
│ │ ┌──────────────▼──────────────────────┐ │ │
│ │ │ ModelRouter │ │ │
│ │ │ GPT-4o │ 通义千问 │ DeepSeek │ Ollama│ │ │
│ │ └─────────────────────────────────────┘ │ │
│ └──────────────────────────────────────────────────────┘ │
│ │ │
│ ┌──────────────────────▼─────────────────────────────────┐ │
│ │ 基础设施层 │ │
│ │ Redis(记忆) │ PGVector(向量) │ MySQL(业务) │ K8s │ │
│ └────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────┘
核心要点回顾
- Java是AI应用的最佳生产载体 — 并发、安全、运维三位一体,Python训练用,Java应用用
- Spring AI是2026年首选框架 — Spring官方出品,声明式配置,零学习成本
- 多轮对话必须有持久化记忆 — Redis ChatMemory,窗口/摘要/混合策略按场景选
- RAG是企业AI的基石 — ETL Pipeline + 向量检索 + 上下文注入,缺一不可
- Function Calling连接模型与世界 — 模型决策何时调用,你定义调用什么
- Agent = Planner + Executor + Evaluator — 从被动响应到主动执行
- 多模型路由是生产标配 — GPT-4o + 通义千问 + DeepSeek,Fallback链保可用性
- 生产级部署不能省 — Docker + 熔断 + 限流 + 监控,一个都不能少
Spring Boot 3 + AI大模型,不是"Java追赶AI",而是"AI终于找到了最可靠的生产运行时"。2026年,Java开发者的AI时代,已经到来。
本站提供浏览器本地工具,免注册即可试用 →
#Spring Boot#Spring AI#LangChain4j#AI大模型#RAG#Function Calling#Java Agent