Spring AI 1.1实战:构建智能周报生成器的完整指南
每周五下午,当办公室里的咖啡机开始频繁运作时,总有一群开发者对着空白的周报文档发呆。这种重复性工作不仅消耗时间,更消磨创造力。现在,借助Spring AI 1.1和大型语言模型(LLM),我们可以用Java代码让这个流程自动化——不仅能自动生成内容,还能根据汇报对象(领导/团队)调整表达风格。本文将带你从零构建一个能理解项目上下文、自动归纳工作成果的智能周报系统。
1. 环境配置与项目初始化
在开始编码前,我们需要搭建好开发环境。与简单的Demo项目不同,生产级周报生成器需要考虑模型稳定性、生成质量控制和异常处理机制。
基础环境要求:
- JDK 17+(Spring AI 1.1的强制要求)
- Spring Boot 3.2.x
- Maven 3.6+ 或 Gradle 7.x
- 任一LLM服务账号(推荐通义千问或OpenAI)
创建项目时,在pom.xml中添加这些核心依赖:
<dependencies> <!-- Spring Boot基础依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- Spring AI核心 --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-core</artifactId> </dependency> <!-- 通义千问集成(国内开发者推荐) --> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-tongyi-spring-boot-starter</artifactId> </dependency> <!-- 数据校验 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency> </dependencies>在application.yml中配置模型参数:
spring: ai: tongyi: access-key-id: ${TONGYI_ACCESS_KEY} access-key-secret: ${TONGYI_SECRET_KEY} chat: model: qwen-plus # 增强版模型 temperature: 0.3 # 降低随机性保证稳定性 max-tokens: 2000提示:敏感配置建议通过环境变量注入,避免硬编码在配置文件中
2. 周报数据结构设计与输入处理
好的周报生成器首先要解决的是如何结构化输入数据。我们设计一个包含多维度信息的DTO:
public class WeeklyReportRequest { @NotBlank private String userId; @NotNull private LocalDate weekStartDate; @Size(min = 3) private List<WorkItem> completedItems; private List<WorkItem> plannedItems; @NotBlank private String reportStyle; // "leader"或"team" // 嵌套的工作项类 public static class WorkItem { private String projectName; private String taskDescription; private Integer timeSpent; // 分钟 private String difficultyLevel; } }对应的控制器需要处理数据验证和预处理:
@RestController @RequestMapping("/api/reports") public class ReportController { @PostMapping("/weekly") public ResponseEntity<String> generateWeeklyReport( @Valid @RequestBody WeeklyReportRequest request) { // 数据预处理逻辑 String processedInput = InputPreprocessor.process(request); // 调用AI服务生成报告 String report = reportService.generateReport(processedInput); return ResponseEntity.ok(report); } }3. 提示词工程与模板设计
周报质量的核心在于提示词设计。我们创建可动态适配不同风格的模板系统:
基础模板(resources/prompts/weekly_base.st):
你是一位专业的{reportStyle.equals('leader') ? '部门主管' : '技术团队成员'},需要基于以下工作记录生成周报: 本周时间范围:{weekStartDate} 至 {weekStartDate.plusDays(4)} 完成事项: {#each completedItems} - 项目:{projectName} | 任务:{taskDescription} | 耗时:{timeSpent}分钟 {/each} 生成要求: 1. 使用{reportStyle.equals('leader') ? '成果导向型' : '协作分享型'}表达 2. 突出技术难点{difficultyLevel != null ? '(难度等级:'+difficultyLevel+')' : ''} 3. 按优先级排序事项 4. 输出Markdown格式Java模板处理器:
@Service public class PromptEngine { private final TemplateResolver templateResolver; public String buildPrompt(WeeklyReportRequest request) { Context context = new Context(); context.setVariable("reportStyle", request.getReportStyle()); // 设置其他变量... return templateResolver.resolve("weekly_base", context); } }当检测到特殊需求时,可以动态追加提示词:
if (request.containsKeyword("KPI")) { prompt += "\n额外要求:将工作成果与季度KPI目标关联分析"; }4. 模型调用与结果优化
基础生成代码很容易写,但要生产可用还需要这些增强处理:
@Service public class ReportGenerator { private final ChatClient chatClient; public String generateReport(String prompt) { // 第一次生成原始内容 String draft = chatClient.generate(prompt); // 二次优化:检查完整性 if (!validateReport(draft)) { String refinementPrompt = prompt + "\n请修复以下问题:\n" + "- 补充缺失的项目关联分析\n" + "- 调整语气更加专业"; draft = chatClient.generate(refinementPrompt); } // 格式标准化处理 return formatHelper.standardizeMarkdown(draft); } private boolean validateReport(String content) { // 实现内容质量检查逻辑 } }典型优化策略对比表:
| 问题类型 | 检测方法 | 优化方案 |
|---|---|---|
| 内容空洞 | 关键词密度分析 | 追加具体案例要求 |
| 格式混乱 | 正则表达式检查 | 指定Markdown模板 |
| 语气不当 | 情感分析 | 调整提示词角色设定 |
| 技术细节不足 | 术语识别 | 要求补充实现细节 |
5. 高级功能实现
5.1 多模型降级策略
为确保服务可用性,实现模型自动切换机制:
@Primary @Service public class FallbackChatClient implements ChatClient { private final List<ChatClient> clients; public String generate(String prompt) { for (ChatClient client : clients) { try { return client.generate(prompt); } catch (Exception e) { log.warn("Model {} failed, trying next", client.getClass()); } } throw new RuntimeException("All models unavailable"); } }5.2 流式响应实现
对于长周报,采用流式输出提升用户体验:
@GetMapping(value = "/weekly/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> streamWeeklyReport(WeeklyReportRequest request) { String prompt = promptEngine.buildPrompt(request); return chatClient.stream(prompt) .map(ChatResponse::getResult) .map(ChatResult::getOutput) .map(AssistantMessage::getContent); }5.3 历史上下文支持
让模型记住上周内容以实现连贯性:
public String generateWithContext(WeeklyReportRequest request, String lastWeekReport) { String contextPrompt = """ 以下是上周报告摘要: {lastWeekReport} 请确保本周报告与上周内容保持连贯,特别是关于: - 项目进展的延续性 - 问题解决的后续跟踪 """; String mainPrompt = promptEngine.buildPrompt(request); return chatClient.generate(contextPrompt + mainPrompt); }6. 部署与性能优化
生产环境部署时,这些配置能显著提升稳定性:
spring: ai: tongyi: connect-timeout: 5000 read-timeout: 30000 retry: max-attempts: 3 initial-interval: 1000 max-interval: 5000缓存策略实现示例:
@Cacheable(value = "reports", key = "{#request.userId,#request.weekStartDate}") public String generateReport(WeeklyReportRequest request) { // 生成逻辑 }在Kubernetes环境中,建议配置这些资源限制:
resources: limits: cpu: "1" memory: "1Gi" requests: cpu: "500m" memory: "512Mi"7. 安全与合规考量
企业级应用必须注意这些安全实践:
数据脱敏处理
public String sanitizeInput(String input) { return input.replaceAll("\\d{4}-\\d{4}-\\d{4}", "[CARD]") .replaceAll("\\b\\d{3}-\\d{2}-\\d{4}\\b", "[SSN]"); }访问控制
@PreAuthorize("#request.userId == authentication.name") public String generateReport(WeeklyReportRequest request) { // 生成逻辑 }审计日志
@Aspect @Component public class ReportGenerationAudit { @AfterReturning(pointcut = "execution(* com..ReportService.generate*(..))", returning = "result") public void auditSuccess(JoinPoint jp, Object result) { // 记录生成操作 } }
实际部署中,我们发现在高峰时段为模型调用配置适当的限流策略至关重要。使用Resilience4j实现的断路器模式可以有效防止级联故障:
@Bean public CircuitBreakerConfig circuitBreakerConfig() { return CircuitBreakerConfig.custom() .failureRateThreshold(50) .waitDurationInOpenState(Duration.ofSeconds(30)) .permittedNumberOfCallsInHalfOpenState(5) .build(); }