feat: Resilience Hardening & Boundary Stress Validation (v2.77.3)

This commit is contained in:
g1nation
2026-05-05 17:17:00 +09:00
parent cf10d14148
commit c2f17cfb03
11 changed files with 308 additions and 19 deletions
+2 -2
View File
@@ -4,7 +4,7 @@ export class AgentDataValidator {
/**
* 에이전트 간 핸드오프(Handoff) 시 데이터 무결성을 검증하고 품질 점수를 반환합니다.
*/
public static validateHandoff(stage: string, data: string): number {
public static validateHandoff(stage: string, data: string): { score: number, conflictRisk: number } {
if (!data || data.trim().length === 0) {
throw new Error(`[IntegrityError] 데이터 누락: ${stage} 에이전트의 출력이 비어 있습니다.`);
}
@@ -46,7 +46,7 @@ export class AgentDataValidator {
logInfo(`[ConflictAlert] ${stage} 단계에서 지식 충돌 위험 감지 (Risk: ${conflictRisk}).`);
}
return score - (conflictRisk * 0.5); // 충돌 위험이 높으면 전체 품질 점수를 감쇄함
return { score, conflictRisk };
}
}
+29 -14
View File
@@ -120,8 +120,7 @@ export class MissionState {
*/
private saveToDisk(): void {
try {
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
if (!workspacePath) return;
const workspacePath = process.env.ASTRA_TEST_ROOT || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || process.cwd();
const astraDir = path.join(workspacePath, '.astra', 'missions');
if (!fs.existsSync(astraDir)) {
@@ -156,8 +155,7 @@ export class MissionState {
*/
public static loadFromDisk(missionId: string): MissionState | null {
try {
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
if (!workspacePath) return null;
const workspacePath = process.env.ASTRA_TEST_ROOT || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || process.cwd();
const filePath = path.join(workspacePath, '.astra', 'missions', `${missionId}.json`);
if (!fs.existsSync(filePath)) return null;
@@ -165,6 +163,12 @@ export class MissionState {
const state = new MissionState(missionId);
state._stage = data.status;
state._results = data.results || {};
state.resilienceMetrics = data.resilienceMetrics || {
fallbacks: 0,
retries: 0,
maxConflictScore: 0,
deduplications: 0
};
return state;
} catch (err) {
return null;
@@ -221,7 +225,8 @@ export class MissionState {
durationMs: e.durationFromPrev,
message: e.message,
ts: new Date(e.timestamp).toISOString()
}))
})),
resilienceMetrics: this.resilienceMetrics
};
}
}
@@ -378,7 +383,7 @@ export class ErrorClassifier {
*/
export class CacheManager {
private static getCacheDir(): string {
const workspacePath = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || '';
const workspacePath = process.env.ASTRA_TEST_ROOT || vscode.workspace.workspaceFolders?.[0]?.uri.fsPath || process.cwd();
const cacheDir = path.join(workspacePath, '.astra', 'cache');
if (!fs.existsSync(cacheDir)) {
fs.mkdirSync(cacheDir, { recursive: true });
@@ -444,7 +449,8 @@ export class AgentEngine {
prompt: string,
brainContext: string,
signal: AbortSignal,
onProgress: (stage: PipelineStage, message: string) => void
onProgress: (stage: PipelineStage, message: string) => void,
options?: AgentExecuteOptions
): Promise<string> {
let state: MissionState;
@@ -470,7 +476,10 @@ export class AgentEngine {
// --- Phase 1: Planner ---
const plan = await this.executeStep(
state, 'planner', '전략 수립 중...',
() => this.resilientExecute(state, this.planner, 'Planner', prompt, brainContext, signal, onProgress, { context: brainContext, signal, config: { role: 'planner' } }),
() => this.resilientExecute(state, this.planner, 'Planner', prompt, brainContext, signal, onProgress, {
...options,
context: brainContext, signal, config: { ...options?.config, role: 'planner' }
}),
prompt, brainContext, signal, onProgress
);
@@ -480,7 +489,11 @@ export class AgentEngine {
// --- Phase 2: Researcher ---
const research = await this.executeStep(
state, 'researcher', '핵심 정보 수집 및 분석 중...',
() => this.resilientExecute(state, this.researcher, 'Researcher', plan, brainContext, signal, onProgress, { context: brainContext, signal, config: { role: 'researcher' }, abstractionLevel: researcherLevel }),
() => this.resilientExecute(state, this.researcher, 'Researcher', plan, brainContext, signal, onProgress, {
...options,
context: brainContext, signal, config: { ...options?.config, role: 'researcher' },
abstractionLevel: researcherLevel
}),
plan, brainContext, signal, onProgress
);
@@ -498,8 +511,9 @@ export class AgentEngine {
const finalReport = await this.executeStep(
state, 'writer', '최종 리포트 작성 및 편집 중...',
() => this.resilientExecute(state, this.writer, 'Writer', research, prompt, signal, onProgress, {
context: brainContext, signal, config: { role: 'writer', allowFallback: true },
priorResults: { plan, writerPrep, previousValidData: state.getResult('finalReport') },
...options,
context: brainContext, signal, config: { role: 'writer', allowFallback: true, ...options?.config },
priorResults: { plan, writerPrep, previousValidData: state.getResult('finalReport'), ...options?.priorResults },
abstractionLevel: writerLevel
}),
research, prompt, signal, onProgress
@@ -631,8 +645,8 @@ export class AgentEngine {
const durationMs = Date.now() - startTime;
// [Reliability Check] 충돌 위험도 추적
const conflictScore = AgentDataValidator.validateHandoff(agentName, result);
state.resilienceMetrics.maxConflictScore = Math.max(state.resilienceMetrics.maxConflictScore, conflictScore);
const validation = AgentDataValidator.validateHandoff(agentName, result);
state.resilienceMetrics.maxConflictScore = Math.max(state.resilienceMetrics.maxConflictScore, validation.conflictRisk);
PerformanceProfiler.logLLMLatency(agentName, durationMs, result.length);
@@ -729,7 +743,8 @@ export class AgentEngine {
private validateResult(data: string, step: string): number {
// Error Recovery Matrix: Permanent 오류 발생을 방지하기 위한 선제적 핸드오프 검증
return AgentDataValidator.validateHandoff(step, data);
const validation = AgentDataValidator.validateHandoff(step, data);
return validation.score;
}
/**