当前位置: 首页 > news >正文

错题分析接口实现全流程

一、整体流程介绍

@PostMapping(value = "/choice") 定义一个 POST 接口,接收一个 JSON 格式的 AnalysisRequest 请求体,返回一个 Map<String, Object> 类型的响应(包含分析结果或错误信息)。

核心处理流程:

  1.     日志记录 + 参数校验
  2.     构造 AI 请求消息(System + User Prompt)
  3.     调用 AI 接口分析错题
  4.     解析 AI 的 JSON 响应
  5.     封装为结构化数据对象 AnalysisResponse
  6.     记录日志、返回响应

二、接口入口解析

接口入口:analyzeChoiceQuestions()

核心流程:

构造提示词(Prompt):

String systemPrompt = buildSystemPrompt(request);
String userMessage = buildUserMessage(request);

调用 AI 接口分析错题:

String aiResponse = analyzeWithAISync(systemPrompt, userMessage, request);

解析 AI 返回结果:

AnalysisResponse analysisResponse = parseAIResponse(analysisId, request, aiResponse);

构造返回数据结构:

result.put("data", analysisResponse);
result.put("analysis", analysisResponse.getOverallAnalysis());

三、核心方法细讲

1.AI 调用:analyzeWithAISync()

方法签名:

private String analyzeWithAISync(String systemPrompt, String userMessage, AnalysisRequest request)throws IOException, InterruptedException

入参:

    systemPrompt:系统指令(告诉 AI 它的角色、职责)

    userMessage:用户输入(具体要分析的内容)

    request:分析请求体(虽然这里没有直接使用,但可能用于日志、回溯等)

返回值:AI 回复的正文 content 字符串

构造消息结构:

List<Map<String, String>> messages = new ArrayList<>();

 创建一个用于存储对话的消息列表,每条消息是一个 Map(角色+内容)。

使用 OkHttp 客户端发起 HTTP 请求:

OkHttpClient client = new OkHttpClient.Builder().build();

发送 POST 请求,模型使用的是:

params.put("model", "deepseek-chat");

携带两种类型的 Prompt:

System Prompt:指导 AI 扮演教学分析师

Map<String, String> systemMessage = new HashMap<>();
systemMessage.put("role", "system");
systemMessage.put("content", systemPrompt);
messages.add(systemMessage);

User Prompt:具体的错题信息、题干、选项、用户作答等

Map<String, String> userMsg = new HashMap<>();
userMsg.put("role", "user");
userMsg.put("content", userMessage);
messages.add(userMsg);

最终效果:

[{"role": "system", "content": "你是一个老师..."},{"role": "user", "content": "以下是我的答题..." }
]

 构造请求参数(组装 API 的 payload)

Map<String, Object> params = new HashMap<>();
params.put("model", "deepseek-chat");
params.put("stream", false);
params.put("messages", messages);
params.put("temperature", 0.7);

model:指定使用的模型(比如 deepseek-chat)

stream:是否启用流式返回(false 表示同步一次性拿到结果)

messages:传入的对话历史(包括 system 和 user)

temperature:控制 AI 输出的“随机程度”,数值越高越发散。

String jsonParams = JsonUtils.convertObj2Json(params);

使用工具类将 Map 转换为 JSON 字符串,便于发送。

构建 HTTP 请求

Request.Builder builder = new Request.Builder().url(AI_URL);
builder.addHeader("Authorization", "Bearer " + apiPassword);
builder.addHeader("Content-Type", "application/json");

指定 URL(AI 的接口地址)

添加鉴权(Bearer Token)

设置请求头为 JSON 格式

okhttp3.RequestBody body = okhttp3.RequestBody.create(okhttp3.MediaType.parse("application/json; charset=utf-8"),jsonParams
);
Request httpRequest = builder.post(body).build();

设置请求体为 JSON

构造 POST 请求

创建 OkHttp 客户端并发起同步请求

OkHttpClient client = new OkHttpClient.Builder().connectTimeout(timeout, TimeUnit.SECONDS).writeTimeout(timeout, TimeUnit.SECONDS).readTimeout(timeout, TimeUnit.SECONDS).build();

配置超时时间(连接、写入、读取)

从 AI 响应中提取 "content"

Map<String, Object> responseMap = JsonUtils.convertJson2Obj(responseBody, Map.class);

将 JSON 字符串反序列化为 Map 结构

if (responseMap != null && responseMap.containsKey("choices")) {List<Map<String, Object>> choices = (List<Map<String, Object>>) responseMap.get("choices");if (!choices.isEmpty()) {Map<String, Object> firstChoice = choices.get(0);if (firstChoice.containsKey("message")) {Map<String, Object> message = (Map<String, Object>) firstChoice.get("message");if (message.containsKey("content")) {return (String) message.get("content");}}}
}

DeepSeek 通用响应结构如下:

{"choices": [{"message": {"role": "assistant","content": "分析结果内容..."}}]
}

上述逻辑层层检查字段是否存在,最终提取 message.content 字段作为 AI 的回答文本。

2.解析AI返回:parseAIResponse()

方法定义:

private AnalysisResponse parseAIResponse(Long analysisId, AnalysisRequest request, String aiResponse)

analysisId:本次分析的唯一 ID。

request:用户发起请求时的详细信息,包含题目、正确率、难度等。

aiResponse:AI 模型返回的原始字符串,预期为 JSON 格式。

返回值为构建好的 AnalysisResponse 对象。

流程:

创建返回对象并设置分析ID:

AnalysisResponse response = new AnalysisResponse();
response.setAnalysisId(analysisId);

这里创建一个空的响应对象 response,并先设置分析 ID。

清理 AI 响应内容:

String cleanedResponse = cleanAIResponse(aiResponse);
logger.debug("清理后的AI响应: {}", cleanedResponse);

AI 可能返回带有 Markdown 代码块标记(如 json ... ),或者格式不规范,因此调用 cleanAIResponse 做预处理。

这一步是为了解决 AI 返回不是纯净 JSON 的问题,提高解析成功率。

解析 JSON 内容:

Map<String, Object> parsedResponse = JsonUtils.convertJson2Obj(cleanedResponse, Map.class);

将清理后的字符串解析成 Map<String, Object> 结构,然后使用工具类 JsonUtils 将 JSON 转换为 Java Map 便于访问字段。

成功解析 JSON 的情况:

如果 parsedResponse 不为 null,则逐个提取字段:

提取整体分析总结:  

response.setOverallAnalysis((String) parsedResponse.get("overallAnalysis"));
response.setLearningPlan((String) parsedResponse.get("learningPlan"));

提取每道题的分析信息:

if (parsedResponse.containsKey("questionAnalyses")) {List<Map<String, Object>> questionAnalyses =(List<Map<String, Object>>) parsedResponse.get("questionAnalyses");List<QuestionAnalysis> analyses = new ArrayList<>();for (Map<String, Object> qa : questionAnalyses) {QuestionAnalysis analysis = new QuestionAnalysis();analysis.setQuestion((String) qa.get("question"));analysis.setErrorReason((String) qa.get("errorReason"));analysis.setCorrectAnswerExplanation((String) qa.get("correctAnswerExplanation"));analysis.setImprovementSuggestion((String) qa.get("improvementSuggestion"));analyses.add(analysis);}response.setQuestionAnalyses(analyses);
}

道错题的信息被封装在 questionAnalyses 数组中,每条记录被解析为 QuestionAnalysis 对象,添加到结果中。

提取薄弱知识点:

if (parsedResponse.containsKey("weaknessPoints")) {List<String> weaknessPoints = (List<String>) parsedResponse.get("weaknessPoints");response.setWeaknessPoints(weaknessPoints != null ? weaknessPoints : new ArrayList<>());
}

以这个解析为例,进行详细分析逻辑过程:
 

if (parsedResponse.containsKey("weaknessPoints")) {

    作用:判断 AI 返回的 JSON 数据中是否包含 "weaknessPoints" 这个字段。

    parsedResponse 是一个 Map<String, Object> 类型的数据结构。

    "weaknessPoints" 是 AI 分析输出中标记“知识薄弱点”的字段。

    如果不包含,就什么都不做;如果包含,就进入下一步。

List<String> weaknessPoints = (List<String>) parsedResponse.get("weaknessPoints");

    从 Map 中取出 "weaknessPoints" 对应的值,并尝试将其强制转换为 List<String> 类型。

    假设 AI 返回的结构是类似下面这种 JSON:

{"weaknessPoints": ["函数定义与调用","循环结构的控制","语法错误识别"]
}

那么这里 parsedResponse.get("weaknessPoints") 的结果应该是一个 List,其中每个元素是一个 String。强制转换是必要的,因为从 Map<String, Object> 拿出来的是 Object 类型。

response.setWeaknessPoints(weaknessPoints != null ? weaknessPoints : new ArrayList<>());

目的:设置最终的结果对象 response 的 weaknessPoints 字段。

这个三目运算符是一个空值保护机制:

    如果 AI 的 JSON 中 "weaknessPoints" 字段存在但为 null,就不会直接传 null 进去;

    而是使用一个新的空 List<String>,防止后续出现 NullPointerException。

换句话说:无论 AI 有没有真正提供内容,最终结果中的 weaknessPoints 字段一定不会是 null,而是一个 List(可能为空)。

示例:

假设输入为:

{"overallAnalysis": "...","weaknessPoints": ["集合运算", "图形几何", "函数表达式"]
}

执行完这一段后:

response.getWeaknessPoints()

 返回值为:

["集合运算", "图形几何", "函数表达式"]

提取学习建议:

if (parsedResponse.containsKey("studySuggestions")) {List<String> studySuggestions = (List<String>) parsedResponse.get("studySuggestions");response.setStudySuggestions(studySuggestions != null ? studySuggestions : new ArrayList<>());
}

如果 JSON 解析失败,则降级为文本处理:

parseTextResponse(response, aiResponse, request);

这是一个降级策略,保证系统即使遇到错误数据也能返回“最起码的结果”。

相关文章:

  • Flink 与 Hive 深度集成
  • 【系统分析师】第5章-基础知识:数据库系统(核心总结)
  • Oracle 单实例双IP配置
  • List的简单模拟实现
  • 树莓派智能小车基本移动实验指导书
  • SSH参数优化与内网穿透技术融合:打造高效远程访问解决方案
  • [CVPR 2025] DiCo:动态协作网络助力半监督3D血管分割新突破
  • Java开发中避免NullPointerException的全面指南
  • RabbitMQ--集群
  • java设计模式[2]之创建型模式
  • 学习设计模式《十三》——迭代器模式
  • C++线性DP-最优解问题、方案数问题
  • Design Compiler:解组(Ungroup)
  • 自编码模型原理
  • Python中提取图片特征的指南
  • 2.2 订阅话题
  • 在Ubuntu linux终端写文件的方法
  • DataWhale-零基础网络爬虫技术(一)
  • 互联网大厂Java求职面试:云原生架构与微服务设计中的复杂挑战
  • sql列中数据通过逗号分割的集合,对其中的值进行全表查重
  • 用家里的电脑做网站服务器/软文客
  • 公安部/宁波seo营销平台
  • 假发的出口做b2c网站/西安seo全网营销
  • 网站开发和推广的不同/军事新闻 今日关注
  • 产品介绍网站如何做seo/友情链接交换
  • 深度网营销型网站建设公司怎么样/申泽seo