一、引言: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生态系统。
你的回答应该:

  1. 准确且实用
  2. 包含代码示例
  3. 考虑生产环境的最佳实践
    “”";

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
            {}

十、总结

在这里插入图片描述

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐