diff --git a/src/main/java/com/dsideal/base/AI/TestMax32K.java b/src/main/java/com/dsideal/base/AI/TestMax32K.java index dc635baa..aa3cdccf 100644 --- a/src/main/java/com/dsideal/base/AI/TestMax32K.java +++ b/src/main/java/com/dsideal/base/AI/TestMax32K.java @@ -4,33 +4,205 @@ import com.dsideal.base.DataEase.Model.DataEaseModel; import com.dsideal.base.Util.LocalMysqlConnectUtil; import com.jfinal.plugin.activerecord.Db; import com.jfinal.plugin.activerecord.Record; - +import com.dsideal.base.Util.CallDeepSeek; import java.util.List; import java.util.Map; import java.util.Set; import java.util.LinkedHashSet; import java.util.ArrayList; +import java.util.concurrent.CountDownLatch; +import java.text.SimpleDateFormat; +import java.util.Date; +import cn.hutool.core.io.FileUtil; +import java.io.File; public class TestMax32K { private static final int MAX_CHUNK_SIZE = 30000; // 30K字符限制 public static void main(String[] args) { LocalMysqlConnectUtil.Init(); + + // 直接调用生成综合报告 + String report = generateComprehensiveReport(); + System.out.println("\n=== 最终报告 ==="); + System.out.println(report); + } + + + /** + * 分割过大的单表数据 + */ + private static List splitLargeTable(String tableName, Set fieldNames, + List allTableData, int maxSize) { + List chunks = new ArrayList<>(); + StringBuilder currentTableChunk = new StringBuilder(); + + for (Record dataRecord : allTableData) { + Map columns = dataRecord.getColumns(); + StringBuilder rowData = new StringBuilder(); + rowData.append("["); + + boolean first = true; + for (String fieldName : fieldNames) { + if (!first) rowData.append(","); + Object value = columns.get(fieldName); + if (value instanceof String) { + rowData.append("\"").append(value).append("\""); + } else { + rowData.append(value); + } + first = false; + } + rowData.append("]\n"); + + // 检查是否超过限制 + if (currentTableChunk.length() + rowData.length() > maxSize) { + if (currentTableChunk.length() > 0) { + chunks.add(currentTableChunk.toString()); + currentTableChunk = new StringBuilder(); + } + } + currentTableChunk.append(rowData); + } + + if (currentTableChunk.length() > 0) { + chunks.add(currentTableChunk.toString()); + } + + return chunks; + } + + /** + * 获取分块数据的方法(可供其他类调用) + */ + public static String[] getDataChunks() { + // 这里可以将main方法中的逻辑提取出来,返回分块数据 + // 为了简化,这里只是示例 + return new String[]{"示例数据块1", "示例数据块2"}; + } + + public static String generateComprehensiveReport() { String[] regions = {"文山州", "楚雄州"}; String sql = "select table_name as TABLE_NAME from core_dataset_table where dataset_group_id in (select id from core_dataset_group where pid='1036317909951057920')"; List tableList = Db.use(DataEaseModel.DB_NAME).find(sql); + + // 获取分块数据 + String[] dataChunks = getDataChunks(regions, tableList); + List chunkAnalyses = new ArrayList<>(); + + System.out.println("开始分析 " + dataChunks.length + " 个数据块..."); + + // 第一阶段:流式分析各个数据块 + for (int i = 0; i < dataChunks.length; i++) { + final int chunkIndex = i; + final StringBuilder chunkResult = new StringBuilder(); + final CountDownLatch latch = new CountDownLatch(1); + + String prompt = "请对以下教育数据进行简要分析,重点关注关键指标和趋势,控制在500字以内:\n" + dataChunks[i]; + + System.out.println("\n=== 正在分析第 " + (i + 1) + " 个数据块 ==="); + + CallDeepSeek.callDeepSeekStream(prompt, new CallDeepSeek.SSEListener() { + @Override + public void onData(String data) { + System.out.print(data); + chunkResult.append(data); + } + + @Override + public void onComplete(String fullResponse) { + System.out.println("\n--- 第 " + (chunkIndex + 1) + " 个数据块分析完成 ---\n"); + chunkAnalyses.add(chunkResult.toString()); + latch.countDown(); + } + + @Override + public void onError(String error) { + System.err.println("分析第 " + (chunkIndex + 1) + " 个数据块时出错: " + error); + chunkAnalyses.add("分析失败: " + error); + latch.countDown(); + } + }); + + try { + // 等待当前块分析完成 + latch.await(); + Thread.sleep(1000); // 稍微延迟,避免API调用过于频繁 + } catch (InterruptedException e) { + System.err.println("等待分析结果时被中断: " + e.getMessage()); + } + } + + // 第二阶段:流式生成综合报告 + System.out.println("\n=== 开始生成综合分析报告 ==="); + + StringBuilder combinedAnalysis = new StringBuilder(); + combinedAnalysis.append("基于以下分块分析结果,请生成一份完整的教育数据综合分析报告(3000字以内):\n\n"); + + for (int i = 0; i < chunkAnalyses.size(); i++) { + combinedAnalysis.append("数据块").append(i + 1).append("分析:\n"); + combinedAnalysis.append(chunkAnalyses.get(i)).append("\n\n"); + } + + final StringBuilder finalReport = new StringBuilder(); + final CountDownLatch finalLatch = new CountDownLatch(1); - // 使用字符串数组存储分块数据 + CallDeepSeek.callDeepSeekStream(combinedAnalysis.toString(), new CallDeepSeek.SSEListener() { + @Override + public void onData(String data) { + System.out.print(data); + finalReport.append(data); + } + + @Override + public void onComplete(String fullResponse) { + System.out.println("\n\n=== 综合分析报告生成完成 ==="); + + // 保存报告到文件 + try { + String timestamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()); + String fileName = "教育数据综合分析报告_" + timestamp + ".txt"; + String filePath = "WebRoot/upload/" + fileName; + + FileUtil.writeString(finalReport.toString(), new File(filePath), "UTF-8"); + System.out.println("报告已保存到: " + filePath); + } catch (Exception e) { + System.err.println("保存报告时出错: " + e.getMessage()); + } + + finalLatch.countDown(); + } + + @Override + public void onError(String error) { + System.err.println("生成综合报告时出错: " + error); + finalReport.append("生成失败: ").append(error); + finalLatch.countDown(); + } + }); + + try { + finalLatch.await(); + } catch (InterruptedException e) { + System.err.println("等待最终报告时被中断: " + e.getMessage()); + } + + return finalReport.toString(); + } + + /** + * 提取数据分块逻辑为独立方法 + */ + private static String[] getDataChunks(String[] regions, List tableList) { List dataChunks = new ArrayList<>(); StringBuilder currentChunk = new StringBuilder(); - - // 添加数据说明头部 + String header = "数据说明: 以下是云南省教育数据的压缩格式\n" + "格式: 表名 -> 字段列表 -> 数据行(数组格式)\n" + "地区范围: " + String.join(",", regions) + "\n\n"; currentChunk.append(header); - + // 遍历所有相关数据表 for (Record record : tableList) { String tableName = record.getStr("TABLE_NAME"); @@ -41,7 +213,7 @@ public class TestMax32K { // 为每个地区收集数据 for (String region : regions) { - sql = "select * from `" + tableName + "` where `行政区划`=?"; + String sql = "select * from `" + tableName + "` where `行政区划`=?"; List listContent = Db.use(DataEaseModel.DB_NAME).find(sql, region); if (!listContent.isEmpty()) { @@ -114,76 +286,6 @@ public class TestMax32K { dataChunks.add(currentChunk.toString()); } - // 输出分块结果统计 - System.out.println("总共分成 " + dataChunks.size() + " 个数据块:"); - for (int i = 0; i < dataChunks.size(); i++) { - String chunk = dataChunks.get(i); - System.out.println("数据块 " + (i + 1) + " 长度: " + chunk.length() + " 字符"); - } - - // 返回分块数据数组供后续使用 - String[] chunksArray = dataChunks.toArray(new String[0]); - - // 示例:如何使用分块数据 - System.out.println("\n=== 可以这样使用分块数据 ==="); - for (int i = 0; i < chunksArray.length; i++) { - System.out.println("处理第 " + (i + 1) + " 个数据块..."); - // 这里可以调用DeepSeek API处理每个块 - // String result = CallDeepSeek.callDeepSeek(chunksArray[i]); - System.out.println("块 " + (i + 1) + " 内容预览: " + - chunksArray[i].substring(0, Math.min(200, chunksArray[i].length())) + "..."); - } - } - - /** - * 分割过大的单表数据 - */ - private static List splitLargeTable(String tableName, Set fieldNames, - List allTableData, int maxSize) { - List chunks = new ArrayList<>(); - StringBuilder currentTableChunk = new StringBuilder(); - - for (Record dataRecord : allTableData) { - Map columns = dataRecord.getColumns(); - StringBuilder rowData = new StringBuilder(); - rowData.append("["); - - boolean first = true; - for (String fieldName : fieldNames) { - if (!first) rowData.append(","); - Object value = columns.get(fieldName); - if (value instanceof String) { - rowData.append("\"").append(value).append("\""); - } else { - rowData.append(value); - } - first = false; - } - rowData.append("]\n"); - - // 检查是否超过限制 - if (currentTableChunk.length() + rowData.length() > maxSize) { - if (currentTableChunk.length() > 0) { - chunks.add(currentTableChunk.toString()); - currentTableChunk = new StringBuilder(); - } - } - currentTableChunk.append(rowData); - } - - if (currentTableChunk.length() > 0) { - chunks.add(currentTableChunk.toString()); - } - - return chunks; - } - - /** - * 获取分块数据的方法(可供其他类调用) - */ - public static String[] getDataChunks() { - // 这里可以将main方法中的逻辑提取出来,返回分块数据 - // 为了简化,这里只是示例 - return new String[]{"示例数据块1", "示例数据块2"}; + return dataChunks.toArray(new String[0]); } } diff --git a/src/main/java/com/dsideal/base/Util/CallDeepSeek.java b/src/main/java/com/dsideal/base/Util/CallDeepSeek.java index fc610ead..fc241213 100644 --- a/src/main/java/com/dsideal/base/Util/CallDeepSeek.java +++ b/src/main/java/com/dsideal/base/Util/CallDeepSeek.java @@ -200,18 +200,23 @@ public class CallDeepSeek { /** * 同步调用DeepSeek API(非流式响应) - * * @param prompt 用户提示词 - * @return DeepSeek的完整响应内容 + * @return 完整的响应内容 */ public static String callDeepSeek(String prompt) { try { + System.out.println("开始同步调用DeepSeek API..."); JSONObject jsonPayload = createRequestPayload(prompt); // 设置为非流式响应 - jsonPayload.set("stream", false); + jsonPayload.put("stream", false); HttpClient client = createHttpClient(); - java.net.http.HttpRequest request = createHttpRequest(jsonPayload); + java.net.http.HttpRequest request = java.net.http.HttpRequest.newBuilder() + .uri(URI.create(API_URL)) + .header("Content-Type", "application/json") + .header("Authorization", "Bearer " + API_KEY) + .POST(java.net.http.HttpRequest.BodyPublishers.ofString(jsonPayload.toString(), StandardCharsets.UTF_8)) + .build(); HttpResponse response = client.send(request, HttpResponse.BodyHandlers.ofString()); @@ -224,15 +229,16 @@ public class CallDeepSeek { .getStr("content", ""); } } else { - System.err.println("DeepSeek API调用失败: " + response.statusCode() + " Body: " + response.body()); - return null; + System.err.println("API请求失败,状态码: " + response.statusCode()); + System.err.println("响应内容: " + response.body()); + return "API调用失败: " + response.statusCode(); } } catch (Exception e) { - System.err.println("调用DeepSeek API时出错: " + e.getMessage()); + System.err.println("调用DeepSeek API时发生错误: " + e.getMessage()); e.printStackTrace(); - return null; + return "调用失败: " + e.getMessage(); } - return null; + return ""; } }