1. 为什么你的SpringBoot项目需要一个“AI大脑”?

最近几年,AI大模型的发展速度真的太快了,从最初的ChatGPT到现在的各种国产模型,感觉每个月都有新东西出来。我身边很多做Java开发的朋友都在问,怎么才能把这些强大的AI能力快速集成到自己的SpringBoot项目里?毕竟,谁不想让自己的系统变得更“聪明”一点呢?

想象一下,如果你的在线教育平台能自动生成高质量的练习题,如果你的客服系统能理解用户的情绪并给出更贴心的回答,如果你的内容管理平台能自动撰写文章摘要……这些场景听起来很酷,但实现起来是不是感觉门槛很高?其实不然。我今天要跟你分享的,就是如何用SpringBoot这个我们最熟悉的框架,给项目装上“AI大脑”。整个过程就像搭积木一样,有现成的模块可以直接用,你只需要关心业务逻辑怎么设计就行。

我做过不少这类项目,从最初的直接调用API,到后来封装成通用模块,踩过不少坑,也总结出了一套比较成熟的做法。这篇文章我会手把手带你走一遍,从环境准备、API接入,到模块封装、Prompt设计,最后前后端联调,每个环节我都会给出详细的代码和配置。你完全可以把这篇文章当作一个实战指南,跟着做下来,就能在自己的项目里用上AI能力了。

2. 环境准备与模型平台选择

2.1 主流AI平台对比

在开始写代码之前,我们得先选一个AI平台。现在国内外的选择很多,各有各的特点。我整理了一个简单的对比表格,帮你快速了解:

平台名称特点适合场景接入难度
智谱AI (ChatGLM)清华系背景,中文理解强,有免费额度中文对话、代码生成、内容创作★★☆☆☆
百度文心一言百度生态完善,文档齐全搜索增强、知识问答、多模态★★★☆☆
阿里通义千问阿里云集成好,企业级支持电商、客服、企业应用★★★☆☆
深度求索 (DeepSeek)完全免费,API开放学习实验、个人项目、成本敏感★★☆☆☆
OpenAI GPT系列效果稳定,生态成熟国际化项目、复杂推理★★★★☆

我个人的经验是,如果是国内项目,特别是对中文支持要求高的,智谱AI和文心一言都是不错的选择。它们提供了完整的Java SDK,文档也比较友好。这篇文章我会以智谱AI为例来演示,但我要强调的是,封装思路是通用的。你学会了这套方法,换到其他平台也就是改个依赖和配置的事儿。

2.2 创建SpringBoot项目

我们先从创建一个干净的SpringBoot项目开始。我习惯用Spring Initializr,选上这几个依赖:

  • Spring Web
  • Lombok(简化代码的神器)
  • Configuration Processor(让配置有提示)
<!-- 这是pom.xml的基础部分 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.0</version>
</parent>

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

项目创建好后,我建议你先建立一个清晰的包结构。这是我的习惯:

src/main/java/com/yourcompany/ai/
├── config/          # 配置类
├── manager/         # AI管理模块
├── model/          # 请求响应实体
├── constant/       # 常量定义
└── controller/     # 对外接口

好的结构能让后续的维护轻松很多。特别是当AI功能越来越多时,你不会被一堆散落的代码搞晕。

2.3 获取API密钥

接下来是获取API密钥,这一步很重要,也需要注意安全。以智谱AI为例,你需要:

  1. 访问智谱AI开放平台官网
  2. 注册账号并完成实名认证(国内平台基本都需要)
  3. 在控制台创建应用,然后就能看到API Key了

这里有个重要提醒:API Key就像你的银行卡密码,绝对不能直接写在代码里提交到Git!我见过太多因为密钥泄露导致账单暴增的案例。正确的做法是放在环境变量或者配置文件中,然后通过.gitignore排除。

我通常这样处理:在application.yml里配置,但实际的值从环境变量读取:

# application.yml
ai:
  zhipu:
    api-key: ${ZHIPU_API_KEY:}  # 优先从环境变量读取
    base-url: https://open.bigmodel.cn/api/paas/v4

然后在启动项目时设置环境变量。本地开发可以用.env文件,生产环境用K8s ConfigMap或者云平台的密钥管理服务。

3. 基础接入:第一个AI对话接口

3.1 添加SDK依赖

选好平台后,第一步就是引入对应的SDK。智谱AI的Java SDK在Maven中央仓库,添加很方便:

<dependency>
    <groupId>cn.bigmodel.openapi</groupId>
    <artifactId>oapi-java-sdk</artifactId>
    <version>release-V4-2.3.0</version>
</dependency>

如果你用的是其他平台,比如百度文心一言,依赖可能是这样的:

<!-- 百度文心一言示例 -->
<dependency>
    <groupId>com.baidu.aip</groupId>
    <artifactId>java-sdk</artifactId>
    <version>4.16.5</version>
</dependency>

添加完依赖,记得刷新Maven。有时候SDK版本更新比较快,建议去官方文档确认最新版本号。我遇到过因为用了旧版本SDK导致某些新功能不可用的情况。

3.2 编写配置类

有了依赖,接下来要创建配置类。这个类的目的是把API Key等配置信息管理起来,并且创建好AI客户端实例。我习惯用@ConfigurationProperties,这样配置可以在yml文件里自动补全:

@Configuration
@ConfigurationProperties(prefix = "ai.zhipu")
@Data
public class ZhipuAiConfig {
    
    private String apiKey;
    private String baseUrl;
    
    @Bean
    public ClientV4 zhipuClient() {
        return new ClientV4.Builder(apiKey)
                .build();
    }
}

注意这里的@Data是Lombok注解,自动生成getter/setter。如果你不用Lombok,需要自己写这些方法。

配置类写好后,在application.yml里加上配置:

ai:
  zhipu:
    api-key: "你的API密钥"
    base-url: "https://open.bigmodel.cn/api/paas/v4"

3.3 实现简单的对话接口

现在可以写第一个AI接口了。我们先创建一个Controller,实现最基本的问答功能:

@RestController
@RequestMapping("/api/ai")
public class ChatController {
    
    @Autowired
    private ClientV4 zhipuClient;
    
    @PostMapping("/chat")
    public String simpleChat(@RequestParam String question) {
        // 构造消息
        List<ChatMessage> messages = new ArrayList<>();
        ChatMessage userMessage = new ChatMessage(
            ChatMessageRole.USER.value(), 
            question
        );
        messages.add(userMessage);
        
        // 构建请求
        ChatCompletionRequest request = ChatCompletionRequest.builder()
                .model(Constants.ModelChatGLM4)
                .messages(messages)
                .stream(Boolean.FALSE)
                .build();
        
        try {
            // 调用API
            ModelApiResponse response = zhipuClient.invokeModelApi(request);
            return response.getData().getChoices().get(0).getMessage().getContent();
        } catch (Exception e) {
            throw new RuntimeException("AI服务调用失败", e);
        }
    }
}

写完后启动项目,用Postman测试一下:

POST http://localhost:8080/api/ai/chat?question=你好,介绍一下SpringBoot

如果一切正常,你应该能收到AI的回答。不过这个版本还很简陋,没有错误处理,也没有考虑性能问题。别急,我们一步步优化。

3.4 添加异常处理

在实际项目中,网络可能不稳定,API可能限流,各种异常都需要处理。我建议创建一个统一的异常处理机制:

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleAiException(Exception e) {
        ErrorResponse error = new ErrorResponse();
        error.setCode("AI_SERVICE_ERROR");
        error.setMessage("AI服务暂时不可用");
        error.setDetail(e.getMessage());
        error.setTimestamp(LocalDateTime.now());
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(error);
    }
}

@Data
class ErrorResponse {
    private String code;
    private String message;
    private String detail;
    private LocalDateTime timestamp;
}

这样前端收到错误时,能有一个统一的格式,方便展示给用户。

4. 封装通用AI管理模块

4.1 为什么需要封装?

直接在每个Controller里写AI调用代码行不行?技术上可以,但维护起来会很痛苦。想象一下,如果项目里有10个地方需要调用AI,每个地方都要写一遍构建请求、处理响应的代码,哪天API升级或者要换平台,你得改10个地方。

封装的目的就是一次编写,多处使用。好的封装应该:

  1. 隐藏底层API的复杂性
  2. 提供统一的调用接口
  3. 内置常用功能(重试、限流、监控)
  4. 方便切换AI平台

我设计了一个AiManager类,这是整个封装的核心。

4.2 核心管理器设计

先看AiManager的基本结构:

@Component
@Slf4j
public class AiManager {
    
    @Resource
    private ClientV4 zhipuClient;
    
    // 温度参数:控制输出的随机性
    private static final float DEFAULT_TEMPERATURE = 0.7f;
    private static final float CREATIVE_TEMPERATURE = 0.9f;  // 创意任务用
    private static final float PRECISE_TEMPERATURE = 0.3f;   // 精确任务用
    
    /**
     * 同步调用 - 最常用的方法
     */
    public String syncChat(String systemPrompt, String userMessage) {
        return syncChat(systemPrompt, userMessage, DEFAULT_TEMPERATURE);
    }
    
    /**
     * 带温度参数的同步调用
     */
    public String syncChat(String systemPrompt, String userMessage, float temperature) {
        List<ChatMessage> messages = buildMessages(systemPrompt, userMessage);
        
        ChatCompletionRequest request = ChatCompletionRequest.builder()
                .model(Constants.ModelChatGLM4)
                .messages(messages)
                .temperature(temperature)
                .stream(Boolean.FALSE)
                .build();
        
        try {
            ModelApiResponse response = zhipuClient.invokeModelApi(request);
            return extractContent(response);
        } catch (Exception e) {
            log.error("AI同步调用失败", e);
            throw new AiServiceException("AI服务调用失败", e);
        }
    }
    
    /**
     * 流式调用 - 适合长文本生成
     */
    public Flux<String> streamChat(String systemPrompt, String userMessage) {
        return Flux.create(sink -> {
            try {
                List<ChatMessage> messages = buildMessages(systemPrompt, userMessage);
                
                ChatCompletionRequest request = ChatCompletionRequest.builder()
                        .model(Constants.ModelChatGLM4)
                        .messages(messages)
                        .stream(Boolean.TRUE)
                        .build();
                
                ModelApiResponse response = zhipuClient.invokeModelApi(request);
                response.getFlowable().subscribe(
                    data -> sink.next(data.getContent()),
                    sink::error,
                    sink::complete
                );
            } catch (Exception e) {
                sink.error(e);
            }
        });
    }
    
    // 辅助方法
    private List<ChatMessage> buildMessages(String systemPrompt, String userMessage) {
        List<ChatMessage> messages = new ArrayList<>();
        if (StringUtils.hasText(systemPrompt)) {
            messages.add(new ChatMessage(ChatMessageRole.SYSTEM.value(), systemPrompt));
        }
        messages.add(new ChatMessage(ChatMessageRole.USER.value(), userMessage));
        return messages;
    }
    
    private String extractContent(ModelApiResponse response) {
        if (response == null || response.getData() == null 
                || response.getData().getChoices() == null
                || response.getData().getChoices().isEmpty()) {
            throw new AiServiceException("AI返回结果为空");
        }
        return response.getData().getChoices().get(0).getMessage().getContent();
    }
}

这个设计有几个关键点:

  1. 方法重载:提供不同参数组合的方法,方便调用
  2. 温度参数:这是控制AI创造力的关键参数,我提供了几个预设值
  3. 异常处理:统一捕获异常,并转换为自定义异常
  4. 流式支持:用Flux支持流式响应,用户体验更好

4.3 支持多模型切换

实际项目中,你可能需要根据场景选择不同的模型。比如简单问答用轻量模型,复杂推理用大模型。我在AiManager里增加了模型选择支持:

public class AiManager {
    // ... 其他代码
    
    /**
     * 模型枚举
     */
    public enum Model {
        GLM_4("glm-4", "通用大模型"),
        GLM_4_AIR("glm-4-air", "轻量模型,响应快"),
        GLM_4_LONG("glm-4-long", "长文本模型");
        
        private final String code;
        private final String desc;
        
        Model(String code, String desc) {
            this.code = code;
            this.desc = desc;
        }
        
        public String getCode() { return code; }
        public String getDesc() { return desc; }
    }
    
    /**
     * 指定模型的调用
     */
    public String chatWithModel(Model model, String systemPrompt, String userMessage) {
        List<ChatMessage> messages = buildMessages(systemPrompt, userMessage);
        
        ChatCompletionRequest request = ChatCompletionRequest.builder()
                .model(model.getCode())  // 使用指定模型
                .messages(messages)
                .stream(Boolean.FALSE)
                .build();
        
        // ... 调用逻辑
    }
}

这样调用时就可以根据需求选模型了:

// 快速响应场景
String quickAnswer = aiManager.chatWithModel(
    AiManager.Model.GLM_4_AIR, 
    "你是一个简洁的助手",
    "今天天气如何?"
);

// 复杂推理场景  
String complexAnswer = aiManager.chatWithModel(
    AiManager.Model.GLM_4,
    "你是一个专业的分析师",
    "分析一下当前市场趋势..."
);

4.4 添加重试机制

网络请求难免失败,特别是调用外部API时。我建议加上重试机制:

@Slf4j
@Component
public class AiManager {
    // ... 其他代码
    
    @Resource
    private RetryTemplate retryTemplate;
    
    /**
     * 带重试的调用
     */
    public String chatWithRetry(String systemPrompt, String userMessage) {
        return retryTemplate.execute(context -> {
            log.info("第{}次尝试调用AI服务", context.getRetryCount() + 1);
            return syncChat(systemPrompt, userMessage);
        });
    }
    
    @Bean
    public RetryTemplate retryTemplate() {
        RetryTemplate template = new RetryTemplate();
        
        // 指数退避策略
        ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();
        backOffPolicy.setInitialInterval(1000);  // 初始间隔1秒
        backOffPolicy.setMultiplier(2.0);        // 每次翻倍
        backOffPolicy.setMaxInterval(10000);     // 最大间隔10秒
        
        // 重试策略:最多3次,遇到网络异常重试
        SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
        retryPolicy.setMaxAttempts(3);
        
        template.setBackOffPolicy(backOffPolicy);
        template.setRetryPolicy(retryPolicy);
        
        return template;
    }
}

重试机制能显著提高系统的健壮性。特别是对于偶尔的网络抖动,自动重试往往能解决问题。

5. Prompt工程实战技巧

5.1 什么是好的Prompt?

很多人觉得AI不好用,其实问题往往出在Prompt上。好的Prompt就像给AI清晰的指令,它才知道你想要什么。我总结了几条原则:

  1. 明确角色:告诉AI它应该扮演什么角色
  2. 具体任务:清晰描述要做什么
  3. 输出格式:指定返回数据的格式
  4. 约束条件:有哪些限制和要求

让我用实际例子说明。假设我们要让AI生成产品描述:

差的Prompt:"写一个产品描述"

好的Prompt:

你是一名专业的电商文案写手。请为以下产品撰写吸引人的描述:

产品名称:无线蓝牙耳机
特点:降噪、续航30小时、防水、轻便
目标用户:通勤族、运动爱好者
要求:
1. 突出降噪和续航优势
2. 语言生动有感染力
3. 不超过200字
4. 包含3个卖点

看到区别了吗?好的Prompt让AI知道怎么发挥。

5.2 常用Prompt模式

在实际开发中,我整理了一些常用的Prompt模式,放在常量类里复用:

public class PromptTemplates {
    
    // 文本总结
    public static final String SUMMARIZE = """
        你是一个专业的文本总结助手。请将以下文本总结为简洁的要点:
        
        文本:%s
        
        要求:
        1. 提取核心信息
        2. 分条列出
        3. 每点不超过20字
        4. 保留关键数据
        """;
    
    // 代码解释
    public static final String EXPLAIN_CODE = """
        你是一个资深的程序员。请解释以下代码的功能和逻辑:
        
        代码:
        ```java
        %s
        ```
        
        要求:
        1. 说明代码作用
        2. 分析关键逻辑
        3. 指出可能的优化点
        4. 用中文回答
        """;
    
    // 数据提取
    public static final String EXTRACT_DATA = """
        你是一个数据提取专家。请从以下文本中提取结构化信息:
        
        文本:%s
        
        提取字段:
        1. 人名
        2. 日期
        3. 金额
        4. 产品名称
        
        以JSON格式返回:
        {
          "people": [],
          "dates": [],
          "amounts": [],
          "products": []
        }
        """;
    
    // 客服回复
    public static final String CUSTOMER_SERVICE = """
        你是一家电商平台的客服助手。请根据用户问题提供专业回复:
        
        用户问题:%s
        
        要求:
        1. 态度友好亲切
        2. 解决实际问题
        3. 提供明确方案
        4. 适当使用表情符号
        5. 不超过150字
        """;
}

使用时这样调用:

String text = "这是一段很长的文章内容...";
String prompt = String.format(PromptTemplates.SUMMARIZE, text);
String summary = aiManager.syncChat(prompt, "");

5.3 动态Prompt构建

有时候Prompt需要根据业务数据动态生成。我设计了一个PromptBuilder工具类:

@Component
public class PromptBuilder {
    
    /**
     * 构建产品推荐Prompt
     */
    public String buildProductRecommendationPrompt(User user, List<Product> history) {
        StringBuilder prompt = new StringBuilder();
        prompt.append("你是一个个性化的购物助手。");
        prompt.append("用户信息:").append(user.getAge()).append("岁,")
              .append(user.getGender()).append(",兴趣:").append(user.getInterests());
        
        if (!history.isEmpty()) {
            prompt.append("\n历史购买:");
            history.forEach(p -> 
                prompt.append(p.getName()).append("、")
            );
        }
        
        prompt.append("\n请推荐3个适合的商品,说明推荐理由。");
        return prompt.toString();
    }
    
    /**
     * 构建学习计划Prompt
     */
    public String buildStudyPlanPrompt(String subject, String level, int hoursPerWeek) {
        return String.format("""
            你是一个教育专家。请为一名%s水平的学习者制定%s学习计划。
            
            要求:
            1. 每周学习%d小时
            2. 分四周进行
            3. 每周有具体目标和练习
            4. 包含学习资源和检测方法
            
            以表格形式输出计划。
            """, level, subject, hoursPerWeek);
    }
}

5.4 Prompt版本管理

随着业务发展,Prompt可能需要调整优化。我建议对Prompt进行版本管理:

@Configuration
@RefreshScope  // 支持热更新
public class PromptConfig {
    
    @Value("${prompt.version:1.0}")
    private String promptVersion;
    
    @Bean
    public Map<String, String> promptTemplates() {
        Map<String, String> prompts = new HashMap<>();
        
        // 根据版本加载不同的Prompt
        if ("1.0".equals(promptVersion)) {
            prompts.put("summarize", PromptTemplates.SUMMARIZE_V1);
            prompts.put("recommend", PromptTemplates.RECOMMEND_V1);
        } else if ("2.0".equals(promptVersion)) {
            prompts.put("summarize", PromptTemplates.SUMMARIZE_V2);
            prompts.put("recommend", PromptTemplates.RECOMMEND_V2);
        }
        
        return prompts;
    }
}

这样可以在不重启服务的情况下切换Prompt版本,方便A/B测试。

6. 业务集成实战案例

6.1 智能客服系统

让我们看一个实际的业务场景:智能客服。需求是用户输入问题,AI自动回复,如果AI不确定就转人工。

首先定义业务逻辑:

@Service
@Slf4j
public class CustomerService {
    
    @Resource
    private AiManager aiManager;
    
    @Resource
    private HumanAgentService agentService;
    
    /**
     * 处理用户咨询
     */
    public ChatResponse handleInquiry(String userId, String question) {
        // 1. 构建客服Prompt
        String systemPrompt = """
            你是XX公司的智能客服,专业、友好、乐于助人。
            公司产品:软件服务、技术支持、培训课程
            服务时间:周一至周五 9:00-18:00
            
            回答要求:
            1. 准确回答产品相关问题
            2. 技术问题提供解决思路
            3. 价格问题引导联系销售
            4. 如果无法确定,建议转人工
            5. 回复不超过100字
            """;
        
        // 2. 调用AI
        String aiResponse = aiManager.syncChat(systemPrompt, question);
        
        // 3. 判断是否需要转人工
        if (needHumanAgent(aiResponse, question)) {
            log.info("用户{}的问题需要人工处理", userId);
            agentService.assignToAgent(userId, question);
            return ChatResponse.transferToHuman();
        }
        
        // 4. 记录对话历史
        saveChatHistory(userId, question, aiResponse);
        
        return ChatResponse.aiResponse(aiResponse);
    }
    
    /**
     * 判断是否需要人工客服
     */
    private boolean needHumanAgent(String aiResponse, String question) {
        // 规则1:AI明确说需要人工
        if (aiResponse.contains("转人工") || aiResponse.contains("联系客服")) {
            return true;
        }
        
        // 规则2:用户明确要求人工
        if (question.contains("人工") || question.contains("真人")) {
            return true;
        }
        
        // 规则3:AI回答置信度低(这里简化处理)
        if (aiResponse.length() < 20 || aiResponse.contains("不太确定")) {
            return true;
        }
        
        return false;
    }
}

这个实现有几个亮点:

  1. 清晰的职责分离:AI处理标准问题,复杂问题转人工
  2. 智能判断逻辑:多种条件判断是否需要人工
  3. 完整的业务流程:包含历史记录和状态管理

6.2 内容自动生成系统

另一个常见场景是内容生成,比如自动写文章摘要、生成产品描述等。

@Service
public class ContentGenerator {
    
    @Resource
    private AiManager aiManager;
    
    /**
     * 生成文章摘要
     */
    public Summary generateArticleSummary(Article article) {
        String prompt = String.format("""
            你是一个专业的编辑。请为以下文章生成摘要:
            
            标题:%s
            作者:%s
            正文:
            %s
            
            摘要要求:
            1. 提炼核心观点
            2. 保留关键数据
            3. 语言简洁明了
            4. 不超过150字
            5. 包含文章价值点
            
            返回格式:
            {
              "summary": "摘要内容",
              "keywords": ["关键词1", "关键词2", "关键词3"],
              "readingTime": 预估阅读分钟数
            }
            """, article.getTitle(), article.getAuthor(), article.getContent());
        
        String aiResponse = aiManager.syncChat(prompt, "");
        
        // 解析AI返回的JSON
        return parseSummary(aiResponse);
    }
    
    /**
     * 批量生成产品描述
     */
    public List<ProductDescription> batchGenerateDescriptions(List<Product> products) {
        List<ProductDescription> results = new ArrayList<>();
        
        for (Product product : products) {
            String prompt = buildProductPrompt(product);
            String description = aiManager.syncChat(prompt, "");
            
            ProductDescription pd = new ProductDescription();
            pd.setProductId(product.getId());
            pd.setDescription(description);
            pd.setGeneratedTime(LocalDateTime.now());
            
            results.add(pd);
            
            // 避免频繁调用,加一点延迟
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
        
        return results;
    }
    
    private String buildProductPrompt(Product product) {
        return String.format("""
            你是一个电商文案专家。请为以下产品撰写吸引人的描述:
            
            产品名称:%s
            类别:%s
            特点:%s
            目标用户:%s
            价格:%s元
            
            要求:
            1. 突出产品卖点
            2. 激发购买欲望
            3. 适合电商平台展示
            4. 包含3个使用场景
            5. 200字左右
            """, product.getName(), product.getCategory(), 
                product.getFeatures(), product.getTargetUsers(), product.getPrice());
    }
}

6.3 代码审查助手

对于技术团队,AI可以帮助审查代码:

@Service
public class CodeReviewAssistant {
    
    @Resource
    private AiManager aiManager;
    
    /**
     * 审查Java代码
     */
    public CodeReviewResult reviewJavaCode(String code, String requirements) {
        String prompt = String.format("""
            你是一个资深的Java架构师。请审查以下代码:
            
            需求背景:%s
            
            代码:
            ```java
            %s
            ```
            
            请从以下角度审查:
            1. 代码规范(命名、注释、格式)
            2. 性能问题(时间复杂度、内存使用)
            3. 安全性(SQL注入、XSS等)
            4. 可维护性(复杂度、耦合度)
            5. 最佳实践遵循情况
            
            发现问题请给出:
            - 问题描述
            - 风险等级(高/中/低)
            - 修改建议
            - 示例代码
            
            以JSON格式返回审查结果。
            """, requirements, code);
        
        String reviewResult = aiManager.syncChat(prompt, "");
        return parseReviewResult(reviewResult);
    }
    
    /**
     * 生成单元测试
     */
    public String generateUnitTest(String className, String methodSignature, String logicDescription) {
        String prompt = String.format("""
            你是一个测试专家。请为以下方法生成单元测试:
            
            类名:%s
            方法签名:%s
            方法逻辑:%s
            
            要求:
            1. 使用JUnit 5
            2. 覆盖正常场景和边界条件
            3. 包含必要的Mock
            4. 添加有意义的断言
            5. 包含测试说明
            
            只返回测试代码,不要解释。
            """, className, methodSignature, logicDescription);
        
        return aiManager.syncChat(prompt, "");
    }
}

7. 高级功能与性能优化

7.1 流式响应实现

对于长文本生成,流式响应能显著提升用户体验。Spring Boot支持Server-Sent Events (SSE):

@RestController
@RequestMapping("/api/ai/stream")
public class StreamChatController {
    
    @Resource
    private AiManager aiManager;
    
    /**
     * 流式对话接口
     */
    @GetMapping("/chat")
    public Flux<ServerSentEvent<String>> streamChat(
            @RequestParam String question,
            @RequestParam(defaultValue = "false") boolean creative) {
        
        String systemPrompt = "你是一个有帮助的助手";
        float temperature = creative ? 0.9f : 0.7f;
        
        return aiManager.streamChat(systemPrompt, question, temperature)
                .map(content -> ServerSentEvent.builder(content).build())
                .onErrorResume(e -> {
                    log.error("流式对话失败", e);
                    return Flux.just(
                        ServerSentEvent.builder("[服务异常]")
                            .id("error")
                            .build()
                    );
                });
    }
    
    /**
     * 长文本生成(如文章写作)
     */
    @PostMapping("/generate")
    public Flux<ServerSentEvent<String>> generateArticle(
            @RequestBody ArticleRequest request) {
        
        String prompt = String.format("""
            请以'%s'为主题写一篇文章。
            
            要求:
            1. 字数:%d字左右
            2. 风格:%s
            3. 结构:引言、正文、结论
            4. 包含实际案例
            
            请逐步生成,先写大纲,再填充内容。
            """, request.getTopic(), request.getWordCount(), request.getStyle());
        
        return aiManager.streamChat(prompt, "")
                .delayElements(Duration.ofMillis(100))  // 控制输出速度
                .map(chunk -> {
                    // 可以在这里添加进度信息
                    return ServerSentEvent.builder(chunk)
                            .event("chunk")
                            .build();
                });
    }
}

前端可以这样接收流式响应:

// 前端JavaScript代码
const eventSource = new EventSource('/api/ai/stream/chat?question=' + encodeURIComponent(question));

eventSource.onmessage = function(event) {
    const chunk = event.data;
    // 逐步显示到页面
    document.getElementById('response').innerHTML += chunk;
    
    // 自动滚动到底部
    const container = document.getElementById('response-container');
    container.scrollTop = container.scrollHeight;
};

eventSource.onerror = function(event) {
    console.error('流式连接错误', event);
    eventSource.close();
};

7.2 缓存优化策略

AI调用比较耗时,合理使用缓存能大幅提升性能:

@Service
@Slf4j
public class CachedAiService {
    
    @Resource
    private AiManager aiManager;
    
    @Resource
    private CacheManager cacheManager;
    
    /**
     * 带缓存的AI调用
     */
    @Cacheable(value = "aiResponses", key = "#systemPrompt + '|' + #userMessage")
    public String cachedChat(String systemPrompt, String userMessage) {
        log.info("缓存未命中,调用AI服务");
        return aiManager.syncChat(systemPrompt, userMessage);
    }
    
    /**
     * 带条件缓存的调用
     */
    @Cacheable(value = "aiResponses", 
               key = "#systemPrompt + '|' + #userMessage",
               condition = "#userMessage.length() < 100")
    public String conditionalCachedChat(String systemPrompt, String userMessage) {
        // 只有短问题才缓存
        return aiManager.syncChat(systemPrompt, userMessage);
    }
    
    /**
     * 手动缓存管理
     */
    public String manualCachedChat(String systemPrompt, String userMessage) {
        String cacheKey = generateCacheKey(systemPrompt, userMessage);
        Cache cache = cacheManager.getCache("aiResponses");
        
        // 先查缓存
        Cache.ValueWrapper cached = cache.get(cacheKey);
        if (cached != null) {
            log.info("缓存命中: {}", cacheKey);
            return (String) cached.get();
        }
        
        // 调用AI
        String response = aiManager.syncChat(systemPrompt, userMessage);
        
        // 存入缓存(带TTL)
        cache.put(cacheKey, response);
        
        return response;
    }
    
    /**
     * 批量预缓存
     */
    @Scheduled(fixedDelay = 3600000)  // 每小时执行一次
    public void preCacheCommonQuestions() {
        List<CommonQuestion> questions = questionService.getHotQuestions();
        
        for (CommonQuestion question : questions) {
            try {
                String response = aiManager.syncChat(
                    "你是智能助手", 
                    question.getContent()
                );
                
                cacheManager.getCache("aiResponses")
                    .put(question.getContent(), response);
                
                log.info("预缓存问题: {}", question.getContent());
                
                // 避免频繁调用
                Thread.sleep(500);
            } catch (Exception e) {
                log.warn("预缓存失败: {}", question.getContent(), e);
            }
        }
    }
}

缓存配置示例:

# application.yml
spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=1000,expireAfterWrite=1h

7.3 限流与降级

AI API通常有调用限制,我们需要在客户端做好限流:

@Component
public class RateLimiterService {
    
    // 令牌桶限流器:每秒10个令牌
    private final RateLimiter rateLimiter = RateLimiter.create(10.0);
    
    // 并发信号量:最大10个并发
    private final Semaphore concurrencySemaphore = new Semaphore(10);
    
    /**
     * 带限流的AI调用
     */
    public String rateLimitedChat(String systemPrompt, String userMessage) {
        // 1. 获取令牌(等待最多1秒)
        if (!rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) {
            throw new RateLimitException("请求过于频繁,请稍后重试");
        }
        
        // 2. 获取并发许可
        if (!concurrencySemaphore.tryAcquire()) {
            throw new ConcurrencyLimitException("系统繁忙,请稍后重试");
        }
        
        try {
            // 3. 调用AI
            return aiManager.syncChat(systemPrompt, userMessage);
        } finally {
            // 4. 释放并发许可
            concurrencySemaphore.release();
        }
    }
    
    /**
     * 降级策略:当AI服务不可用时
     */
    @Slf4j
    @Component
    public static class FallbackService {
        
        private final Map<String, String> fallbackResponses = new HashMap<>();
        
        public FallbackService() {
            // 初始化兜底回答
            fallbackResponses.put("你好", "您好!我现在有点忙,请稍后再试。");
            fallbackResponses.put("天气", "天气查询功能暂时不可用。");
            fallbackResponses.put("帮助", "请尝试刷新页面或联系客服。");
        }
        
        /**
         * 获取兜底回答
         */
        public String getFallbackResponse(String question) {
            // 简单关键词匹配
            for (Map.Entry<String, String> entry : fallbackResponses.entrySet()) {
                if (question.contains(entry.getKey())) {
                    return entry.getValue();
                }
            }
            
            // 默认回答
            return "抱歉,服务暂时不可用。我们已经收到您的问题,稍后会为您处理。";
        }
    }
}

7.4 监控与日志

完善的监控能帮助我们及时发现和解决问题:

@Aspect
@Component
@Slf4j
public class AiMonitorAspect {
    
    @Around("execution(* com..AiManager.*(..))")
    public Object monitorAiCall(ProceedingJoinPoint joinPoint) throws Throwable {
        String methodName = joinPoint.getSignature().getName();
        Object[] args = joinPoint.getArgs();
        
        long startTime = System.currentTimeMillis();
        boolean success = false;
        
        try {
            Object result = joinPoint.proceed();
            success = true;
            return result;
        } catch (Exception e) {
            log.error("AI调用失败 - 方法: {}, 参数: {}", methodName, args, e);
            throw e;
        } finally {
            long cost = System.currentTimeMillis() - startTime;
            
            // 记录指标
            recordMetrics(methodName, success, cost, args);
            
            // 慢查询告警
            if (cost > 5000) {  // 超过5秒
                log.warn("AI调用过慢 - 方法: {}, 耗时: {}ms", methodName, cost);
                alertSlowCall(methodName, cost, args);
            }
        }
    }
    
    private void recordMetrics(String method, boolean success, long cost, Object[] args) {
        // 这里可以接入监控系统,如Prometheus
        Metrics.counter("ai_calls_total",
                "method", method,
                "success", String.valueOf(success))
                .increment();
        
        Metrics.timer("ai_call_duration",
                "method", method)
                .record(cost, TimeUnit.MILLISECONDS);
        
        // 记录到日志系统
        log.info("AI调用统计 - 方法: {}, 成功: {}, 耗时: {}ms", 
                method, success, cost);
    }
    
    private void alertSlowCall(String method, long cost, Object[] args) {
        // 发送告警通知
        String message = String.format(
            "AI服务响应过慢\n方法: %s\n耗时: %dms\n时间: %s",
            method, cost, LocalDateTime.now()
        );
        
        // 可以集成企业微信、钉钉、邮件等通知方式
        notificationService.sendAlert(message);
    }
}

8. 前后端联调与部署

8.1 前端集成示例

前端调用AI接口时,需要考虑用户体验。这里是一个Vue 3的示例:

<template>
  <div class="chat-container">
    <div class="messages" ref="messagesRef">
      <div v-for="(msg, index) in messages" :key="index" 
           :class="['message', msg.role]">
        {{ msg.content }}
      </div>
      
      <!-- 流式响应显示 -->
      <div v-if="streaming" class="message assistant streaming">
        {{ streamContent }}
        <span class="cursor">▌</span>
      </div>
    </div>
    
    <div class="input-area">
      <textarea v-model="inputText" 
                @keydown.enter.prevent="sendMessage"
                placeholder="输入您的问题...">
      </textarea>
      <button @click="sendMessage" :disabled="loading">
        {{ loading ? '思考中...' : '发送' }}
      </button>
    </div>
  </div>
</template>

<script setup>
import { ref, onMounted, nextTick } from 'vue'

const messages = ref([])
const inputText = ref('')
const loading = ref(false)
const streaming = ref(false)
const streamContent = ref('')
const messagesRef = ref(null)

// 发送消息
const sendMessage = async () => {
  if (!inputText.value.trim() || loading.value) return
  
  const userMessage = inputText.value.trim()
  inputText.value = ''
  
  // 添加用户消息
  messages.value.push({
    role: 'user',
    content: userMessage
  })
  
  // 滚动到底部
  scrollToBottom()
  
  // 调用AI接口
  loading.value = true
  streaming.value = true
  streamContent.value = ''
  
  try {
    const response = await fetch('/api/ai/stream/chat?question=' + 
      encodeURIComponent(userMessage))
    
    if (!response.ok) throw new Error('请求失败')
    
    const reader = response.body.getReader()
    const decoder = new TextDecoder()
    
    while (true) {
      const { done, value } = await reader.read()
      if (done) break
      
      const chunk = decoder.decode(value)
      streamContent.value += chunk
      
      // 每次收到数据都滚动到底部
      scrollToBottom()
    }
    
    // 流式接收完成,添加到消息列表
    messages.value.push({
      role: 'assistant',
      content: streamContent.value
    })
    
  } catch (error) {
    console.error('AI对话失败:', error)
    messages.value.push({
      role: 'assistant',
      content: '抱歉,服务暂时不可用。'
    })
  } finally {
    loading.value = false
    streaming.value = false
    streamContent.value = ''
    scrollToBottom()
  }
}

// 自动滚动到底部
const scrollToBottom = () => {
  nextTick(() => {
    if (messagesRef.value) {
      messagesRef.value.scrollTop = messagesRef.value.scrollHeight
    }
  })
}

// 初始化欢迎消息
onMounted(() => {
  messages.value.push({
    role: 'assistant',
    content: '你好!我是AI助手,有什么可以帮您的?'
  })
})
</script>

<style scoped>
.chat-container {
  max-width: 800px;
  margin: 0 auto;
  height: 600px;
  display: flex;
  flex-direction: column;
}

.messages {
  flex: 1;
  overflow-y: auto;
  padding: 20px;
  background: #f5f5f5;
}

.message {
  margin-bottom: 15px;
  padding: 12px 16px;
  border-radius: 12px;
  max-width: 80%;
  word-wrap: break-word;
}

.message.user {
  background: #007bff;
  color: white;
  margin-left: auto;
}

.message.assistant {
  background: white;
  border: 1px solid #ddd;
}

.message.streaming {
  background: #f0f8ff;
  border-left: 4px solid #007bff;
}

.cursor {
  animation: blink 1s infinite;
}

@keyframes blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0; }
}

.input-area {
  padding: 20px;
  background: white;
  border-top: 1px solid #ddd;
  display: flex;
  gap: 10px;
}

textarea {
  flex: 1;
  padding: 12px;
  border: 1px solid #ddd;
  border-radius: 8px;
  resize: none;
  font-size: 14px;
}

button {
  padding: 12px 24px;
  background: #007bff;
  color: white;
  border: none;
  border-radius: 8px;
  cursor: pointer;
}

button:disabled {
  background: #ccc;
  cursor: not-allowed;
}
</style>

8.2 部署配置建议

部署AI应用时,有几个关键点需要注意:

Docker配置示例:

# Dockerfile
FROM openjdk:17-jdk-slim

WORKDIR /app

# 复制构建产物
COPY target/ai-application.jar app.jar

# 设置时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# JVM参数优化
ENV JAVA_OPTS="-Xmx2g -Xms1g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+HeapDumpOnOutOfMemoryError"

# 健康检查
HEALTHCHECK --interval=30s --timeout=3s --start-period=60s --retries=3 \
  CMD curl -f http://localhost:8080/actuator/health || exit 1

EXPOSE 8080

ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar app.jar"]

Kubernetes部署配置:

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: ai-application
spec:
  replicas: 3
  selector:
    matchLabels:
      app: ai-application
  template:
    metadata:
      labels:
        app: ai-application
    spec:
      containers:
      - name: ai-app
        image: your-registry/ai-application:latest
        ports:
        - containerPort: 8080
        env:
        - name: ZHIPU_API_KEY
          valueFrom:
            secretKeyRef:
              name: ai-secrets
              key: zhipu-api-key
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        resources:
          requests:
            memory: "1Gi"
            cpu: "500m"
          limits:
            memory: "2Gi"
            cpu: "1000m"
        livenessProbe:
          httpGet:
            path: /actuator/health/liveness
            port: 8080
          initialDelaySeconds: 60
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health/readiness
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 5
---
# service.yaml
apiVersion: v1
kind: Service
metadata:
  name: ai-application-service
spec:
  selector:
    app: ai-application
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP
---
# ingress.yaml (如果需要外部访问)
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ai-application-ingress
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "10m"
    nginx.ingress.kubernetes.io/proxy-read-timeout: "300"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "300"
spec:
  rules:
  - host: ai.yourdomain.com
    http:
      paths:
      - path: /
        pathType: Prefix
        backend:
          service:
            name: ai-application-service
            port:
              number: 80

8.3 安全考虑

AI应用涉及用户数据,安全非常重要:

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf().disable()  // API服务通常禁用CSRF
            .authorizeHttpRequests(auth -> auth
                .requestMatchers("/api/public/**").permitAll()
                .requestMatchers("/api/ai/**").authenticated()
                .anyRequest().permitAll()
            )
            .oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
            .sessionManagement(session -> 
                session.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
            );
        
        return http.build();
    }
    
    /**
     * 输入内容安全检查
     */
    @Component
    public class ContentValidator {
        
        private final List<String> forbiddenPatterns = Arrays.asList(
            "恶意关键词1", "恶意关键词2"
        );
        
        public ValidationResult validateInput(String input) {
            ValidationResult result = new ValidationResult();
            
            // 1. 长度检查
            if (input.length() > 1000) {
                result.setValid(false);
                result.setMessage("输入内容过长");
                return result;
            }
            
            // 2. 敏感词检查
            for (String pattern : forbiddenPatterns) {
                if (input.contains(pattern)) {
                    result.setValid(false);
                    result.setMessage("包含不允许的内容");
                    return result;
                }
            }
            
            // 3. SQL注入检查(简单示例)
            if (input.matches(".*([';]+|(--)+).*")) {
                result.setValid(false);
                result.setMessage("输入内容不安全");
                return result;
            }
            
            result.setValid(true);
            return result;
        }
    }
    
    /**
     * 请求限流(防止滥用)
     */
    @Bean
    public MeterRegistryCustomizer<MeterRegistry> metricsCommonTags() {
        return registry -> registry.config().commonTags(
            "application", "ai-service"
        );
    }
    
    @Bean
    public MeterFilter meterFilter() {
        return MeterFilter.deny(id -> {
            String uri = id.getTag("uri");
            return uri != null && uri.contains("/actuator");
        });
    }
}

8.4 监控告警配置

最后,设置监控告警,确保服务稳定:

# prometheus监控配置
scrape_configs:
  - job_name: 'ai-application'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['ai-application:8080']
    relabel_configs:
      - source_labels: [__address__]
        target_label: instance
        regex: '(.*):.*'
        replacement: '${1}'

# alertmanager告警规则
groups:
  - name: ai-service-alerts
    rules:
      - alert: HighErrorRate
        expr: rate(http_server_requests_errors_total{job="ai-application"}[5m]) > 0.1
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "AI服务错误率过高"
          description: "错误率超过10%,当前值 {{ $value }}"
      
      - alert: SlowAIResponse
        expr: histogram_quantile(0.95, rate(ai_call_duration_seconds_bucket[5m])) > 5
        for: 5m
        labels:
          severity: warning
        annotations:
          summary: "AI响应时间过长"
          description: "95%分位响应时间超过5秒,当前值 {{ $value }}s"
      
      - alert: HighAICallRate
        expr: rate(ai_calls_total{job="ai-application"}[5m]) > 100
        for: 2m
        labels:
          severity: warning
        annotations:
          summary: "AI调用频率过高"
          description: "可能遭到滥用,当前调用频率 {{ $value }}/s"

这些配置能帮助你在生产环境中稳定运行AI服务。记住,监控和告警不是一次性的工作,需要根据实际运行情况不断调整优化。

更多推荐