한 턴의 복잡한 처리(컨텍스트 조립→라우팅→스트리밍→후처리→학습)는 거대 orchestrator 하나가 흐름의 골격만 쥐고 세부는 추출된 모듈에 위임하는 구조가 유지보수에 유리하며, 멀티에이전트는 "병렬 persona 줄세우기"보다 자원 제약에 맞춘 순차 실행 + 단일 작성자 다중 역할이 로컬 환경에서 더 견고하다 [S1][S2][S4].
🧠 핵심 개념 (Core concepts)
얇은 골격 + 추출 위임:agent.ts(orchestrator)는 한 턴의 흐름을 읽을 수 있게 유지하고, 세부는 handlePrompt/, llm/, actions/, sessions/, multiAgent/ 로 추출 [S1].
Action tag 실행: 모델 출력의 <create_file>, <run_command> 등 태그를 액션 실행기로 라우팅해 도구를 수행 [S1][S4].
단일 작성자 다중 역할(ChunkedWriter): outline → section[N] → polish 를 같은 모델이 번갈아 수행 — hop 마다 컨텍스트 폭증·본문 손실을 피함 [S2].
순차 디스패치(company): CEO 플래너 → 전문가들 순차 실행(peer-context 전달) → CEO 리포터 합성 [S4].
God-class 분해: orchestrator 의 import 가 100줄을 넘지만, 이는 "기능을 작은 모듈로 추출하고 흐름에서 다시 끌어모은" 결과 — 흐름은 한 곳, 구현은 분산 [S1].
메시지 프로토콜 UI: webview 와 streamStart/streamChunk/streamEnd/workflowStage 메시지로 통신 — 진행 단계는 본문이 아니라 상단 인디케이터로 [S3].
모든 종료 경로에서 인디케이터 닫기: 성공·취소·에러 어디서든 workflowStage{done:true} 를 보내 "영원히 도는 스피너" 방지 [S3].
peer-context 버퍼: 앞 에이전트 출력을 잘라(PEER_OUTPUT_BUDGET 1500) 다음 에이전트 프롬프트에 전달 [S4].
raw 출력 → 공용 액션 실행기 재사용: 전문가도 action tag 를 낼 수 있고, 채팅과 같은 실행기를 통과시켜 도구 동작 일관성 유지 [S4].
📖 세부 내용 (Details)
멀티에이전트 설계의 진화 (post-mortem)
초기엔 planner/researcher/reflector/writer/synthesizer 5개 persona 를 줄세웠다. 문제: 각 hop 마다 컨텍스트가 누적되고 원본 본문이 추상화로 손실 돼, 사용자가 본문 분석을 요청해도 "분석 방법론" 만 만들어내는 사고가 났다. → 현재는 단일 ChunkedWriter 가 outline/section/polish 세 역할을 같은 모델에서 번갈아 수행 — 각 호출이 작고 본문은 매 호출에 직접 전달돼 손실이 없다 [S2].
왜 순차 디스패치인가 (company 모드)
사용자는 단일 GPU/제한된 RAM 에서 Astra 를 돌린다. 병렬 에이전트는 여러 모델을 동시에 메모리에 상주시켜야 한다. 순차 실행은 "정확히 한 번에 하나의 모델만 상주" 를 보장하고, LM Studio lifecycle 매니저가 이전 모델을 unload 하고 다음을 load 한다 [S4]. → 이 위키의 sub-agent 제약(병렬 fanout 금지)과도 같은 원리.
왜 handlePrompt 를 재사용하지 않는가
handlePrompt 는 대화형 경로용이라 대화 이력·스트리밍 UI·에이전트 모드 주입 등 12가지를 떠안는다. company 턴은 "system 1개 + user 1개 → 문자열 1개" 의 깨끗한 primitive 가 필요하므로 AIService.chat() 을 쓴다 — 책임이 다른 경로는 다른 primitive [S4].
⚖️ 비교 및 선택 기준 (Comparison & decision criteria)
멀티에이전트 방식
장점
단점
언제
병렬 persona N개
빠름(자원 충분 시)
모델 다중 상주, 컨텍스트 누적/본문 손실
RAM/GPU 넉넉한 서버
단일 작성자 다중 역할
컨텍스트 작고 본문 보존
한 모델 품질에 의존
로컬 단일 모델
순차 디스패치
한 번에 한 모델, 자원 안전
총 시간 김
단일 GPU/제한 RAM
⚖️ 모순 및 업데이트 (Contradictions & updates)
orchestrator 크기: 분해했어도 agent.ts 가 크다 — "흐름 가독성" 을 위해 의도적으로 골격을 남긴 트레이드오프(완전 분해 시 흐름 추적이 파일 사이를 떠돈다).
순차의 비용: 응답이 느리다. 사용자 경험을 위해 진행 단계를 webview 인디케이터로 보여 체감 지연을 완화한다.
AstraAI/src/agents/AgentWorkflowManager.ts — ChunkedWriter 단일 작성자 다중 역할 [S2].
AstraAI/src/agent/multiAgent/workflow.ts — webview 메시지 프로토콜, 모든 경로 인디케이터 닫기 [S3].
AstraAI/src/features/company/dispatcher.ts — 순차 디스패치, peer-context, 설계 근거 주석 [S4].
💻 코드 패턴 (Code patterns)
// 1) 단일 작성자 다중 역할 — 스테이지를 UI 라벨로 (src/agents/AgentWorkflowManager.ts)
constwriter=newChunkedWriter(modelName,overrides);// outline → section → polish
constengine=newAgentEngine(writer);returnawaitengine.runMission(missionId,prompt,brainContext,signal,(stage,msg)=>onProgress(this.mapStageToUI(stage),msg));// ① 구조 → ② 본문 → ③ 다듬기
// 2) 모든 종료 경로에서 인디케이터 닫기 (src/agent/multiAgent/workflow.ts)
}catch(error: any){deps.getWebview()?.postMessage({type:'workflowStage',value:{step:'완료',done: true}});if(error.name==='AbortError'){/* 취소 */return;}// ... 에러 카드
}finally{deps.options.onStreamLifecycle?.end();}// 3) 순차 디스패치 + peer-context 버퍼 (src/features/company/dispatcher.ts, 개념)
letpeerContext='';for(consttaskofplan.tasks){constprompt=buildSpecialistPrompt(task,peerContext);// 앞 에이전트 맥락 포함
constout=awaitaiService.chat({system,user: prompt});// 한 번에 한 모델만 상주
writeAgentOutput(sessionDir,task,out.content);peerContext+='\n'+out.content.slice(0,PEER_OUTPUT_BUDGET);// 다음 에이전트에 전달
}