package com.teacher.service.Impl;
import com.google.common.util.concurrent.RateLimiter;
import com.teacher.exception.AiServiceException;
import com.teacher.model.dto.AiRequest;
import com.teacher.model.dto.AiResponse;
import com.teacher.model.dto.TeachingPlanDto;
import com.teacher.service.AiService;
import com.teacher.util.QwenClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Service
public class AiServiceImpl implements AiService {
private static final double AI_RATE_LIMIT = 5.0; // 5 requests per second
private final QwenClient qwenClient;
private final RateLimiter rateLimiter = RateLimiter.create(AI_RATE_LIMIT);
@Autowired
public AiServiceImpl(QwenClient qwenClient) {
this.qwenClient = qwenClient;
}
@Override
public AiResponse chatWithAI(AiRequest request) {
try {
// 限流保护
if (!rateLimiter.tryAcquire(1, TimeUnit.SECONDS)) {
throw new AiServiceException("AI服务请求过载,请稍后再试");
}
// 调用AI服务
String response = qwenClient.callQwenApi(request.getPrompt());
return new AiResponse(response, System.currentTimeMillis());
} catch (Exception e) {
throw new AiServiceException("AI服务调用失败", e);
}
}
@Override
public String generateSummary(String content, int maxLength) {
String prompt = String.format(
"请为以下内容生成一个简洁的摘要,不超过%d个字符:\n\n%s",
maxLength, content
);
return callQwenApiWithRetry(prompt, 2);
}
@Override
public List<String> extractKnowledgePoints(String content) {
String prompt = String.format(
"请从以下教学内容中提取关键知识点,每个知识点用短句表示,按重要性排序:\n\n%s",
content
);
String response = callQwenApiWithRetry(prompt, 2);
return parseKnowledgePoints(response);
}
@Override
public String correctText(String text) {
String prompt = String.format(
"请校对以下文本,修正语法、拼写和标点错误,保持原意不变:\n\n%s",
text
);
return callQwenApiWithRetry(prompt, 1);
}
@Override
public String generateMultimediaContent(String prompt, String mediaType) {
String fullPrompt = String.format(
"根据以下描述生成%s内容:\n%s\n\n请使用适合%s的格式输出",
mediaType, prompt, mediaType
);
return callQwenApiWithRetry(fullPrompt, 2);
}
private String callQwenApiWithRetry(String prompt, int maxRetries) {
int attempt = 0;
while (attempt < maxRetries) {
try {
return qwenClient.callQwenApi(prompt);
} catch (Exception e) {
attempt++;
if (attempt >= maxRetries) {
throw new AiServiceException("AI服务调用失败,重试次数已用完", e);
}
// 指数退避重试
try {
Thread.sleep((long) (Math.pow(2, attempt) * 1000));
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
}
}
}
return "AI服务暂时不可用";
}
private List<String> parseKnowledgePoints(String response) {
List<String> points = new ArrayList<>();
String[] lines = response.split("\n");
for (String line : lines) {
if (line.matches("^\\d+\\.\\s+.+")) {
points.add(line.substring(line.indexOf('.') + 1).trim());
}
}
return points;
}
// 实现生成课程设计方法
@Override
public String generateCourseDesign(String subject, List<String> knowledgePoints) {
String knowledgeList = String.join("、", knowledgePoints);
String prompt = String.format(
"请为%s学科设计一个完整的课程,包含以下知识点:%s。要求包括教学目标、教学活动和评估方法。",
subject, knowledgeList
);
return callQwenApiWithRetry(prompt, 2);
}
// 实现生成教学计划方法
@Override
public TeachingPlanDto generateTeachingPlan(String subject, List<String> keywords) {
String keywordStr = String.join("、", keywords);
String prompt = String.format(
"请为%s学科生成一个详细的教学计划,关键词包括:%s。要求包括年级、计划标题、描述、时长和多个教学单元(每个单元包含主题、目标、知识点、资源和评估方法)。",
subject, keywordStr
);
String response = callQwenApiWithRetry(prompt, 2);
return parseTeachingPlan(response);
}
// 实现分析学生答案方法
@Override
public String analyzeAnswer(String question, String studentAnswer, String correctAnswer) {
String prompt = String.format(
"问题:%s\n学生的答案:%s\n正确答案:%s\n请分析学生答案中的错误,并给出改进建议。",
question, studentAnswer, correctAnswer
);
return callQwenApiWithRetry(prompt, 2);
}
// 解析教学计划响应的辅助方法
private TeachingPlanDto parseTeachingPlan(String response) {
TeachingPlanDto plan = new TeachingPlanDto();
// 简化的解析逻辑 - 实际项目中应使用更健壮的解析方法
if (response.contains("标题:")) {
plan.setTitle(response.substring(response.indexOf("标题:") + 3, response.indexOf("\n")));
}
// 设置其他字段...
return plan;
}
}
出现了'com.teacher.util.QwenClient' 中的 'callQwenApi(com.teacher.model.dto.AiRequest)' 无法应用于 '(java.lang.String)'
以下是QwenClient的代码package com.teacher.util;
import com.teacher.model.dto.AiRequest;
import lombok.Setter;
import org.springframework.http.*;
import org.springframework.web.client.RestTemplate;
import java.util.HashMap;
import java.util.Map;
public class QwenClient {
// 添加必要的 setter 方法
@Setter
private String apiKey;
@Setter
private String defaultModel;
@Setter
private int defaultMaxTokens;
@Setter
private double defaultTemperature;
private final RestTemplate restTemplate;
private static final String API_URL = "https://dashscope.aliyuncs.com/api/v1/services/aigc/text-generation/generation";
public QwenClient(RestTemplate restTemplate) {
this.restTemplate = restTemplate;
}
/**
* 调用通义千问 API
*
* @param request AI 请求参数
* @return API 响应内容
*/
public String callQwenApi(AiRequest request) {
// 设置请求头
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
headers.set("Authorization", "Bearer " + apiKey);
// 构建请求体
Map<String, Object> requestBody = new HashMap<>();
requestBody.put("model", request.getModel() != null ? request.getModel() : defaultModel);
Map<String, Object> input = new HashMap<>();
input.put("prompt", request.getPrompt());
if (request.getContext() != null) {
input.put("context", request.getContext());
}
requestBody.put("input", input);
Map<String, Object> parameters = new HashMap<>();
parameters.put("max_tokens", request.getMaxTokens() > 0 ? request.getMaxTokens() : defaultMaxTokens);
parameters.put("temperature", request.getTemperature() >= 0 ? request.getTemperature() : defaultTemperature);
parameters.put("stream", request.isStream());
if (request.getUserId() != null) {
parameters.put("user", request.getUserId());
}
requestBody.put("parameters", parameters);
// 发送请求
HttpEntity<Map<String, Object>> entity = new HttpEntity<>(requestBody, headers);
ResponseEntity<Map> response = restTemplate.exchange(
API_URL,
HttpMethod.POST,
entity,
Map.class
);
// 处理响应
if (response.getStatusCode() == HttpStatus.OK && response.getBody() != null) {
Map<String, Object> output = (Map<String, Object>) response.getBody().get("output");
if (output != null && output.containsKey("text")) {
return (String) output.get("text");
}
}
throw new RuntimeException("API调用失败: " + response.getStatusCode());
}
}