1. 图像识别、图文问答实战

我来分享一个完整的Spring AI图像识别与图文问答实战指南,包含代码示例和最佳实践。

项目环境搭建

1.1 依赖配置
<!-- pom.xml -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
# application.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4-vision-preview
          temperature: 0.7

基础图像识别

2.1 图像描述生成
@Service
public class ImageDescriptionService {
    
    @Autowired
    private OpenAiChatClient chatClient;
    
    public String describeImage(String imageUrl) {
        UserMessage userMessage = new UserMessage(
            "请详细描述这张图片的内容",
            List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageUrl))
        );
        
        ChatResponse response = chatClient.call(
            new Prompt(List.of(userMessage))
        );
        
        return response.getResult().getOutput().getContent();
    }
    
    public String describeImage(MultipartFile imageFile) throws IOException {
        String base64Image = Base64.getEncoder()
            .encodeToString(imageFile.getBytes());
        
        UserMessage userMessage = new UserMessage(
            "描述这张图片",
            List.of(new Media(MimeTypeUtils.IMAGE_PNG, 
                "data:image/png;base64," + base64Image))
        );
        
        ChatResponse response = chatClient.call(
            new Prompt(List.of(userMessage))
        );
        
        return response.getResult().getOutput().getContent();
    }
}
2.2 图像分类服务
@Service
public class ImageClassificationService {
    
    private static final Map<String, String> CATEGORY_PROMPTS = Map.of(
        "SCENE", "这是一张什么场景的照片?",
        "OBJECT", "图片中的主要物体是什么?",
        "EMOTION", "这张图片传达了什么情感?",
        "ACTION", "图片中正在发生什么动作?"
    );
    
    public Map<String, String> classifyImage(String imageUrl) {
        Map<String, String> results = new HashMap<>();
        
        for (Map.Entry<String, String> entry : CATEGORY_PROMPTS.entrySet()) {
            UserMessage userMessage = new UserMessage(
                entry.getValue(),
                List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageUrl))
            );
            
            ChatResponse response = chatClient.call(
                new Prompt(List.of(userMessage))
            );
            
            results.put(entry.getKey(), 
                response.getResult().getOutput().getContent());
        }
        
        return results;
    }
}

图文问答系统

3.1 问答服务实现
@Service
public class VisualQAService {
    
    @Autowired
    private OpenAiChatClient chatClient;
    
    public String answerQuestion(String imageUrl, String question) {
        // 构建包含图像和问题的消息
        UserMessage userMessage = new UserMessage(
            question,
            List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageUrl))
        );
        
        ChatResponse response = chatClient.call(
            new Prompt(List.of(userMessage))
        );
        
        return response.getResult().getOutput().getContent();
    }
    
    public VisualQAResponse analyzeWithContext(String imageUrl, 
                                               String question,
                                               String context) {
        
        String systemPrompt = """
            你是一个视觉问答助手。请基于提供的图像和上下文信息回答问题。
            上下文:%s
            如果图像内容与上下文矛盾,以图像为准。
            """.formatted(context);
        
        SystemPrompt systemMessage = new SystemPrompt(systemPrompt);
        UserMessage userMessage = new UserMessage(
            question,
            List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageUrl))
        );
        
        ChatResponse response = chatClient.call(
            new Prompt(List.of(systemMessage, userMessage))
        );
        
        return VisualQAResponse.builder()
            .question(question)
            .answer(response.getResult().getOutput().getContent())
            .confidence(extractConfidence(response))
            .build();
    }
    
    private double extractConfidence(ChatResponse response) {
        // 从响应中提取置信度(示例逻辑)
        String content = response.getResult().getOutput().getContent();
        if (content.contains("确定") || content.contains("肯定")) {
            return 0.9;
        } else if (content.contains("可能") || content.contains("大概")) {
            return 0.7;
        } else {
            return 0.5;
        }
    }
    
    @Data
    @Builder
    public static class VisualQAResponse {
        private String question;
        private String answer;
        private double confidence;
        private LocalDateTime timestamp;
    }
}
3.2 多轮对话支持
@Service
public class MultiTurnVisualChatService {
    
    private final Map<String, List<Message>> conversationHistory = 
        new ConcurrentHashMap<>();
    
    public String chat(String sessionId, String imageUrl, String userMessage) {
        List<Message> history = conversationHistory
            .computeIfAbsent(sessionId, k -> new ArrayList<>());
        
        // 如果是第一次消息,添加图像
        if (history.isEmpty() && imageUrl != null) {
            history.add(new UserMessage(
                "这是我们要讨论的图片",
                List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageUrl))
            ));
        }
        
        // 添加用户问题
        history.add(new UserMessage(userMessage));
        
        // 限制历史记录长度
        if (history.size() > 10) {
            history = history.subList(history.size() - 10, history.size());
        }
        
        ChatResponse response = chatClient.call(new Prompt(history));
        String assistantResponse = response.getResult()
            .getOutput().getContent();
        
        // 保存助手回复
        history.add(new AssistantMessage(assistantResponse));
        conversationHistory.put(sessionId, history);
        
        return assistantResponse;
    }
    
    public void clearHistory(String sessionId) {
        conversationHistory.remove(sessionId);
    }
}

REST API 控制器

@RestController
@RequestMapping("/api/vision")
public class VisionController {
    
    @Autowired
    private ImageDescriptionService descriptionService;
    
    @Autowired
    private VisualQAService qaService;
    
    @Autowired
    private MultiTurnVisualChatService chatService;
    
    @PostMapping("/describe")
    public ResponseEntity<String> describeImage(
            @RequestParam("imageUrl") String imageUrl) {
        return ResponseEntity.ok(
            descriptionService.describeImage(imageUrl)
        );
    }
    
    @PostMapping("/describe/upload")
    public ResponseEntity<String> describeUploadedImage(
            @RequestParam("file") MultipartFile file) throws IOException {
        return ResponseEntity.ok(
            descriptionService.describeImage(file)
        );
    }
    
    @PostMapping("/qa")
    public ResponseEntity<VisualQAResponse> visualQA(
            @RequestBody VisualQARequest request) {
        return ResponseEntity.ok(
            qaService.answerQuestion(
                request.getImageUrl(), 
                request.getQuestion()
            )
        );
    }
    
    @PostMapping("/chat")
    public ResponseEntity<String> visualChat(
            @RequestBody VisualChatRequest request) {
        return ResponseEntity.ok(
            chatService.chat(
                request.getSessionId(),
                request.getImageUrl(),
                request.getMessage()
            )
        );
    }
    
    @Data
    public static class VisualQARequest {
        private String imageUrl;
        private String question;
    }
    
    @Data
    public static class VisualChatRequest {
        private String sessionId;
        private String imageUrl;
        private String message;
    }
}

高级功能:图像分析报告

@Service
public class ImageAnalysisReportService {
    
    public AnalysisReport generateReport(String imageUrl) {
        String description = describeImage(imageUrl);
        Map<String, String> classifications = classifyImage(imageUrl);
        List<String> tags = extractTags(imageUrl);
        String summary = generateSummary(description, classifications);
        
        return AnalysisReport.builder()
            .description(description)
            .classifications(classifications)
            .tags(tags)
            .summary(summary)
            .analysisTime(LocalDateTime.now())
            .build();
    }
    
    private List<String> extractTags(String imageUrl) {
        UserMessage userMessage = new UserMessage(
            "提取这张图片的关键标签,用逗号分隔",
            List.of(new Media(MimeTypeUtils.IMAGE_PNG, imageUrl))
        );
        
        ChatResponse response = chatClient.call(
            new Prompt(List.of(userMessage))
        );
        
        String tagsStr = response.getResult().getOutput().getContent();
        return Arrays.stream(tagsStr.split(","))
            .map(String::trim)
            .collect(Collectors.toList());
    }
    
    private String generateSummary(String description, 
                                   Map<String, String> classifications) {
        String prompt = """
            基于以下信息生成图片分析摘要:
            描述:%s
            分类:%s
            请生成一段简洁的总结,不超过100字。
            """.formatted(description, classifications);
        
        ChatResponse response = chatClient.call(
            new Prompt(prompt)
        );
        
        return response.getResult().getOutput().getContent();
    }
    
    @Data
    @Builder
    public static class AnalysisReport {
        private String description;
        private Map<String, String> classifications;
        private List<String> tags;
        private String summary;
        private LocalDateTime analysisTime;
    }
}

性能优化与缓存

@Service
public class CachedVisionService {
    
    @Autowired
    private RedisTemplate<String, Object> redisTemplate;
    
    private static final long CACHE_TTL = 3600; // 1小时
    
    public String describeImageWithCache(String imageUrl) {
        String cacheKey = "image:desc:" + hashImageUrl(imageUrl);
        
        // 尝试从缓存获取
        String cached = (String) redisTemplate.opsForValue().get(cacheKey);
        if (cached != null) {
            return cached;
        }
        
        // 调用API
        String description = descriptionService.describeImage(imageUrl);
        
        // 存入缓存
        redisTemplate.opsForValue().set(
            cacheKey, 
            description, 
            CACHE_TTL, 
            TimeUnit.SECONDS
        );
        
        return description;
    }
    
    private String hashImageUrl(String url) {
        try {
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(url.getBytes(StandardCharsets.UTF_8));
            return Base64.getEncoder().encodeToString(hash);
        } catch (NoSuchAlgorithmException e) {
            return String.valueOf(url.hashCode());
        }
    }
}

错误处理与监控

@ControllerAdvice
public class VisionExceptionHandler {
    
    @ExceptionHandler(Exception.class)
    public ResponseEntity<ErrorResponse> handleVisionException(Exception ex) {
        ErrorResponse error = ErrorResponse.builder()
            .timestamp(LocalDateTime.now())
            .status(HttpStatus.INTERNAL_SERVER_ERROR.value())
            .error("Vision Processing Error")
            .message(ex.getMessage())
            .path(getRequestPath())
            .build();
        
        // 记录监控指标
        Metrics.counter("vision.api.errors").increment();
        
        return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(error);
    }
    
    @Data
    @Builder
    public static class ErrorResponse {
        private LocalDateTime timestamp;
        private int status;
        private String error;
        private String message;
        private String path;
    }
}

测试用例

@SpringBootTest
class VisionServiceTest {
    
    @Autowired
    private VisualQAService qaService;
    
    @Test
    void testImageDescription() {
        String imageUrl = "https://example.com/image.jpg";
        String description = qaService.answerQuestion(
            imageUrl, 
            "描述这张图片"
        );
        
        assertNotNull(description);
        assertTrue(description.length() > 10);
    }
    
    @Test
    void testComplexQuestion() {
        String imageUrl = "https://example.com/chart.png";
        String answer = qaService.answerQuestion(
            imageUrl,
            "这张图表展示了什么趋势?最大值和最小值分别是多少?"
        );
        
        assertNotNull(answer);
        assertTrue(answer.contains("趋势") || 
                  answer.contains("增长") || 
                  answer.contains("下降"));
    }
}

部署配置

# application-prod.yml
spring:
  ai:
    openai:
      base-url: https://api.openai.com/v1
      chat:
        options:
          model: gpt-4-vision-preview
          max-tokens: 1000
          
management:
  endpoints:
    web:
      exposure:
        include: health,metrics,prometheus
  metrics:
    export:
      prometheus:
        enabled: true

resilience4j:
  ratelimiter:
    instances:
      vision-api:
        limit-for-period: 10
        limit-refresh-period: 1s
        timeout-duration: 0

最佳实践建议

  1. 图像预处理 :压缩大图像,限制分辨率
  2. 速率限制 :实现API调用频率控制
  3. 成本控制 :监控token使用量
  4. 异步处理 :对耗时操作使用异步调用
  5. 结果缓存 :缓存常见图像的识别结果
  6. 错误重试 :实现指数退避重试机制
  7. 内容审核 :添加敏感内容检测

这个实战指南提供了从基础到高级的完整实现,你可以根据具体需求进行调整和扩展。

2. 文档解析、表格提取、智能总结

我来为你详细介绍 Spring AI 在文档解析、表格提取和智能总结方面的功能。

1. 文档解析

Spring AI 提供了多种文档解析器,支持多种格式:

支持的文档格式:
// 常见解析器
- PdfDocumentReader       // PDF 文件
- TikaDocumentReader      // 多种格式(docx, pptx, xlsx, html等)
- TextDocumentReader      // 纯文本文件
- MarkdownDocumentReader  // Markdown 文件
- JsonDocumentReader      // JSON 文件
使用示例:
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
    // 解析 PDF 文档
    DocumentReader pdfReader = new PdfDocumentReader("path/to/document.pdf");
    List<Document> documents = pdfReader.read();
    
    // 解析多种格式文档
    TikaDocumentReader tikaReader = new TikaDocumentReader(
        ResourceUtils.getFile("classpath:documents/")
    );
    
    return new SimpleVectorStore(embeddingModel);
}

2. 表格提取

使用 Tika 提取表格:
import org.springframework.ai.reader.tika.TikaDocumentReader;
import org.springframework.ai.document.Document;

public class TableExtractor {
    
    public List<TableData> extractTables(String filePath) {
        TikaDocumentReader reader = new TikaDocumentReader(filePath);
        List<Document> documents = reader.read();
        
        // 使用正则表达式或自定义逻辑提取表格
        return extractTableData(documents);
    }
    
    private List<TableData> extractTableData(List<Document> docs) {
        List<TableData> tables = new ArrayList<>();
        
        for (Document doc : docs) {
            String content = doc.getContent();
            // 提取表格内容(示例:简单的 CSV 格式表格)
            Pattern tablePattern = Pattern.compile("\\|.*?\\|", Pattern.DOTALL);
            Matcher matcher = tablePattern.matcher(content);
            
            while (matcher.find()) {
                tables.add(parseTable(matcher.group()));
            }
        }
        return tables;
    }
}
使用 DocumentTransformers 处理表格:
@Bean
public DocumentTransformer tableExtractorTransformer() {
    return documents -> {
        return documents.stream()
            .map(doc -> {
                String content = doc.getContent();
                // 提取并格式化表格数据
                String extractedTables = extractAndFormatTables(content);
                return new Document(
                    doc.getContent() + "\n\n提取的表格数据:\n" + extractedTables,
                    doc.getMetadata()
                );
            })
            .collect(Collectors.toList());
    };
}

3. 智能总结

使用 ChatClient 进行文档总结:
@Service
public class DocumentSummaryService {
    
    private final ChatClient chatClient;
    
    public DocumentSummaryService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }
    
    public String summarizeDocument(String documentContent) {
        String prompt = """
            请对以下文档内容进行智能总结:
            
            要求:
            1. 提取核心要点
            2. 保持关键数据
            3. 总结主要结论
            4. 限制在300字以内
            
            文档内容:
            %s
            """.formatted(documentContent);
        
        return chatClient.call(prompt);
    }
    
    public Map<String, String> structuredSummary(Document document) {
        String prompt = """
            请以结构化格式总结以下文档:
            
            要求返回JSON格式:
            {
              "title": "文档标题",
              "keyPoints": ["要点1", "要点2", ...],
              "summary": "详细总结",
              "keywords": ["关键词1", "关键词2", ...],
              "actionItems": ["行动项1", "行动项2", ...]
            }
            
            文档内容:
            %s
            """.formatted(document.getContent());
        
        String jsonResponse = chatClient.call(prompt);
        return parseJsonResponse(jsonResponse);
    }
}
批量文档处理管道:
@Configuration
public class DocumentProcessingPipeline {
    
    @Bean
    public Function<List<Document>, List<Document>> documentProcessingPipeline() {
        return documents -> {
            // 1. 文档解析和清理
            DocumentTransformer cleaner = new ContentFormattingTransformer();
            
            // 2. 表格提取
            DocumentTransformer tableExtractor = new TableExtractionTransformer();
            
            // 3. 文本分割(用于向量化)
            DocumentTransformer splitter = new TokenTextSplitter(
                1000,  // 最大token数
                200    // 重叠token数
            );
            
            // 4. 应用转换链
            return documents.stream()
                .map(cleaner::apply)
                .map(tableExtractor::apply)
                .flatMap(doc -> splitter.apply(doc).stream())
                .collect(Collectors.toList());
        };
    }
}

4. 完整示例:端到端文档处理

@Service
public class CompleteDocumentProcessor {
    
    private final ChatClient chatClient;
    private final VectorStore vectorStore;
    private final EmbeddingModel embeddingModel;
    
    public CompleteDocumentProcessor(ChatClient chatClient, 
                                    VectorStore vectorStore,
                                    EmbeddingModel embeddingModel) {
        this.chatClient = chatClient;
        this.vectorStore = vectorStore;
        this.embeddingModel = embeddingModel;
    }
    
    public ProcessingResult processDocument(String filePath) {
        // 1. 解析文档
        DocumentReader reader = new TikaDocumentReader(filePath);
        List<Document> documents = reader.read();
        
        // 2. 提取表格
        List<TableData> tables = extractTables(documents);
        
        // 3. 生成智能总结
        String fullText = documents.stream()
            .map(Document::getContent)
            .collect(Collectors.joining("\n"));
        
        String summary = generateSummary(fullText);
        
        // 4. 向量化存储
        List<Document> processedDocs = new TokenTextSplitter(1000, 200)
            .apply(documents);
        
        vectorStore.add(processedDocs);
        
        // 5. 返回结果
        return new ProcessingResult(
            documents,
            tables,
            summary,
            processedDocs.size()
        );
    }
    
    private String generateSummary(String text) {
        String prompt = """
            你是一个专业的文档分析助手。请完成以下任务:
            
            1. 文档总结(不超过200字)
            2. 提取3-5个关键点
            3. 识别重要数据/数字
            4. 如果有表格,总结表格主要内容
            
            文档内容:
            %s
            """.formatted(text.substring(0, Math.min(text.length(), 4000)));
        
        return chatClient.call(prompt);
    }
    
    public String queryDocument(String question) {
        // 使用向量搜索找到相关文档片段
        List<Document> relevantDocs = vectorStore.similaritySearch(
            SearchRequest.query(question).withTopK(3)
        );
        
        String context = relevantDocs.stream()
            .map(Document::getContent)
            .collect(Collectors.joining("\n\n"));
        
        String prompt = """
            基于以下文档内容回答问题:
            
            文档内容:
            %s
            
            问题:%s
            
            要求:如果文档中有表格数据,请引用表格内容。
            """.formatted(context, question);
        
        return chatClient.call(prompt);
    }
}

5. 配置建议

# application.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      chat:
        options:
          model: gpt-4
          temperature: 0.1  # 降低创造性,提高准确性
    
    vectorstore:
      pgvector:
        enabled: true
        initialize-schema: true
    
    document:
      splitter:
        chunk-size: 1000
        chunk-overlap: 200

6. 最佳实践

  1. 分块策略 :根据文档类型调整分块大小
  1. 技术文档:500-800 tokens
  2. 法律文档:200-400 tokens
  3. 普通文章:1000 tokens
  1. 表格处理 :
  1. 将表格转换为Markdown格式存储
  2. 为表格添加描述性元数据
  3. 考虑使用专门的表格识别库(如Tabula for PDF)
  1. 总结优化 :
  1. 使用few-shot提示提高总结质量
  2. 为不同文档类型定制总结模板
  3. 实现增量总结(用于长文档)
  1. 性能考虑 :
  1. 对大文档实现流式处理
  2. 缓存处理结果
  3. 并行处理多个文档

Spring AI 的这些功能可以很好地集成到企业文档处理流程中,实现自动化的文档解析、信息提取和智能分析。

3. 文本翻译、内容润色、代码优化AI工具开发

我来帮你设计一个基于Spring AI的AI工具开发方案,包含文本翻译、内容润色和代码优化三大功能。

项目架构设计

# Maven依赖
dependencies:
- Spring Boot 3.x
- Spring AI (支持OpenAI/Azure OpenAI/本地模型)
- Spring Web
- Spring Validation

核心功能实现

2.1 统一AI服务接口

@Service
public class AIService {
    
    @Autowired
    private ChatClient chatClient;
    
    // 文本翻译
    public String translate(String text, String sourceLang, String targetLang) {
        String prompt = String.format("""
            请将以下%s文本翻译成%s:
            原文:%s
            
            要求:
            1. 保持原意准确
            2. 符合目标语言表达习惯
            3. 专业术语准确
            """, sourceLang, targetLang, text);
        
        return chatClient.call(prompt);
    }
    
    // 内容润色
    public String polishContent(String text, PolishStyle style) {
        String stylePrompt = switch(style) {
            case FORMAL -> "正式、专业的语气";
            case CASUAL -> "轻松、口语化的语气";
            case ACADEMIC -> "学术、严谨的语气";
            case CONCISE -> "简洁、精炼的表达";
        };
        
        String prompt = String.format("""
            请润色以下文本,要求:
            1. 保持原意不变
            2. 使用%s
            3. 优化语法和表达
            4. 提升可读性
            
            原文:%s
            """, stylePrompt, text);
        
        return chatClient.call(prompt);
    }
    
    // 代码优化
    public String optimizeCode(String code, String language, OptimizationGoal goal) {
        String goalPrompt = switch(goal) {
            case PERFORMANCE -> "提升性能,优化算法复杂度";
            case READABILITY -> "提高代码可读性和可维护性";
            case SECURITY -> "增强安全性,修复潜在漏洞";
            case BEST_PRACTICE -> "遵循最佳实践和设计模式";
        };
        
        String prompt = String.format("""
            请优化以下%s代码:
            %s
            
            优化目标:%s
            
            要求:
            1. 提供优化后的完整代码
            2. 说明优化点和原因
            3. 保持原有功能不变
            4. 添加必要的注释
            """, language, code, goalPrompt);
        
        return chatClient.call(prompt);
    }
}

2.2 RESTful API接口

@RestController
@RequestMapping("/api/ai")
public class AIController {
    
    @PostMapping("/translate")
    public ResponseEntity<TranslationResponse> translate(
            @Valid @RequestBody TranslationRequest request) {
        String result = aiService.translate(
            request.getText(),
            request.getSourceLang(),
            request.getTargetLang()
        );
        return ResponseEntity.ok(new TranslationResponse(result));
    }
    
    @PostMapping("/polish")
    public ResponseEntity<PolishResponse> polish(
            @Valid @RequestBody PolishRequest request) {
        String result = aiService.polishContent(
            request.getText(),
            request.getStyle()
        );
        return ResponseEntity.ok(new PolishResponse(result));
    }
    
    @PostMapping("/optimize-code")
    public ResponseEntity<CodeOptimizationResponse> optimizeCode(
            @Valid @RequestBody CodeOptimizationRequest request) {
        String result = aiService.optimizeCode(
            request.getCode(),
            request.getLanguage(),
            request.getGoal()
        );
        return ResponseEntity.ok(new CodeOptimizationResponse(result));
    }
}

2.3 配置类

@Configuration
public class AIConfig {
    
    @Value("${spring.ai.openai.api-key}")
    private String apiKey;
    
    @Value("${spring.ai.openai.model}")
    private String model;
    
    @Bean
    public OpenAiChatClient openAiChatClient() {
        OpenAiApi openAiApi = new OpenAiApi("https://api.openai.com/v1", apiKey);
        return new OpenAiChatClient(openAiApi, model);
    }
}

高级功能扩展

3.1 批量处理服务

@Service
public class BatchAIService {
    
    public List<TranslationResult> batchTranslate(
            List<String> texts, 
            String sourceLang, 
            String targetLang) {
        
        return texts.parallelStream()
            .map(text -> {
                try {
                    String translated = aiService.translate(text, sourceLang, targetLang);
                    return TranslationResult.success(text, translated);
                } catch (Exception e) {
                    return TranslationResult.failure(text, e.getMessage());
                }
            })
            .collect(Collectors.toList());
    }
}

3.2 历史记录与缓存

@Entity
@Table(name = "ai_operations")
public class AIOperation {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    private String operationType; // TRANSLATE, POLISH, OPTIMIZE
    private String input;
    private String output;
    private String modelUsed;
    private LocalDateTime createdAt;
    
    @PrePersist
    protected void onCreate() {
        createdAt = LocalDateTime.now();
    }
}

3.3 流式响应(SSE)

@GetMapping(value = "/stream/translate", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> streamTranslate(
        @RequestParam String text,
        @RequestParam String targetLang) {
    
    return Flux.create(sink -> {
        // 模拟流式输出
        String[] words = text.split(" ");
        for (int i = 0; i < words.length; i++) {
            sink.next("翻译进度: " + (i + 1) + "/" + words.length);
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                sink.error(e);
            }
        }
        sink.complete();
    });
}

应用配置文件

# application.yml
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}
      model: gpt-4
      temperature: 0.7
      max-tokens: 2000
    
  datasource:
    url: jdbc:mysql://localhost:3306/ai_tools
    username: root
    password: password
    
  jpa:
    hibernate:
      ddl-auto: update

server:
  port: 8080

# 自定义配置
ai:
  tools:
    max-input-length: 5000
    enable-cache: true
    default-language: zh-CN
    rate-limit:
      requests-per-minute: 60

前端示例(Vue.js)

<template>
  <div class="ai-tools">
    <!-- 翻译功能 -->
    <div class="tool-card">
      <h3>文本翻译</h3>
      <select v-model="sourceLang">
        <option value="zh">中文</option>
        <option value="en">英文</option>
        <option value="ja">日文</option>
      </select>
      <select v-model="targetLang">
        <option value="en">英文</option>
        <option value="zh">中文</option>
        <option value="ja">日文</option>
      </select>
      <textarea v-model="textToTranslate" placeholder="输入要翻译的文本"></textarea>
      <button @click="translate">翻译</button>
      <div class="result">{{ translationResult }}</div>
    </div>
    
    <!-- 代码优化 -->
    <div class="tool-card">
      <h3>代码优化</h3>
      <select v-model="codeLanguage">
        <option value="java">Java</option>
        <option value="python">Python</option>
        <option value="javascript">JavaScript</option>
      </select>
      <textarea v-model="codeToOptimize" placeholder="输入代码"></textarea>
      <button @click="optimizeCode">优化代码</button>
      <pre class="code-result">{{ optimizedCode }}</pre>
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import axios from 'axios'

const textToTranslate = ref('')
const translationResult = ref('')
const codeToOptimize = ref('')
const optimizedCode = ref('')

const translate = async () => {
  const response = await axios.post('/api/ai/translate', {
    text: textToTranslate.value,
    sourceLang: 'zh',
    targetLang: 'en'
  })
  translationResult.value = response.data.result
}

const optimizeCode = async () => {
  const response = await axios.post('/api/ai/optimize-code', {
    code: codeToOptimize.value,
    language: 'java',
    goal: 'PERFORMANCE'
  })
  optimizedCode.value = response.data.result
}
</script>

部署与监控

# Dockerfile
FROM openjdk:17-jdk-slim
WORKDIR /app
COPY target/ai-tools.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
# docker-compose.yml
version: '3.8'
services:
  ai-tools:
    build: .
    ports:
      - "8080:8080"
    environment:
      - OPENAI_API_KEY=${OPENAI_API_KEY}
      - SPRING_PROFILES_ACTIVE=prod
    depends_on:
      - mysql
      - redis
  
  mysql:
    image: mysql:8.0
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: ai_tools
  
  redis:
    image: redis:alpine

增强功能建议

  1. 多模型支持 :集成多个AI提供商(OpenAI、Claude、本地模型)
  2. 自定义提示词模板 :允许用户保存和重用提示词
  3. API密钥管理 :多用户API密钥轮询使用
  4. 质量评估 :对AI输出进行评分和反馈收集
  5. 插件系统 :支持自定义功能扩展
  6. 团队协作 :共享历史记录和模板

这个方案提供了完整的Spring AI工具开发框架,你可以根据具体需求进行调整和扩展。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐