feat(engine): implement self-reflection (reflector) stage in multi-agent pipeline

- Added ReflectorAgent for meta-cognition and critical review between Research and Writing
- Updated WriterAgent to explicitly address reflection critiques
- Introduced 'g1nation.enableReflection' configuration setting
- Added comprehensive integration tests for the self-reflection stage
- Documented design decisions in ADR-0010 and related discussion records
This commit is contained in:
g1nation
2026-05-14 01:47:28 +09:00
parent e075779635
commit 8da9532ca1
18 changed files with 610 additions and 124 deletions
+39 -8
View File
@@ -49,7 +49,7 @@ export interface IAgent {
/**
* 파이프라인 단계 상태 정의
*/
export type PipelineStage = 'idle' | 'planner' | 'researcher' | 'writer' | 'completed' | 'error';
export type PipelineStage = 'idle' | 'planner' | 'researcher' | 'reflector' | 'writer' | 'completed' | 'error';
/**
* 감사(Audit) 이력에 기록되는 단일 상태 전환 엔트리.
@@ -449,7 +449,9 @@ export class AgentEngine {
constructor(
private readonly planner: IAgent,
private readonly researcher: IAgent,
private readonly writer: IAgent
private readonly writer: IAgent,
// [Self-Reflection] Researcher와 Writer 사이에 주입되는 메타인지 노드. 미주입 시 기존 3단계 파이프라인을 그대로 유지.
private readonly reflector?: IAgent
) {}
/**
@@ -528,16 +530,45 @@ export class AgentEngine {
// [Structural Fix] 점수가 낮을수록 더 상세한 근거를 요구(comprehensive)하도록 로직 역전
const writerLevel: AbstractionLevel = researchScore < 65 ? 'comprehensive' : 'balanced';
// --- Phase 3.5: Reflector (Self-Reflection) ---
// Reflector가 주입되어 있고 옵션에서 명시적으로 끄지 않은 경우에만 실행한다.
// 실패해도 파이프라인을 막지 않는다(soft-fail): Reflector는 품질 보강이지 필수 게이트가 아님.
let reflection = '';
const reflectionDisabled = options?.config?.enableReflection === false;
if (this.reflector && !reflectionDisabled) {
try {
reflection = await this.executeStep(
state, 'reflector', '중간 산출물 자기검증 중...',
() => this.resilientExecute(state, this.reflector!, 'Reflector', research, brainContext, signal, onProgress, {
...options,
context: brainContext,
signal,
config: { ...options?.config, role: 'reflector', isSamePrompt: true },
priorResults: { plan, originalPrompt: prompt, ...options?.priorResults },
abstractionLevel: 'balanced'
}),
// [Cache namespace] Writer와 동일한 (research, prompt) 페어를 쓰면 CacheManager가
// Writer 호출 시 reflector 결과를 그대로 반환해버린다. 단계명을 prefix로 분리.
`reflector::${research}`, prompt, signal, onProgress
);
} catch (reflErr: any) {
// Reflector 실패는 치명적이지 않다. 감사 이력에만 남기고 빈 reflection으로 Writer를 진행시킨다.
if (reflErr?.name === 'AbortError') throw reflErr;
logError(`[AgentEngine] Reflector soft-fail — Writer 계속 진행: ${reflErr?.message || reflErr}`);
reflection = '';
}
}
// --- Phase 4: Writer ---
const finalReport = await this.executeStep(
state, 'writer', '최종 리포트 작성 및 편집 중...',
() => this.resilientExecute(state, this.writer, 'Writer', research, prompt, signal, onProgress, {
() => this.resilientExecute(state, this.writer, 'Writer', research, prompt, signal, onProgress, {
...options,
context: brainContext,
signal,
config: { role: 'writer', allowFallback: true, isSamePrompt: true, ...options?.config },
priorResults: { plan, writerPrep, previousValidData: state.getResult('finalReport'), ...options?.priorResults },
abstractionLevel: writerLevel
context: brainContext,
signal,
config: { role: 'writer', allowFallback: true, isSamePrompt: true, ...options?.config },
priorResults: { plan, writerPrep, reflection, previousValidData: state.getResult('finalReport'), ...options?.priorResults },
abstractionLevel: writerLevel
}),
research, prompt, signal, onProgress
);