Spring集成AI模型(通义千问)与业务融合

 前言:现在AI大模型已经在各行各业大放异彩,相信各位同学在日常的开发中也经常使用各种AI解决我们遇到的问题,那么在我们的业务中接入了AI,这样就能让AI自动帮我们解决问题,这样会极大的提升我们的效率,那么在下文中,我将介绍如何把通义千问集成到spring、并且如何结合业务,业务功能为让通义千问帮我生成一段博客简介

一、项目依赖 (pom.xml)

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>Ai-Access</artifactId>
	<version>1.0.0</version>
	<packaging>jar</packaging>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.7.0</version>
	</parent>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-validation</artifactId>
		</dependency>
		<dependency>
			<groupId>com.fasterxml.jackson.core</groupId>
			<artifactId>jackson-databind</artifactId>
		</dependency>
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

		<!-- 添加配置处理器 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-configuration-processor</artifactId>
			<optional>true</optional>
		</dependency>
	</dependencies>
</project>

二、配置文件 (application.yml)

spring:
  application:
    name: Ai-Access
server:
  port: 8081

tongyi:
  api:
    key: "apiKey"  # 在阿里云百炼平台获取(有免费token额度)
    url: "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation"
    model: "qwen-turbo"  # 也可以使用 qwen-plus, qwen-max 等

三、配置类

package test.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@Data
@Configuration
@ConfigurationProperties(prefix = "tongyi.api")
public class TongYiConfig {
    private String key;
    private String url;
    private String model;
}

四、实体类

4.1 请求对象

package test.entity;

import lombok.Data;

import javax.validation.constraints.NotBlank;

@Data
public class BlogGenerateRequest {

    @NotBlank(message = "博客标题不能为空")
    private String title;

    private String keywords;

    private String contentOutline;

    private Integer targetLength = 200; // 简介目标长度
}

4.2 调用通义的请求对象

package test.entity;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class TongYiRequest {
    @JsonProperty("model")
    private String model;

    @JsonProperty("input")
    private Input input;

    @JsonProperty("parameters")
    private Parameters parameters;

    @Data
    public static class Input {
        @JsonProperty("messages")
        private Message[] messages;
    }

    @Data
    public static class Message {
        @JsonProperty("role")
        private String role;

        @JsonProperty("content")
        private String content;
    }

    @Data
    public static class Parameters {
        @JsonProperty("result_format")
        private String resultFormat = "message";

        // 添加温度参数控制随机性
        @JsonProperty("temperature")
        private Double temperature = 0.8;

        // 添加top_p参数
        @JsonProperty("top_p")
        private Double topP = 0.8;
    }
}

4.3 通义的响应对象

package test.entity;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;

@Data
public class TongYiResponse {
    @JsonProperty("output")
    private Output output;

    @JsonProperty("usage")
    private Usage usage;

    @JsonProperty("request_id")
    private String requestId;

    @Data
    public static class Output {
        @JsonProperty("text")
        private String text;

        @JsonProperty("finish_reason")
        private String finishReason;

        // 通义千问可能使用choices数组
        @JsonProperty("choices")
        private java.util.List<Choice> choices;
    }

    @Data
    public static class Choice {
        @JsonProperty("message")
        private Message message;

        @JsonProperty("finish_reason")
        private String finishReason;
    }

    @Data
    public static class Message {
        @JsonProperty("content")
        private String content;
    }

    @Data
    public static class Usage {
        @JsonProperty("input_tokens")
        private Integer inputTokens;

        @JsonProperty("output_tokens")
        private Integer outputTokens;

        @JsonProperty("total_tokens")
        private Integer totalTokens;
    }

    // 获取文本内容的辅助方法
    public String getResponseText() {
        if (output != null) {
            // 优先从choices中获取
            if (output.getChoices() != null && !output.getChoices().isEmpty()) {
                Message message = output.getChoices().get(0).getMessage();
                if (message != null && message.getContent() != null) {
                    return message.getContent();
                }
            }
            // 其次从text字段获取
            if (output.getText() != null) {
                return output.getText();
            }
        }
        return null;
    }
}

4.4 服务的响应对象

package test.entity;

import lombok.Data;

@Data
public class ApiResponse<T> {
    private boolean success;
    private String message;
    private T data;
    
    public static <T> ApiResponse<T> success(T data) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setSuccess(true);
        response.setMessage("成功");
        response.setData(data);
        return response;
    }
    
    public static <T> ApiResponse<T> error(String message) {
        ApiResponse<T> response = new ApiResponse<>();
        response.setSuccess(false);
        response.setMessage(message);
        return response;
    }
}

五、通义千问服务类

package test.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import test.config.TongYiConfig;
import test.entity.TongYiRequest;
import test.entity.TongYiResponse;

import javax.annotation.Resource;

@Slf4j
@Service
public class TongYiService {

    @Resource
    private TongYiConfig tongYiConfig;
    @Resource
    private RestTemplate restTemplate;
    @Resource
    private ObjectMapper objectMapper;

    public String generateBlogSummary(String title, String keywords, String contentOutline, Integer targetLength) {
        try {
            String prompt = buildPrompt(title, keywords, contentOutline, targetLength);
            TongYiRequest request = buildTongYiRequest(prompt);
            log.info("调用通义千问API,模型: {}", tongYiConfig.getModel());
            log.debug("请求Prompt: {}", prompt);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            headers.set("Authorization", "Bearer " + tongYiConfig.getKey());
            HttpEntity<TongYiRequest> entity = new HttpEntity<>(request, headers);
            ResponseEntity<TongYiResponse> response = restTemplate.exchange(
                    tongYiConfig.getUrl(),
                    HttpMethod.POST,
                    entity,
                    TongYiResponse.class
            );
            log.info("API响应状态: {}", response.getStatusCode());
            if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
                TongYiResponse responseBody = response.getBody();
                log.debug("完整响应: {}", objectMapper.writeValueAsString(responseBody));
                String summary = extractTextFromResponse(responseBody);
                if (summary != null && !summary.trim().isEmpty()) {
                    log.info("AI生成的博客简介成功,长度: {}", summary.length());
                    return summary.trim();
                } else {
                    log.warn("API返回内容为空");
                    throw new RuntimeException("AI返回内容为空");
                }
            } else {
                throw new RuntimeException("API调用失败: " + response.getStatusCode());
            }
        } catch (Exception e) {
            log.error("调用通义千问API失败", e);
            throw new RuntimeException("生成博客简介失败: " + e.getMessage());
        }
    }

    /**
     * 从响应中提取文本内容(适配多种响应格式)
     */
    private String extractTextFromResponse(TongYiResponse response) {
        if (response == null) {
            return null;
        }
        // 方式1:从output.text获取
        if (response.getOutput() != null && response.getOutput().getText() != null) {
            return response.getOutput().getText();
        }
        // 方式2:从choices[0].message.content获取
        if (response.getOutput() != null &&
                response.getOutput().getChoices() != null &&
                !response.getOutput().getChoices().isEmpty()) {
            TongYiResponse.Choice firstChoice = response.getOutput().getChoices().get(0);
            if (firstChoice.getMessage() != null && firstChoice.getMessage().getContent() != null) {
                return firstChoice.getMessage().getContent();
            }
        }
        return null;
    }

    private String buildPrompt(String title, String keywords, String contentOutline, Integer targetLength) {
        StringBuilder prompt = new StringBuilder();
        prompt.append("请你作为一个专业的博客作者,为下面的博客内容生成一段吸引人的简介。\n\n");

        prompt.append("博客标题:").append(title).append("\n");

        if (keywords != null && !keywords.trim().isEmpty()) {
            prompt.append("关键词:").append(keywords).append("\n");
        }

        if (contentOutline != null && !contentOutline.trim().isEmpty()) {
            prompt.append("内容大纲:").append(contentOutline).append("\n");
        }

        prompt.append("\n要求:\n");
        prompt.append("1. 简介长度约").append(targetLength).append("字\n");
        prompt.append("2. 语言生动有趣,能够吸引读者\n");
        prompt.append("3. 突出博客的核心价值和亮点\n");
        prompt.append("4. 不要使用'本文'、'本博客'这样的词语\n");
        prompt.append("5. 直接输出简介内容,不要有其他解释\n");

        return prompt.toString();
    }

    private TongYiRequest buildTongYiRequest(String prompt) {
        TongYiRequest request = new TongYiRequest();
        request.setModel(tongYiConfig.getModel());

        // 构建输入消息
        TongYiRequest.Input input = new TongYiRequest.Input();
        TongYiRequest.Message[] messages = new TongYiRequest.Message[1];
        messages[0] = new TongYiRequest.Message();
        messages[0].setRole("user");
        messages[0].setContent(prompt);
        input.setMessages(messages);
        request.setInput(input);

        // 构建参数 - 确保使用同步调用格式
        TongYiRequest.Parameters parameters = new TongYiRequest.Parameters();
        parameters.setResultFormat("message"); // 使用message格式
        parameters.setTemperature(0.8);
        parameters.setTopP(0.8);
        request.setParameters(parameters);

        return request;
    }
}

六、业务服务类

package test.service;

import org.springframework.stereotype.Service;
import test.entity.BlogGenerateRequest;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;

@Service
public class BlogAIService {

    @Resource
    private TongYiService tongYiService;
    
    public String generateBlogSummary(BlogGenerateRequest request) {
        // 参数验证和默认值处理
        String title = request.getTitle();
        String keywords = request.getKeywords();
        String contentOutline = request.getContentOutline();
        Integer targetLength = request.getTargetLength() != null ? request.getTargetLength() : 200;
        
        // 调用AI服务生成简介
        return tongYiService.generateBlogSummary(title, keywords, contentOutline, targetLength);
    }
    
    // 批量生成简介的方法
    public Map<String, String> generateBatchSummaries(Map<String, BlogGenerateRequest> requests) {
        Map<String, String> results = new HashMap<>();
        for (Map.Entry<String, BlogGenerateRequest> entry : requests.entrySet()) {
            try {
                String summary = generateBlogSummary(entry.getValue());
                results.put(entry.getKey(), summary);
            } catch (Exception e) {
                results.put(entry.getKey(), "生成失败: " + e.getMessage());
            }
        }
        return results;
    }
}

七、控制器类

package test.controller;

import org.springframework.web.bind.annotation.*;
import test.entity.ApiResponse;
import test.entity.BlogGenerateRequest;
import test.service.BlogAIService;

import javax.annotation.Resource;
import javax.validation.Valid;

@RestController
@RequestMapping("/api/blog")
public class BlogAIController {

    @Resource
    private BlogAIService blogAIService;
    
    @PostMapping("/generate-summary")
    public ApiResponse<String> generateBlogSummary(@Valid @RequestBody BlogGenerateRequest request) {
        try {
            String summary = blogAIService.generateBlogSummary(request);
            return ApiResponse.success(summary);
        } catch (Exception e) {
            return ApiResponse.error("生成博客简介失败: " + e.getMessage());
        }
    }
    
    @GetMapping("/health")
    public ApiResponse<String> healthCheck() {
        return ApiResponse.success("服务运行正常");
    }
}

八、Springboot配置类

package test.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

@Configuration
public class AppConfig {
    
    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

九、启动类

package test;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class AiAccessApplication {
    public static void main(String[] args) {
        SpringApplication.run(AiAccessApplication.class, args);
    }
}

十、测试

​​​​

十一、完整源码地址

https://github.com/xiaodonglicn/Ai-Access