perf(LLM 併發): 角色分析與誤報/補行號裁決改為並行 sub-agent(預設不限併發)
This commit is contained in:
+19
-17
@@ -1,6 +1,6 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { chatJSON } from './llm.js';
|
||||
import { chatJSON, mapWithConcurrency, LLM_CONCURRENCY } from './llm.js';
|
||||
import { buildAnalysisPrompt, loadRole, buildVerdictPrompt, buildLocateLinePrompt } from './roles.js';
|
||||
import { FINDINGS_PATH, EXCLUSIONS_PATH } from './config.js';
|
||||
import { line, ok, warn } from './log.js';
|
||||
@@ -368,14 +368,14 @@ function extractFileDiff(diff, file) {
|
||||
* 成功則把 location 補成 `檔案:行號`,否則保留原檔名。
|
||||
*/
|
||||
export async function resolveMissingLineNumbers(findings, diff, deps = {}) {
|
||||
const { chatFn = chatJSON, getRole = loadRole, maxAttempts = MAX_LOCATE_ATTEMPTS } = deps;
|
||||
let resolved = 0;
|
||||
let pending = 0;
|
||||
for (const f of findings) {
|
||||
if (findingLine(f.location) != null) continue; // 已有行號
|
||||
const { chatFn = chatJSON, getRole = loadRole, maxAttempts = MAX_LOCATE_ATTEMPTS, concurrency = LLM_CONCURRENCY } = deps;
|
||||
// 只挑「缺行號且有檔名」的 finding;各自以獨立 LLM 子行程並行定位(併發上限見 concurrency)。
|
||||
const pending = findings.filter(f => findingLine(f.location) == null
|
||||
&& String(f.location || '').split(',')[0].split(':')[0].trim());
|
||||
if (pending.length === 0) return findings;
|
||||
|
||||
const outcomes = await mapWithConcurrency(pending, concurrency, async (f) => {
|
||||
const file = String(f.location || '').split(',')[0].split(':')[0].trim();
|
||||
if (!file) continue;
|
||||
pending += 1;
|
||||
const systemPrompt = buildLocateLinePrompt(getRole(f.role) || { name: f.role });
|
||||
const userContent = `${JSON.stringify({ file, problem: f.problem, suggestion: f.suggestion })}\n\n--- ${file} Git Diff ---\n${extractFileDiff(diff, file)}`;
|
||||
let located = null;
|
||||
@@ -390,12 +390,13 @@ export async function resolveMissingLineNumbers(findings, diff, deps = {}) {
|
||||
}
|
||||
if (located != null) {
|
||||
f.location = `${file}:${located}`;
|
||||
resolved += 1;
|
||||
} else {
|
||||
warn(`[${f.role}] ${maxAttempts} 次嘗試後仍無法定位行號,保留檔名: ${file}`);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (pending > 0) ok(`補行號: ${resolved}/${pending} 筆成功定位`);
|
||||
warn(`[${f.role}] ${maxAttempts} 次嘗試後仍無法定位行號,保留檔名: ${file}`);
|
||||
return false;
|
||||
});
|
||||
|
||||
ok(`補行號: ${outcomes.filter(Boolean).length}/${pending.length} 筆成功定位`);
|
||||
return findings;
|
||||
}
|
||||
|
||||
@@ -576,10 +577,11 @@ export async function filterFalsePositivesWithAI(findings, exclusions = [], chat
|
||||
? `${exclusionContext.prompt}\n規則:若此 finding 與上述任何一類的路徑、角色或描述高度相似,優先視為誤報或不適用。`
|
||||
: '';
|
||||
|
||||
// 每條 finding 各派一個防守方 sub-agent 裁決,多條時平行處理
|
||||
const verdicts = await Promise.all(
|
||||
findings.map(f => judgeFindingIsFalsePositive(f, defender, exclusionHint, chatFn).then(isFP => ({ f, isFP }))),
|
||||
);
|
||||
// 每條 finding 各派一個防守方 sub-agent 裁決;併發上限與其他 LLM 任務共用 LLM_CONCURRENCY(預設不限制)。
|
||||
const verdicts = await mapWithConcurrency(findings, LLM_CONCURRENCY, async (f) => ({
|
||||
f,
|
||||
isFP: await judgeFindingIsFalsePositive(f, defender, exclusionHint, chatFn),
|
||||
}));
|
||||
const kept = verdicts.filter(v => !v.isFP).map(v => v.f);
|
||||
ok(`AI 誤報過濾(防守方${findings.length > 1 ? '平行' : ''}裁決): ${findings.length} -> ${kept.length} 筆`);
|
||||
return kept;
|
||||
|
||||
Reference in New Issue
Block a user