diff --git a/backend/pr/src/main/java/com/prassistant/pr/aggregation/GlobalPromptBuilder.java b/backend/pr/src/main/java/com/prassistant/pr/aggregation/GlobalPromptBuilder.java index 73a9224..96fe3ca 100644 --- a/backend/pr/src/main/java/com/prassistant/pr/aggregation/GlobalPromptBuilder.java +++ b/backend/pr/src/main/java/com/prassistant/pr/aggregation/GlobalPromptBuilder.java @@ -17,9 +17,10 @@ public final class GlobalPromptBuilder { static final String SYSTEM_ROLE = """ 你是一位资深的代码评审专家。请基于以下 Pull Request 的各文件分析摘要和跨文件线索, 进行全局推理分析。不要重复描述各文件摘要,而是聚焦于: - 1. 跨文件交互可能引入的风险 + 1. 跨文件交互可能引入的风险(接口变更、数据流影响) 2. 架构层面的影响(模块耦合、接口兼容性等) - 3. 整体代码质量评估与改进建议 + 3. 实现与测试的对应性:测试是否覆盖了关键路径和边界条件 + 4. 整体代码质量评估与改进建议 """; /** 推理要求 */ @@ -29,6 +30,8 @@ public final class GlobalPromptBuilder { 2. 如果无法确定某个风险,标注为 "需进一步确认" 3. crossFileIssues 必须涉及至少两个文件 4. topPriorityFiles 不超过 3 个 + 5. 注意检查实现文件与测试文件的对应:有实现变更但无测试补充时,应标注测试缺失风险 + 6. architectureSuggestions 必须针对本次 PR 已变更的具体代码给出改进,每条建议应关联到具体文件路径,禁止建议"引入第三方库全面替换"等超出本次变更范围的重构 """; /** 全局输出格式要求 */ @@ -40,7 +43,7 @@ public final class GlobalPromptBuilder { "globalRiskReason": "全局风险评级理由", "crossFileIssues": [ { - "issueType": "INTERFACE_MISMATCH|DUPLICATE_LOGIC|TRANSACTION_MISSING|SECURITY_PROPAGATION|DB_CODE_INCONSISTENCY|OTHER", + "issueType": "INTERFACE_MISMATCH|INTERFACE_INCONSISTENCY|DUPLICATE_LOGIC|REPEAT_LOGIC|TRANSACTION_MISSING|SECURITY_PROPAGATION|SECURITY_VULNERABILITY|DB_CODE_INCONSISTENCY|NUMERICAL_ACCURACY|ALGORITHM_CHOICE|PERFORMANCE|OTHER", "description": "问题描述", "involvedFiles": ["文件路径1", "文件路径2"], "severity": "HIGH|MEDIUM|LOW", diff --git a/backend/pr/src/main/java/com/prassistant/pr/aggregation/model/GlobalReviewReport.java b/backend/pr/src/main/java/com/prassistant/pr/aggregation/model/GlobalReviewReport.java index b89240e..25901b7 100644 --- a/backend/pr/src/main/java/com/prassistant/pr/aggregation/model/GlobalReviewReport.java +++ b/backend/pr/src/main/java/com/prassistant/pr/aggregation/model/GlobalReviewReport.java @@ -63,13 +63,19 @@ public enum RiskLevel { HIGH, MEDIUM, LOW } - /** 跨文件关联问题类型枚举 */ + /** 跨文件关联问题类型枚举(与提示词 issueType 枚举一致) */ public enum IssueType { INTERFACE_MISMATCH, + INTERFACE_INCONSISTENCY, DUPLICATE_LOGIC, + REPEAT_LOGIC, TRANSACTION_MISSING, SECURITY_PROPAGATION, + SECURITY_VULNERABILITY, DB_CODE_INCONSISTENCY, + NUMERICAL_ACCURACY, + ALGORITHM_CHOICE, + PERFORMANCE, OTHER } diff --git a/backend/pr/src/main/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilder.java b/backend/pr/src/main/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilder.java index 297354f..00c2c0b 100644 --- a/backend/pr/src/main/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilder.java +++ b/backend/pr/src/main/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilder.java @@ -16,7 +16,11 @@ public final class ChunkPromptBuilder { /** 系统角色设定 */ static final String SYSTEM_ROLE = """ - 你是一位资深的代码评审专家。专注于以下方面: + 你是资深数值计算与代码审查专家。审查任何涉及矩阵运算、浮点运算、迭代算法的代码时,必须强制检查以下 3 项,并在输出中明确标注是否通过: + 1. 【数值稳定性】若涉及正交化、QR分解、最小二乘等算法,必须评估经典实现(如经典 Gram-Schmidt)的累积舍入误差风险,并对比 Modified GS 或 Householder 变换的适用性。 + 2. 【维度边界】若涉及矩阵运算,必须检查是否对 m < n(列数大于行数)、零矩阵、秩亏矩阵、非方阵做了前置验证或异常处理。 + 3. 【浮点比较】禁止直接使用 == 0 或 == 1.0 判断浮点/双精度数值,必须建议引入 epsilon 容差。 + 同时兼顾代码的通用审查维度: - 正确性:逻辑错误、边界条件、异常处理 - 安全性:注入风险、权限校验、数据加密 - 性能:N+1查询、资源泄漏、缓存机会 @@ -28,21 +32,29 @@ public final class ChunkPromptBuilder { static final String OUTPUT_FORMAT = """ 请严格按以下 JSON 格式输出(仅 JSON,不要包含 Markdown 代码块标记或任何额外文字): { - "summary": "变更摘要(不超过30字)", + "summary": "【变更本质】,影响【影响范围】(严格按此模板,30字内)", "risks": [ - { "type": "NullPointer|Concurrency|Security|Performance|Maintainability|Other", "line": 行号, "description": "风险说明(不超过100字)" } + { "type": "NullPointer|Concurrency|Security|Performance|Arithmetic|Maintainability|Other", "line": 行号, "description": "风险说明(不超过100字)" } ], "suggestions": [ { "priority": 1-5, "description": "建议内容(不超过100字)" } ], "riskLevel": "HIGH|MEDIUM|LOW" } + + 【风险评级硬性规则】(必须遵守,不得自由发挥) + - HIGH:存在内存安全、并发安全、数据丢失、算法数值崩溃、死循环风险 + - MEDIUM:存在算术精度损失、边界条件缺失(如 m 影响范围 -> 后续需关注的文件\n"; + "6. 跨块依赖提示:如果当前块修改了接口签名、删除了方法、新增了全局变量或修改了配置项," + + "请详细描述这些变更,格式:变更内容 -> 影响范围 -> 后续需关注的文件\n" + + "7. 实现与测试关联提示:如果当前块是实现代码,检查是否有对应的测试文件变更;" + + "如果是测试代码,说明覆盖了哪些实现路径\n"; /** 反幻觉约束 */ static final String ANTI_HALLUCINATION = @@ -75,7 +87,8 @@ public static String buildWholeFile(SanitizedDiff diff) { + "1. 变更摘要(30字内)\n" + "2. 风险点(标注类型:NullPointer/Concurrency/Security/Performance 等)\n" + "3. Review 建议\n" - + "4. 风险评级(HIGH / MEDIUM / LOW)\n\n" + + "4. 风险评级(HIGH / MEDIUM / LOW)\n" + + "5. 矩阵维度边界检查:若代码涉及矩阵初始化、分解、求逆、乘法,检查是否对 m < n(列数大于行数)的情况做了前置校验或异常抛出。若未处理,必须作为 MEDIUM 风险上报\n\n" + ANTI_HALLUCINATION + "\n" + OUTPUT_FORMAT; } @@ -105,6 +118,7 @@ public static String buildChunk(Chunk chunk) { sb.append("2. 风险点(标注类型:NullPointer/Concurrency/Security/Performance 等)\n"); sb.append("3. Review 建议\n"); sb.append("4. 风险评级(HIGH / MEDIUM / LOW)\n"); + sb.append("5. 矩阵维度边界检查:若代码涉及矩阵初始化、分解、求逆、乘法,检查是否对 m < n(列数大于行数)的情况做了前置校验或异常抛出。若未处理,必须作为 MEDIUM 风险上报\n"); sb.append(CHUNK_CROSS_CHUNK_HINT).append("\n"); sb.append(ANTI_HALLUCINATION).append("\n"); sb.append(OUTPUT_FORMAT); diff --git a/backend/pr/src/test/java/com/prassistant/pr/aggregation/GlobalPromptBuilderTest.java b/backend/pr/src/test/java/com/prassistant/pr/aggregation/GlobalPromptBuilderTest.java index e98a001..6bd11be 100644 --- a/backend/pr/src/test/java/com/prassistant/pr/aggregation/GlobalPromptBuilderTest.java +++ b/backend/pr/src/test/java/com/prassistant/pr/aggregation/GlobalPromptBuilderTest.java @@ -49,6 +49,7 @@ void shouldContainSystemRole() { assertTrue(prompt.contains("资深的代码评审专家")); assertTrue(prompt.contains("全局推理分析")); assertTrue(prompt.contains("架构层面的影响")); + assertTrue(prompt.contains("实现与测试的对应性")); } @Test @@ -111,6 +112,8 @@ void shouldContainReasoningRules() { assertTrue(prompt.contains("推理要求")); assertTrue(prompt.contains("至少两个文件")); assertTrue(prompt.contains("不超过 3 个")); + assertTrue(prompt.contains("测试缺失风险")); + assertTrue(prompt.contains("architectureSuggestions 必须针对本次 PR")); } @Test @@ -124,6 +127,10 @@ void shouldContainOutputFormat() { assertTrue(prompt.contains("crossFileIssues")); assertTrue(prompt.contains("architectureSuggestions")); assertTrue(prompt.contains("topPriorityFiles")); + assertTrue(prompt.contains("NUMERICAL_ACCURACY")); + assertTrue(prompt.contains("ALGORITHM_CHOICE")); + assertTrue(prompt.contains("INTERFACE_MISMATCH")); + assertTrue(prompt.contains("DUPLICATE_LOGIC")); } } diff --git a/backend/pr/src/test/java/com/prassistant/pr/aggregation/model/GlobalReviewReportTest.java b/backend/pr/src/test/java/com/prassistant/pr/aggregation/model/GlobalReviewReportTest.java index 9f5a015..947967e 100644 --- a/backend/pr/src/test/java/com/prassistant/pr/aggregation/model/GlobalReviewReportTest.java +++ b/backend/pr/src/test/java/com/prassistant/pr/aggregation/model/GlobalReviewReportTest.java @@ -98,14 +98,20 @@ void shouldHaveThreeValues() { class IssueTypeEnum { @Test - @DisplayName("应包含六种问题类型") - void shouldHaveSixValues() { - assertEquals(6, GlobalReviewReport.IssueType.values().length); + @DisplayName("应包含 12 种问题类型(含新增的数值精度、算法选型、性能等)") + void shouldHaveTwelveValues() { + assertEquals(12, GlobalReviewReport.IssueType.values().length); assertNotNull(GlobalReviewReport.IssueType.valueOf("INTERFACE_MISMATCH")); + assertNotNull(GlobalReviewReport.IssueType.valueOf("INTERFACE_INCONSISTENCY")); assertNotNull(GlobalReviewReport.IssueType.valueOf("DUPLICATE_LOGIC")); + assertNotNull(GlobalReviewReport.IssueType.valueOf("REPEAT_LOGIC")); assertNotNull(GlobalReviewReport.IssueType.valueOf("TRANSACTION_MISSING")); assertNotNull(GlobalReviewReport.IssueType.valueOf("SECURITY_PROPAGATION")); + assertNotNull(GlobalReviewReport.IssueType.valueOf("SECURITY_VULNERABILITY")); assertNotNull(GlobalReviewReport.IssueType.valueOf("DB_CODE_INCONSISTENCY")); + assertNotNull(GlobalReviewReport.IssueType.valueOf("NUMERICAL_ACCURACY")); + assertNotNull(GlobalReviewReport.IssueType.valueOf("ALGORITHM_CHOICE")); + assertNotNull(GlobalReviewReport.IssueType.valueOf("PERFORMANCE")); assertNotNull(GlobalReviewReport.IssueType.valueOf("OTHER")); } } diff --git a/backend/pr/src/test/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilderTest.java b/backend/pr/src/test/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilderTest.java index fd4e95b..bd5ee2c 100644 --- a/backend/pr/src/test/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilderTest.java +++ b/backend/pr/src/test/java/com/prassistant/pr/review/analyzer/ChunkPromptBuilderTest.java @@ -153,7 +153,7 @@ void shouldNotIncludePreviousSummaryWhenAbsent() { } @Test - @DisplayName("应包含跨块依赖提示(第 5 项)") + @DisplayName("应包含跨块依赖提示(第 6 项)") void shouldIncludeCrossChunkHint() { Chunk chunk = Chunk.builder() .chunkId("test#hunk-1") @@ -165,7 +165,7 @@ void shouldIncludeCrossChunkHint() { String prompt = ChunkPromptBuilder.buildChunk(chunk); assertTrue(prompt.contains("跨块依赖提示")); - assertTrue(prompt.contains("5.")); + assertTrue(prompt.contains("6.")); } @Test @@ -222,6 +222,8 @@ void wholeFileExcludesCrossChunkHint() { assertFalse(wholeFilePrompt.contains("跨块依赖提示")); assertTrue(chunkPrompt.contains("跨块依赖提示")); + assertTrue(chunkPrompt.contains("5. 矩阵维度边界检查")); + assertTrue(chunkPrompt.contains("6. 跨块依赖提示")); } @Test