import { ChunkedWriter, PersonaOverrides } from './factory'; import { AgentEngine, PipelineStage, AgentExecuteOptions } from '../lib/engine'; import { getConfig } from '../config'; /** * Multi-agent 워크플로우를 외부에 노출하는 얇은 매니저. * * 예전엔 planner / researcher / reflector / writer / synthesizer 5개 persona 를 * 줄세웠지만, 각 hop 마다 컨텍스트가 누적되고 원본 본문이 추상화로 손실돼 * 사용자가 본문 분석을 요청해도 "분석 방법론" 만 만들어내는 사고가 있었음. * * 현재 흐름: 단일 ChunkedWriter 가 outline → section[N] → polish 세 역할을 * 같은 모델에서 번갈아 수행 → 각 호출은 작고(컨텍스트 폭증 없음), 본문은 * 매 호출에 직접 전달돼 손실 없음. */ export class AgentWorkflowManager { public static async runStrictWorkflow( prompt: string, modelName: string, brainContext: string, signal: AbortSignal, onProgress: (step: string, message: string) => void ): Promise { // 사용자 config 의 polish persona override 가 있으면 ChunkedWriter 에 주입. // 비어 있으면 기본 polish persona 사용. const cfg = getConfig(); const overrides: PersonaOverrides | undefined = cfg.polishPersonaOverride ? { polish: cfg.polishPersonaOverride } : undefined; const writer = new ChunkedWriter(modelName, overrides); const engine = new AgentEngine(writer); const missionId = `mission_${Date.now()}`; const runOptions: AgentExecuteOptions = { config: { model: modelName }, }; try { return await engine.runMission( missionId, prompt, brainContext, signal, (stage: PipelineStage, message: string) => { onProgress(this.mapStageToUI(stage), message); }, runOptions ); } catch (error: any) { if (error.name === 'AbortError' || error.message.includes('cancelled')) { throw error; } throw new Error(`[Workflow Manager] ${error.message}`); } } private static mapStageToUI(stage: PipelineStage): string { const maps: Record = { idle: '대기', outline: '① 구조 잡기', section: '② 본문 작성', polish: '③ 최종 다듬기', direct: '⚡ 즉답', completed: '완료', error: '오류', }; return maps[stage] || '진행 중'; } }