아키텍처 설계
왜 아키텍처가 중요한가
섹션 제목: “왜 아키텍처가 중요한가”Agent Harness는 단순한 API 래퍼가 아닙니다. 프로덕션 환경에서 에이전트를 안정적으로 운영하려면 관심사의 명확한 분리 와 교체 가능한 컴포넌트 구조 가 필수입니다. 잘못 설계된 harness는 모델을 교체하거나 툴을 추가할 때마다 전체 코드를 수정해야 하는 상황을 만듭니다.
SWE-bench 등의 벤치마크에서 반복적으로 확인되듯, 동일한 모델이라도 harness 설계에 따라 성능이 30~40%p 이상 달라질 수 있습니다. 이는 harness 자체가 에이전트 성능의 핵심 변수임을 의미합니다.
4계층 아키텍처
섹션 제목: “4계층 아키텍처”┌─────────────────────────────────────────────────────┐│ Entry & UI Layer ││ TUI / Web UI │ ConfigManager │ SessionManager ││ │ ApprovalManager │├─────────────────────────────────────────────────────┤│ Agent Core Layer ││ MainAgent (parameterized) │ SubAgentSpec ││ AgentDependencies │ SubAgentDeps │├─────────────────────────────────────────────────────┤│ Tool & Context Layer ││ ToolRegistry │ PromptComposer │ MCPClient ││ ContextCompactor │ EventReminder │├─────────────────────────────────────────────────────┤│ Persistence Layer ││ SessionStore │ ConfigResolver │ OperationLog ││ ProviderCache │ GitRollback │└─────────────────────────────────────────────────────┘각 계층은 아래 계층에만 의존 하며, 위 계층은 인터페이스를 통해 접근합니다. 이 단방향 의존성이 테스트 용이성과 교체 가능성을 보장합니다.
계층별 책임
섹션 제목: “계층별 책임”| 계층 | 핵심 책임 | 주요 컴포넌트 |
|---|---|---|
| Entry & UI | 부트스트랩, 사용자 입출력, 승인 요청 | ConfigManager, SessionManager, ApprovalManager |
| Agent Core | 에이전트 생명주기, 서브에이전트 조율 | MainAgent, SubAgentSpec, Dependency Injection |
| Tool & Context | 툴 실행, 프롬프트 조합, 컨텍스트 관리 | ToolRegistry, PromptComposer, MCP Client |
| Persistence | 상태 저장, 설정 해석, 롤백 | SessionStore, ConfigResolver, OperationLog |
설계 원칙
섹션 제목: “설계 원칙”1. 관심사 분리 (Separation of Concerns)
섹션 제목: “1. 관심사 분리 (Separation of Concerns)”각 계층은 하나의 책임만 집니다. UI 계층은 렌더링만, Core 계층은 에이전트 로직만 담당합니다. 이 원칙을 어기면 UI가 바뀔 때 에이전트 로직도 함께 수정해야 하는 결합이 발생합니다.
# 나쁜 예: UI 계층에 에이전트 로직 혼재async function handleUserInput(input: string) { renderSpinner() const response = await llm.complete(buildPrompt(input)) // ← 잘못된 위치 renderResponse(response)}
# 좋은 예: 계층 경계 유지async function handleUserInput(input: string) { renderSpinner() const response = await agentCore.run(input) // ← 위임 renderResponse(response)}2. 의존성 주입 (Dependency Injection)
섹션 제목: “2. 의존성 주입 (Dependency Injection)”MainAgent 는 구체적인 구현 대신 인터페이스 를 받습니다. 이를 통해 테스트에서 mock을, 프로덕션에서 실제 구현을 주입할 수 있습니다.
interface AgentDependencies { llmClient: LLMClient; toolRegistry: ToolRegistry; sessionStore: SessionStore; promptComposer: PromptComposer;}
class MainAgent { constructor(private deps: AgentDependencies) {}}3. 단일 구체 클래스 (Single Concrete Class)
섹션 제목: “3. 단일 구체 클래스 (Single Concrete Class)”상속 계층 대신 생성 파라미터로 행동을 변화 시킵니다. PlannerAgent, ExecutorAgent 같은 별도 클래스를 만드는 대신, MainAgent 에 SubAgentSpec 을 전달해 역할을 정의합니다.
이 패턴의 장점은 모든 에이전트가 동일한 실행 경로를 공유하므로, 버그 수정이 전체에 즉시 반영된다는 점입니다.
Entry & UI 계층 상세
섹션 제목: “Entry & UI 계층 상세”부트스트랩 시점에 공유 매니저들을 초기화합니다.
// 부트스트랩 진입점async function bootstrap(config: AppConfig) { const configManager = new ConfigManager(config); const sessionManager = new SessionManager(configManager); const approvalManager = new ApprovalManager(configManager);
const ui = config.mode === 'tui' ? new TUIAdapter(approvalManager) : new WebUIAdapter(approvalManager);
return new Application({ configManager, sessionManager, approvalManager, ui });}UICallback 인터페이스를 통해 TUI와 Web UI가 동일한 계약을 구현합니다. Agent Core는 구체적인 UI 타입을 알 필요가 없습니다.
Persistence 계층과 설정 해석
섹션 제목: “Persistence 계층과 설정 해석”설정은 우선순위 계층 을 통해 해석됩니다.
프로젝트 로컬 (.agent/config.json) ↓ 없으면사용자 글로벌 (~/.config/agent/config.json) ↓ 없으면환경 변수 (AGENT_MODEL, AGENT_TOKEN 등) ↓ 없으면내장 기본값이 계층적 해석은 로컬 오버라이드를 허용하면서도 글로벌 기본값을 유지하게 해줍니다. CI/CD 환경에서는 환경 변수로, 개발자 로컬에서는 프로젝트 설정으로 제어할 수 있습니다.