Spring AI + JManus 从入门到实战
Spring AI + JManus 从入门到实战
一、引言:AI时代的Java开发
1.1 为什么需要 Spring AI?
在ChatGPT爆火之后,大语言模型(LLM)已经成为应用开发的核心能力。然而,对于Java开发者来说,直接集成各种AI服务面临诸多挑战:
Spring AI 应运而生!它是Spring生态系统中AI应用的终极解决方案,提供:
统一的API抽象:一个接口调用所有AI模型
企业级功能:对话管理、RAG、流式响应
Spring生态集成:自动配置、依赖注入、配置管理
生产就绪:可观测性、弹性、安全性
1.2 什么是 JManus?
JManus 是一个轻量级的AI引擎框架,专门为Java开发者设计。它提供了:
智能路由:自动选择最合适的AI模型
Prompt管理:结构化模板,支持变量替换
上下文管理:自动处理对话历史
缓存优化:减少重复调用,降低成本
异步支持:高性能并发处理
Spring AI + JManus 的组合,让Java开发者可以像使用Spring Data JPA一样简单地使用AI能力!
二、Spring AI 框架简介
2.1 核心架构
Spring AI采用了经典的分层架构,确保了高度的可扩展性和灵活性。
2.2 核心组件
Spring AI由以下核心组件构成:
AiService:AI服务接口,统一的调用入口
public interface AiService {
String chat(String message);
String chat(String systemPrompt, String userMessage);
Stream<String> chatStream(String message);
}
JManusEngine:智能引擎核心,负责模型选择和路由
@Service
public class JManusEngine {
private final List<AiModelProvider> providers;
public String generate(PromptTemplate template) {
AiModelProvider provider = selectBestProvider(template);
return provider.call(template);
}
}
PromptTemplate:提示词模板管理
PromptTemplate template = PromptTemplate.from("你是一个{{role}},请{{task}}")
.withVariable("role", "翻译助手")
.withVariable("task", "翻译以下文本");
ChatMemory:对话记忆管理
ChatMemory memory = new InMemoryChatMemory();
memory.add("user", "你好");
memory.add("assistant", "你好!有什么我可以帮助你的吗?");
三、JManus 引擎核心概念
3.1 智能模型选择
JManus Engine的核心优势在于智能模型选择。它可以根据以下因素自动选择最合适的模型:
3.2 Prompt模板系统
JManus提供了强大的Prompt模板系统:
// 基础模板
PromptTemplate template = PromptTemplate.from(
"你是一个专业的{{role}}," +
"请帮我{{task}}。" +
"要求:{{requirements}}"
);
// 变量替换
String prompt = template
.withVariable("role", "Java开发专家")
.withVariable("task", "设计一个钱包系统")
.withVariable("requirements", "支持冷热钱包分离")
.build();
3.3 对话上下文管理
自动管理对话历史,确保上下文连贯:
@Service
publicclass ConversationService {
privatefinal Map<String, List<Message>> conversations = new ConcurrentHashMap<>();
public String chat(String sessionId, String message) {
List<Message> history = conversations.get(sessionId);
// 构建包含历史的Prompt
Prompt prompt = buildPromptWithHistory(history, message);
// 调用AI
String response = aiService.chat(prompt);
// 保存历史
history.add(new Message("user", message));
history.add(new Message("assistant", response));
return response;
}
}
四、快速开始:5分钟搭建第一个AI应用
4.1 环境准备
快速入门图:
4.2 创建项目
步骤1:使用Spring Initializr创建项目
访问 https://start.spring.io/,选择:
Spring Boot 3.2+
添加依赖:Spring Web, Thymeleaf
或使用Maven命令:
mvn archetype:generate \
-DgroupId=com.jmanus \
-DartifactId=jmanus-demo \
-DarchetypeArtifactId=maven-archetype-quickstart \
-Dversion=1.0.0
步骤2:添加依赖
在 pom.xml 中添加:
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI OpenAI -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-bom</artifactId>
<version>1.0.0-M4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<repositories>
<repository>
<id>spring-milestones</id>
<name>Spring Milestones</name>
<url>https://repo.spring.io/milestone</url>
</repository>
</repositories>
步骤3:配置API密钥
在 application.yml 中配置:
spring:
ai:
openai:
api-key:${OPENAI_API_KEY:your-api-key-here}
base-url:${OPENAI_BASE_URL:https://api.openai.com}
chat:
options:
model:gpt-3.5-turbo
temperature:0.7
max-tokens:2000
步骤4:创建AI服务
@Service
publicclass AiChatService {
privatefinal ChatClient chatClient;
public AiChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String chat(String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return chatClient.call(prompt)
.getResult()
.getOutput()
.getContent();
}
}
步骤5:创建Controller
@RestController
@RequestMapping("/ai")
public class AiController {
@Autowired
private AiChatService aiChatService;
@PostMapping("/chat")
public String chat(@RequestParam String message) {
return aiChatService.chat(message);
}
}
步骤6:启动应用
mvn spring-boot:run
访问 http://localhost:8080/ai/chat?message=你好 即可看到AI回复!
五、核心功能实战
5.1 系统交互流程
理解系统交互流程是掌握Spring AI + JManus的关键:
5.2 多轮对话实现
完整的对话系统需要维护会话状态:
@Service
publicclass ConversationService {
privatefinal Map<String, List<Message>> conversations = new ConcurrentHashMap<>();
@Autowired
private ChatClient chatClient;
public ChatResponse chat(ChatRequest request) {
String sessionId = request.getSessionId();
// 获取或创建会话
List<Message> history = conversations.computeIfAbsent(
sessionId, k -> new ArrayList<>()
);
// 构建Prompt(包含历史)
List<org.springframework.ai.chat.messages.Message> messages = new ArrayList<>();
// 添加系统提示
if (request.getSystemPrompt() != null) {
messages.add(new SystemMessage(request.getSystemPrompt()));
}
// 添加历史消息
for (Message msg : history) {
if ("user".equals(msg.getRole())) {
messages.add(new UserMessage(msg.getContent()));
} else {
messages.add(new AssistantMessage(msg.getContent()));
}
}
// 添加当前消息
messages.add(new UserMessage(request.getMessage()));
// 调用AI
Prompt prompt = new Prompt(messages);
String response = chatClient.call(prompt)
.getResult()
.getOutput()
.getContent();
// 保存到历史
history.add(new Message("user", request.getMessage()));
history.add(new Message("assistant", response));
// 限制历史长度
if (history.size() > 20) {
conversations.put(sessionId,
history.subList(history.size() - 20, history.size()));
}
return ChatResponse.builder()
.content(response)
.sessionId(sessionId)
.build();
}
}
5.3 流式响应
流式响应可以显著提升用户体验:
@GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamChat(@RequestParam String message) {
return Flux.create(sink -> {
ChatClient caller = chatClient;
// 模拟流式响应
caller.stream(new Prompt(new UserMessage(message)))
.subscribe(chatResponse -> {
String content = chatResponse.getResult()
.getOutput().getContent();
sink.next(content);
}, error -> {
sink.error(error);
}, () -> {
sink.complete();
});
});
}
前端使用SSE接收:
const eventSource = new EventSource('/ai/stream?message=你好');
eventSource.onmessage = function(event) {
const response = event.data;
// 逐步显示响应
appendToChat(response);
};
六、生产级架构设计
6.1 生产环境部署
生产环境需要考虑高可用、可扩展性:
6.2 核心设计原则
1. 分层解耦
Controller层(接口层)
↓
Service层(业务逻辑)
↓
Engine层(AI处理)
↓
Provider层(模型适配)
2. 缓存策略
@Service
public class CachedAiService {
@Cacheable(value = "ai-responses", key = "#message")
public String chat(String message) {
return aiChatService.chat(message);
}
@CacheEvict(value = "ai-responses", key = "#sessionId")
public void clearCache(String sessionId) {
// 清除会话缓存
}
}
3. 异步处理
@Service
publicclass AsyncAiService {
@Async("aiExecutor")
public CompletableFuture<String> chatAsync(String message) {
String response = aiChatService.chat(message);
return CompletableFuture.completedFuture(response);
}
}
@Configuration
@EnableAsync
publicclass AsyncConfig {
@Bean("aiExecutor")
public Executor aiExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(10);
executor.setMaxPoolSize(50);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("ai-async-");
executor.initialize();
return executor;
}
}
七、完整项目实战
7.1 数据流转分析
理解数据如何流转对构建健壮的AI应用至关重要:
7.2 完整的AI请求处理流程
7.3 核心代码实现
AiChatService 完整实现:
package com.jmanus.service;
import com.jmanus.model.ChatRequest;
import com.jmanus.model.ChatResponse;
import com.jmanus.model.Message;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
@Service
publicclass AiChatService {
privatefinal ChatClient chatClient;
privatefinal Map<String, List<Message>> conversationHistory = new ConcurrentHashMap<>();
@Autowired
public AiChatService(ChatClient chatClient) {
this.chatClient = chatClient;
}
/**
* 简单对话
*/
public String chat(String message) {
Prompt prompt = new Prompt(new UserMessage(message));
return chatClient.call(prompt).getResult().getOutput().getContent();
}
/**
* 带系统提示的对话
*/
public String chat(String systemPrompt, String userMessage) {
List<org.springframework.ai.chat.messages.Message> messages = new ArrayList<>();
messages.add(new SystemMessage(systemPrompt));
messages.add(new UserMessage(userMessage));
Prompt prompt = new Prompt(messages);
return chatClient.call(prompt).getResult().getOutput().getContent();
}
/**
* 带上下文的对话
*/
public String chatWithContext(String sessionId, String message) {
List<Message> history = conversationHistory.computeIfAbsent(sessionId, k -> new ArrayList<>());
List<org.springframework.ai.chat.messages.Message> messages = new ArrayList<>();
// 添加历史消息
for (Message msg : history) {
if ("user".equals(msg.getRole())) {
messages.add(new UserMessage(msg.getContent()));
} elseif ("assistant".equals(msg.getRole())) {
messages.add(new org.springframework.ai.chat.messages.AssistantMessage(msg.getContent()));
}
}
// 添加当前消息
messages.add(new UserMessage(message));
Prompt prompt = new Prompt(messages);
String response = chatClient.call(prompt).getResult().getOutput().getContent();
// 保存历史
history.add(new Message("user", message));
history.add(new Message("assistant", response));
// 限制历史长度
if (history.size() > 20) {
history = history.subList(history.size() - 20, history.size());
conversationHistory.put(sessionId, history);
}
return response;
}
/**
* 完整的聊天响应
*/
public ChatResponse chatResponse(ChatRequest request) {
long startTime = System.currentTimeMillis();
try {
String content;
if (request.getSessionId() != null && !request.getSessionId().isEmpty()) {
content = chatWithContext(request.getSessionId(), request.getMessage());
} elseif (request.getSystemPrompt() != null) {
content = chat(request.getSystemPrompt(), request.getMessage());
} else {
content = chat(request.getMessage());
}
long duration = System.currentTimeMillis() - startTime;
return ChatResponse.builder()
.success(true)
.content(content)
.sessionId(request.getSessionId())
.duration(duration)
.model("gpt-3.5-turbo")
.tokens(content.length() / 2)
.build();
} catch (Exception e) {
long duration = System.currentTimeMillis() - startTime;
return ChatResponse.builder()
.success(false)
.error(e.getMessage())
.duration(duration)
.build();
}
}
/**
* 清除对话历史
*/
public void clearHistory(String sessionId) {
conversationHistory.remove(sessionId);
}
/**
* 获取对话历史
*/
public List<Message> getHistory(String sessionId) {
return conversationHistory.getOrDefault(sessionId, new ArrayList<>());
}
}
AiChatController 完整实现:
package com.jmanus.controller;
import com.jmanus.model.ChatRequest;
import com.jmanus.model.ChatResponse;
import com.jmanus.model.Message;
import com.jmanus.service.AiChatService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@Slf4j
@Controller
@RequestMapping("/ai")
publicclass AiChatController {
@Autowired
private AiChatService aiChatService;
@GetMapping
public String index() {
return"index";
}
@PostMapping("/chat")
@ResponseBody
public ChatResponse chat(@RequestBody ChatRequest request) {
log.info("收到聊天请求: sessionId={}, message={}",
request.getSessionId(), request.getMessage());
if (request.getSessionId() == null || request.getSessionId().isEmpty()) {
request.setSessionId(UUID.randomUUID().toString());
}
return aiChatService.chatResponse(request);
}
@GetMapping("/history/{sessionId}")
@ResponseBody
public List<Message> getHistory(@PathVariable String sessionId) {
return aiChatService.getHistory(sessionId);
}
@DeleteMapping("/history/{sessionId}")
@ResponseBody
public Map<String, Object> clearHistory(@PathVariable String sessionId) {
aiChatService.clearHistory(sessionId);
return Map.of("success", true, "message", "对话历史已清除");
}
@GetMapping("/health")
@ResponseBody
public Map<String, Object> health() {
return Map.of(
"status", "UP",
"service", "Spring AI + JManus",
"timestamp", System.currentTimeMillis()
);
}
}
7.4 前端页面
八、性能优化与最佳实践
8.1 缓存策略
Redis缓存AI响应:
@Service
publicclass CachedAiService {
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Autowired
private AiChatService aiChatService;
public String chat(String message) {
// 生成缓存key
String cacheKey = "ai:response:" + DigestUtils.md5Hex(message);
// 尝试从缓存获取
String cached = redisTemplate.opsForValue().get(cacheKey);
if (cached != null) {
return cached;
}
// 调用AI
String response = aiChatService.chat(message);
// 缓存结果(1小时)
redisTemplate.opsForValue().set(cacheKey, response, 1, TimeUnit.HOURS);
return response;
}
}
8.2 连接池优化
spring:
ai:
openai:
# 连接池配置
client:
connect-timeout: 10s
read-timeout: 60s
max-connections: 50
8.3 Prompt工程最佳实践
1. 清晰的角色定义
String systemPrompt = “”"
你是一个专业的Java技术顾问,专注于Spring生态系统。
你的回答应该:
- 准确且实用
- 包含代码示例
- 考虑生产环境的最佳实践
“”";
2. 结构化输出
String prompt = “”"
请以JSON格式返回以下信息:
{
“summary”: “问题摘要”,
“solution”: “解决方案”,
“code”: “代码示例”,
“resources”: [“参考资源1”, “参考资源2”]
}
“”";
3. 上下文限制
// 限制历史长度,避免token超限
if (history.size() > MAX_HISTORY) {
history = history.subList(history.size() - MAX_HISTORY, history.size());
}
九、企业级实战场景
9.1 场景一:智能客服系统
业务需求:构建一个企业级智能客服系统,支持意图识别、自动路由和知识库问答。
@Service
publicclass IntelligentCustomerService {
@Autowired
private ChatClient chatClient;
@Autowired
private KnowledgeBaseService knowledgeBaseService;
@Autowired
private TicketService ticketService;
/**
* 智能客服路由 - 根据用户问题自动分类处理
*/
public CustomerServiceResponse handleCustomerQuery(CustomerQuery query) {
// 步骤1: 意图识别
String intent = classifyIntent(query.getMessage());
// 步骤2: 根据意图路由
returnswitch (intent) {
case"FAQ" -> handleFAQQuery(query);
case"TECHNICAL" -> handleTechnicalIssue(query);
case"COMPLAINT" -> handleComplaint(query);
case"HUMAN" -> transferToHuman(query);
default -> handleGeneralQuery(query);
};
}
/**
* 意图分类 - 使用AI识别用户意图
*/
private String classifyIntent(String message) {
String systemPrompt = """
你是一个客服意图分类专家。请根据用户消息,将意图分类为以下类型之一:
- FAQ: 常见问题咨询(如:营业时间、联系方式)
- TECHNICAL: 技术问题(如:系统故障、功能使用)
- COMPLAINT: 投诉建议
- HUMAN: 需要人工介入(如:复杂问题、紧急情况)
- GENERAL: 一般咨询
只返回分类结果,不要其他内容。
""";
String response = chatClient.call(
new Prompt(List.of(
new SystemMessage(systemPrompt),
new UserMessage(message)
))
).getResult().getOutput().getContent();
return response.toUpperCase().trim();
}
/**
* 处理FAQ查询 - 结合知识库
*/
private CustomerServiceResponse handleFAQQuery(CustomerQuery query) {
// 先从知识库搜索
List<String> relevantDocs = knowledgeBaseService.search(query.getMessage(), 3);
// 如果找到匹配的知识库文档
if (!relevantDocs.isEmpty()) {
String context = String.join("\n\n", relevantDocs);
String systemPrompt = """
你是一个客服助手。请根据以下知识库内容回答用户问题。
如果知识库中没有相关信息,请礼貌地告知用户。
知识库内容:
""" + context + """
请用简洁、友好的语气回答。
""";
String answer = chatClient.call(
new Prompt(List.of(
new SystemMessage(systemPrompt),
new UserMessage(query.getMessage())
))
).getResult().getOutput().getContent();
return CustomerServiceResponse.builder()
.answer(answer)
.source("KNOWLEDGE_BASE")
.confidence(0.95)
.build();
}
// 知识库没有匹配,使用AI回答
return handleGeneralQuery(query);
}
/**
* 处理技术问题 - 引导用户提供详细信息
*/
private CustomerServiceResponse handleTechnicalIssue(CustomerQuery query) {
String systemPrompt = """
你是一个技术支持专家。用户遇到了技术问题。
请先表示理解,然后引导用户提供以下信息:
1. 问题描述
2. 复现步骤
3. 错误信息
4. 系统环境
如果用户已提供这些信息,尝试给出解决方案。
""";
String answer = chatClient.call(
new Prompt(List.of(
new SystemMessage(systemPrompt),
new UserMessage(query.getMessage())
))
).getResult().getOutput().getContent();
// 自动创建工单
String ticketId = ticketService.createTicket(
query.getUserId(),
"TECHNICAL",
query.getMessage()
);
return CustomerServiceResponse.builder()
.answer(answer + "\n\n工单编号:" + ticketId)
.source("AI_AGENT")
.ticketId(ticketId)
.confidence(0.85)
.build();
}
/**
* 处理投诉 - 转高级处理
*/
private CustomerServiceResponse handleComplaint(CustomerQuery query) {
// 优先级提升
String ticketId = ticketService.createPriorityTicket(
query.getUserId(),
"COMPLAINT",
query.getMessage(),
"HIGH"
);
String response = """
感谢您的反馈。
您的意见对我们非常重要,我们已记录并会尽快处理。
工单编号:%s
预计回复时间:2小时内
""".formatted(ticketId);
return CustomerServiceResponse.builder()
.answer(response)
.source("HUMAN_ROUTING")
.ticketId(ticketId)
.priority("HIGH")
.build();
}
/**
* 转人工客服
*/
private CustomerServiceResponse transferToHuman(CustomerQuery query) {
// 通知人工客服
notificationService.notifyHumanAgent(query);
return CustomerServiceResponse.builder()
.answer("正在为您转接人工客服,请稍候...")
.source("HUMAN_TRANSFER")
.estimatedWaitTime("30秒")
.build();
}
/**
* 处理一般咨询
*/
private CustomerServiceResponse handleGeneralQuery(CustomerQuery query) {
String systemPrompt = """
你是一个友好的客服助手。请用专业、友好的语气回答用户问题。
如果不确定答案,建议用户联系人工客服。
""";
String answer = chatClient.call(
new Prompt(List.of(
new SystemMessage(systemPrompt),
new UserMessage(query.getMessage())
))
).getResult().getOutput().getContent();
return CustomerServiceResponse.builder()
.answer(answer)
.source("AI_AGENT")
.confidence(0.75)
.build();
}
}
9.2 场景二:API文档自动生成
业务需求:根据代码自动生成API文档。
@Service
public class ApiDocGenerator {
@Autowired
private ChatClient chatClient;
/**
* 为Controller生成API文档
*/
public ApiDocumentation generateDocs(Class<?> controllerClass) {
// 获取源代码
String sourceCode = getSourceCode(controllerClass);
// 获取方法签名
List<MethodSignature> methods = extractMethodSignatures(controllerClass);
String systemPrompt = """
你是API文档专家。请根据以下Controller代码生成详细的API文档。
文档格式要求:
## 接口名称
**描述**:接口功能说明
**请求方式**:GET/POST/PUT/DELETE
**请求路径**:/api/path
**请求参数**:
| 参数名 | 类型 | 必填 | 说明 |
**响应示例**:
```json
{}
十、总结

更多推荐


所有评论(0)