feat(formatter): enforce implementation code snippet section in wiki artifacts
This commit is contained in:
+30
-38
@@ -416,8 +416,6 @@ export class CacheManager {
|
||||
* - Error Recovery Matrix 기반의 Transient/Permanent 오류 자동 분류 및 복구
|
||||
*/
|
||||
export class AgentEngine {
|
||||
private state: MissionState | null = null;
|
||||
|
||||
constructor(
|
||||
private readonly planner: IAgent,
|
||||
private readonly researcher: IAgent,
|
||||
@@ -435,14 +433,15 @@ export class AgentEngine {
|
||||
signal: AbortSignal,
|
||||
onProgress: (stage: PipelineStage, message: string) => void
|
||||
): Promise<string> {
|
||||
|
||||
let state: MissionState;
|
||||
|
||||
// 0. 상태 복원 시도 (Resumption)
|
||||
const existingState = MissionState.loadFromDisk(missionId);
|
||||
if (existingState && existingState.stage !== 'completed') {
|
||||
logInfo(`[AgentEngine] 기존 미션 발견. '${existingState.stage}' 단계부터 재개합니다.`);
|
||||
this.state = existingState;
|
||||
state = existingState;
|
||||
} else {
|
||||
this.state = new MissionState(missionId);
|
||||
state = new MissionState(missionId);
|
||||
}
|
||||
|
||||
// 1. 명시적 락 획득 (Mutex) - 동일 미션의 중복 실행 방지
|
||||
@@ -454,9 +453,9 @@ export class AgentEngine {
|
||||
logInfo(`[AgentEngine] 미션 시작: ${missionId}`);
|
||||
|
||||
// --- Phase 1: Planner ---
|
||||
let plan = this.state!.getResult('plan');
|
||||
let plan = state.getResult('plan');
|
||||
if (!plan) {
|
||||
this.transition('planner', '전략 수립 중...', onProgress);
|
||||
this.transition(state, 'planner', '전략 수립 중...', onProgress);
|
||||
this.checkAbort(signal);
|
||||
|
||||
// Deduplication: 동일 프롬프트 캐시 확인
|
||||
@@ -466,26 +465,26 @@ export class AgentEngine {
|
||||
plan = cachedPlan;
|
||||
} else {
|
||||
plan = await this.resilientExecute(
|
||||
this.planner, 'Planner', prompt, brainContext, signal, onProgress,
|
||||
state, this.planner, 'Planner', prompt, brainContext, signal, onProgress,
|
||||
{ context: brainContext, signal, config: { role: 'planner' } }
|
||||
);
|
||||
CacheManager.set(prompt, brainContext, plan);
|
||||
}
|
||||
this.state!.setResult('plan', plan);
|
||||
state.setResult('plan', plan);
|
||||
}
|
||||
this.validateResult(plan, 'Planner');
|
||||
|
||||
// --- Phase 2 & 3: Researcher ---
|
||||
let research = this.state!.getResult('research');
|
||||
let writerPrep = this.state!.getResult('writerPrep');
|
||||
let research = state.getResult('research');
|
||||
let writerPrep = state.getResult('writerPrep');
|
||||
|
||||
if (!research || !writerPrep) {
|
||||
this.transition('researcher', '핵심 정보 수집 및 분석 중...', onProgress);
|
||||
this.transition(state, 'researcher', '핵심 정보 수집 및 분석 중...', onProgress);
|
||||
this.checkAbort(signal);
|
||||
|
||||
const [res, prep] = await Promise.all([
|
||||
research || this.resilientExecute(
|
||||
this.researcher, 'Researcher', plan, brainContext, signal, onProgress,
|
||||
state, this.researcher, 'Researcher', plan, brainContext, signal, onProgress,
|
||||
{ context: brainContext, signal, config: { role: 'researcher' } }
|
||||
),
|
||||
writerPrep || this.prepareWriterContext(prompt, plan, brainContext)
|
||||
@@ -493,16 +492,16 @@ export class AgentEngine {
|
||||
|
||||
research = res;
|
||||
writerPrep = prep;
|
||||
this.state!.setResult('research', research);
|
||||
this.state!.setResult('writerPrep', writerPrep);
|
||||
state.setResult('research', research);
|
||||
state.setResult('writerPrep', writerPrep);
|
||||
}
|
||||
this.validateResult(research, 'Researcher');
|
||||
|
||||
// --- Phase 3: Writer ---
|
||||
this.transition('writer', '최종 리포트 작성 및 편집 중...', onProgress);
|
||||
this.transition(state, 'writer', '최종 리포트 작성 및 편집 중...', onProgress);
|
||||
this.checkAbort(signal);
|
||||
const finalReport = await this.resilientExecute(
|
||||
this.writer, 'Writer', research, prompt, signal, onProgress,
|
||||
state, this.writer, 'Writer', research, prompt, signal, onProgress,
|
||||
{ context: brainContext, signal, config: { role: 'writer' }, priorResults: { plan, writerPrep } }
|
||||
);
|
||||
this.validateResult(finalReport, 'Writer');
|
||||
@@ -510,22 +509,21 @@ export class AgentEngine {
|
||||
// 3. 지식 저장 포맷 표준화 (Standardization: Astra 피드백)
|
||||
const standardizedReport = WikiFormatter.format(finalReport, missionId);
|
||||
|
||||
this.transition('completed', '미션 완료', onProgress);
|
||||
logInfo(`[AgentEngine] 미션 완료: ${missionId} (총 ${this.state!.getElapsedMs()}ms)`);
|
||||
this.transition(state, 'completed', '미션 완료', onProgress);
|
||||
logInfo(`[AgentEngine] 미션 완료: ${missionId} (총 ${state.getElapsedMs()}ms)`);
|
||||
return standardizedReport;
|
||||
});
|
||||
|
||||
|
||||
|
||||
} catch (error: any) {
|
||||
const { type, rule } = ErrorClassifier.classify(error);
|
||||
const stageName = (this.state?.stage || 'unknown').toUpperCase();
|
||||
const stageName = (state!.stage || 'unknown').toUpperCase();
|
||||
|
||||
this.transition('error', `오류 발생: ${error.message}`, onProgress);
|
||||
this.transition(state!, 'error', `오류 발생: ${error.message}`, onProgress);
|
||||
|
||||
// 실패 원인 명시적 기록 (Astra 피드백: Record failure reason)
|
||||
if (this.state) {
|
||||
this.state.setFailureReason(`[${type}] ${rule.description} - 세부원인: ${error.message}`);
|
||||
}
|
||||
state!.setFailureReason(`[${type}] ${rule.description} - 세부원인: ${error.message}`);
|
||||
|
||||
// Error Recovery Matrix 기반 세분화된 로깅
|
||||
switch (type) {
|
||||
@@ -541,27 +539,22 @@ export class AgentEngine {
|
||||
}
|
||||
|
||||
// 감사 이력 덤프 (디버깅용)
|
||||
if (this.state) {
|
||||
logError(`[AgentEngine] Audit Trail for ${missionId}:\n${this.state.summarizeAudit()}`);
|
||||
}
|
||||
logError(`[AgentEngine] Audit Trail for ${missionId}:\n${state!.summarizeAudit()}`);
|
||||
throw error;
|
||||
} finally {
|
||||
// 3. 락 해제
|
||||
release();
|
||||
if (this.state) {
|
||||
this.state = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 현재 미션의 상태 객체를 외부에서 읽을 수 있도록 노출합니다.
|
||||
* 외부 모니터링 시스템 연동에 활용됩니다.
|
||||
* @deprecated 이제 미션 상태는 로컬 스코프에서 관리됩니다.
|
||||
*/
|
||||
public getMissionState(): MissionState | null {
|
||||
return this.state;
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
// ─── Resilience Layer ───
|
||||
|
||||
/**
|
||||
@@ -572,6 +565,7 @@ export class AgentEngine {
|
||||
* - Abort: 조용하게 예외를 전파 (Graceful Exit).
|
||||
*/
|
||||
private async resilientExecute(
|
||||
state: MissionState,
|
||||
agent: IAgent,
|
||||
agentName: string,
|
||||
input: string,
|
||||
@@ -589,7 +583,7 @@ export class AgentEngine {
|
||||
if (attempt > 0) {
|
||||
const backoffMs = transientRule.backoffBaseMs * Math.pow(2, attempt - 1);
|
||||
logInfo(`[AgentEngine] [RETRY] ${agentName} 재시도 ${attempt}/${transientRule.maxRetries} (${backoffMs}ms 후)`);
|
||||
onProgress(this.state?.stage || 'error', `${agentName} 재시도 중... (${attempt}/${transientRule.maxRetries})`);
|
||||
onProgress(state.stage, `${agentName} 재시도 중... (${attempt}/${transientRule.maxRetries})`);
|
||||
await new Promise(r => setTimeout(r, backoffMs));
|
||||
this.checkAbort(signal);
|
||||
}
|
||||
@@ -647,10 +641,8 @@ export class AgentEngine {
|
||||
/**
|
||||
* MissionState를 통한 상태 전환 + 외부 콜백 호출.
|
||||
*/
|
||||
private transition(stage: PipelineStage, message: string, onProgress: (stage: PipelineStage, message: string) => void) {
|
||||
if (this.state) {
|
||||
this.state.transition(stage, message);
|
||||
}
|
||||
private transition(state: MissionState, stage: PipelineStage, message: string, onProgress: (stage: PipelineStage, message: string) => void) {
|
||||
state.transition(stage, message);
|
||||
onProgress(stage, message);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user