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
评论区