fix: enhance suggestions in exclusions.json for clarity and accuracy; update filterFalsePositivesWithAI to accept exclusions

This commit is contained in:
2026-05-12 04:20:35 +00:00
parent 24ae565e38
commit 5ef9ab81ff
3 changed files with 21 additions and 18 deletions
+5 -9
View File
@@ -6,27 +6,23 @@
}, },
{ {
"location": "app/git.js", "location": "app/git.js",
"suggestion": "GITEA_TOKEN 直接嵌入 URL 中" "suggestion": "GITEA_TOKEN 直接嵌入 URL 中,建議改以環境變數或 Gitea Secrets 注入"
}, },
{ {
"role": "Rex", "role": "Rex",
"location": "README.md", "location": "README.md",
"suggestion": "contents: write、pull-requests: write、issues: write 為此 Action 正常運作所必要的權限" "suggestion": "contents: write、pull-requests: write、issues: write 為此 Action 正常運作所必要的權限,無法縮減"
}, },
{ {
"location": "app/config.js", "location": "app/config.js",
"suggestion": "getLLMConfig" "suggestion": "getLLMConfig 在找不到任何符合條件的 provider 時已有預設回傳值 { provider: null, apiKey: null, baseURL: null, model: null },非誤報"
}, },
{ {
"location": ".gitea/ai-review/exclusions.json", "location": ".gitea/ai-review/exclusions.json",
"suggestion": "GITEA_TOKEN" "suggestion": "exclusions.json 是排除規則檔,內容為問題描述字串,不是實際程式碼或 token,role 欄位為有效欄位"
},
{
"location": ".gitea/ai-review/exclusions.json",
"suggestion": "role 欄位"
}, },
{ {
"location": "app/findings.js", "location": "app/findings.js",
"suggestion": "filterFalsePositivesWithAI" "suggestion": "filterFalsePositivesWithAI 拋出的 Error 會被 catch 攔截並降級回傳原始 findings,不會中斷流程"
} }
] ]
+15 -8
View File
@@ -117,33 +117,40 @@ export function loadExclusions(workspace) {
/** /**
* 套用排除規則,過濾掉符合排除條件的 findings * 套用排除規則,過濾掉符合排除條件的 findings
* 排除條件:role/location/suggestion 皆符合(省略的欄位視為萬用 * location 只比對檔案路徑(忽略行數),suggestion 省略時視為萬用
*/ */
export function applyExclusions(findings, exclusions) { export function applyExclusions(findings, exclusions) {
if (exclusions.length === 0) return findings; if (exclusions.length === 0) return findings;
const before = findings.length; const before = findings.length;
const filtered = findings.filter(f => !exclusions.some(ex => const filtered = findings.filter(f => !exclusions.some(ex => {
(!ex.role || ex.role === f.role) && const fPath = String(f.location).split(':')[0];
(!ex.location || String(f.location).includes(ex.location)) && const exPath = ex.location ? String(ex.location).split(':')[0] : null;
(!ex.suggestion || String(f.suggestion).includes(String(ex.suggestion).slice(0, 20))) return (!exPath || fPath === exPath) &&
)); (!ex.role || ex.role === f.role);
}));
console.log(` 排除過濾: ${before} -> ${filtered.length} 筆(排除 ${before - filtered.length} 筆)`); console.log(` 排除過濾: ${before} -> ${filtered.length} 筆(排除 ${before - filtered.length} 筆)`);
return filtered; return filtered;
} }
/** /**
* 呼叫 AI 判斷哪些問題是誤報或不需處理,回傳需保留的 findings * 呼叫 AI 判斷哪些問題是誤報或不需處理,回傳需保留的 findings
* exclusions 為已知誤報清單,供 AI 參考判斷
* 失敗時降級回傳原始 findings * 失敗時降級回傳原始 findings
*/ */
export async function filterFalsePositivesWithAI(findings) { export async function filterFalsePositivesWithAI(findings, exclusions = []) {
if (findings.length === 0) return findings; if (findings.length === 0) return findings;
const exclusionHint = exclusions.length > 0
? `\n\n以下是已知的誤報或不需處理的問題清單(供參考,相同檔案路徑且語意相近的問題應一併排除):\n${JSON.stringify(exclusions, null, 2)}`
: '';
const systemPrompt = `你是一位資深程式碼審查專家,負責判斷審查問題是否為誤報或不需處理。 const systemPrompt = `你是一位資深程式碼審查專家,負責判斷審查問題是否為誤報或不需處理。
給你一份問題清單(JSON 陣列),每筆包含 level、role、location、suggestion。 給你一份問題清單(JSON 陣列),每筆包含 level、role、location、suggestion。
請移除以下類型的問題: 請移除以下類型的問題:
1. 誤報:問題描述與實際程式碼不符(例如:程式碼已正確使用環境變數或 secrets,卻被標記為硬編碼敏感資料) 1. 誤報:問題描述與實際程式碼不符(例如:程式碼已正確使用環境變數或 secrets,卻被標記為硬編碼敏感資料)
2. 不適用:問題在此專案情境下不需處理(例如:CI/CD action 本來就需要透過環境變數傳遞 token) 2. 不適用:問題在此專案情境下不需處理(例如:CI/CD action 本來就需要透過環境變數傳遞 token)
只回傳需要保留的問題 JSON 陣列,不要有其他文字。`; 3. 與已知誤報清單語意相近的問題(檔案路徑相同且建議內容相似)
只回傳需要保留的問題 JSON 陣列,不要有其他文字。${exclusionHint}`;
const userContent = `請判斷以下問題清單,移除誤報或不需處理的問題:\n\n${JSON.stringify(findings, null, 2)}`; const userContent = `請判斷以下問題清單,移除誤報或不需處理的問題:\n\n${JSON.stringify(findings, null, 2)}`;
+1 -1
View File
@@ -85,7 +85,7 @@ async function main() {
console.log('\n🚫 Step4: AI 排除問題過濾'); console.log('\n🚫 Step4: AI 排除問題過濾');
const exclusions = loadExclusions(repoDir || WORKSPACE); const exclusions = loadExclusions(repoDir || WORKSPACE);
const ruleFiltered = applyExclusions(sorted, exclusions); const ruleFiltered = applyExclusions(sorted, exclusions);
const filtered = await filterFalsePositivesWithAI(ruleFiltered); const filtered = await filterFalsePositivesWithAI(ruleFiltered, exclusions);
console.log(` Step4 完成: findings total=${filtered.length}`); console.log(` Step4 完成: findings total=${filtered.length}`);
// Step5: 寫入 findings.json,依序發布 comment // Step5: 寫入 findings.json,依序發布 comment