내 AI 어시스턴트에게 필요했던 것은 더 큰 루프가 아니라 컨트롤 플레인(Control Plane)이었다
요약
단순한 에이전트 루프의 한계를 극복하기 위해 컨트롤 플레인(Control Plane) 중심의 아키텍처로 전환하는 방법을 다룹니다. 경험, 컨트롤, 실행, 프록시 평면으로 시스템을 분리하여 복잡한 워크플로를 효율적으로 관리하는 구조를 제안합니다.
핵심 포인트
- 단일 루프에 도구를 추가하는 방식은 복잡성이 증가하여 한계에 직면함
- 시스템을 경험, 컨트롤, 실행, 프록시 평면으로 분리하여 아키텍처 최적화
- 컨트롤 플레인이 작업의 종류를 결정하고 실행 도구를 조정하는 역할 수행
- 상주형 어시스턴트를 위한 상태 관리 및 작업 위임 구조의 중요성
나는 동일한 루프(loop)에 더 많은 도구(tools)를 추가함으로써 내 AI 어시스턴트를 더 똑똑하게 만들려고 계속 시도했다.
그 방식은 한동안 효과가 있었다. 그러다 어시스턴트가 일반적인 사용자 작업들을 수행해야 하는 상황이 왔다. 채팅에서 Codex 작업을 이어가거나, DingTalk에서 상태 질문에 답하거나, 데스크톱 워크플로(desktop workflow)가 어떻게 성공했는지 기억하거나, 마우스를 사용 중인 다른 실행 프로세스 뒤에서 대기하거나, 여전히 Claude Code 트래픽을 동일한 localhost 서버를 통해 라우팅해야 하는 일들 말이다.
그 시점에서 문제는 더 이상 "한 명의 에이전트(agent)가 얼마나 많은 도구를 호출할 수 있는가?"가 아니었다.
문제는 아키텍처(architecture)였다.
루프에 너무 많은 작업이 몰렸다
첫 번째 형태는 단순했다:
사용자 메시지 -> 어시스턴트 루프 -> 도구 -> 답변
데모용으로는 괜찮다. 하지만 상주형 어시스턴트(resident assistant)에게는 적합하지 않다.
상주형 어시스턴트는 메시지가 새로운 작업인지, 후속 작업인지, 상태 확인인지, 수정인지, 아니면 취소인지 알아야 한다. 실행 중인 다른 작업으로부터 데스크톱 권한을 가로채는 것을 피해야 한다. 모든 오래된 트랜스크립트(transcript)를 컨텍스트(context)에 밀어넣지 않고도 절차를 기억해야 한다. 자신이 실행자인 척하지 않으면서 코딩 작업을 Codex나 Claude Code에 위임해야 한다.
이것들은 서로 다른 작업들이다. 이들을 하나의 루프 안에 계속 가두어 두었을 때, 모든 수정 사항은 루프를 더 유능하게 만들었지만 동시에 이해하기는 더 어렵게 만들었다.
그래서 나는 어시스턴트를 하나의 에이전트로 생각하는 것을 멈추고, 이를 로컬 컨트롤 플레인(local control plane)으로 취급하기 시작했다.
시스템을 추론하기 쉽게 만든 분리
CliGate에서 아키텍처는 이제 다음과 같은 모습이다:
Experience Plane (경험 플레인)
-> Assistant Control Plane (어시스턴트 컨트롤 플레인)
-> Runtime Execution Plane (런타임 실행 플레인)
...
이름은 격식 있게 들리지만, 그 경계는 실용적이다.
**Experience Plane (경험 플레인)**은 사용자가 어디에서 대화하고 있는지를 관리한다: 대시보드 채팅, 어시스턴트 작업, Telegram, Feishu, DingTalk, 예약된 작업 등이다.
**Assistant Control Plane (어시스턴트 컨트롤 플레인)**은 이것이 어떤 종류의 작업인지 결정한다. 상태(state)로부터 답변해야 하는가? 작업을 시작해야 하는가? 기존 작업을 계속해야 하는가? 아니면 데스크톱이 이미 다른 실행 프로세스에 의해 점유되어 있으므로 대기해야 하는가?
**런타임 실행 평면 (runtime execution plane)**은 Codex와 Claude Code가 상주하는 곳입니다. 이들이 실제 코딩 작업을 수행합니다. 어시스턴트는 이들을 파견(dispatch), 계속(continue), 요약(summarize) 및 조정(coordinate)할 수 있지만, 이들의 열등한 버전이 될 필요는 없습니다.
**프록시/모델 액세스 평면 (proxy/model access plane)**은 프로토콜 변환, 계정 풀(account pools), API 키, 라우팅(routing), 모델 매핑(model mapping), 요청 로그(request logs) 및 사용량과 같은 지루하지만 필수적인 제공자(provider) 작업을 처리합니다.
사이드 평면(side planes)은 어시스턴트가 정상적으로 작동하도록 유지해 주는 요소들입니다:
- 관찰 (observation): 흩어져 있는 런타임 이벤트를 작고 구조화된 사실(facts)로 변환합니다.
- 메모리 (memory): 재사용 가능한 사실, 지시 사항(directives), 워크플로(workflows) 및 참조(references)를 저장합니다.
- 정책 (policy): 특정 작업이 허용되는지, 대기열에 있는지, 아니면 승인이 필요한지 결정합니다.
관찰 우선(Observation-first) 방식이 가져온 행동의 변화
가장 큰 개선은 어시스턴트가 가공되지 않은 로그(raw logs) 대신 관찰(observations)을 소비하도록 만든 데서 왔습니다.
만약 Codex 실행이 승인을 기다리고 있다면, 어시스턴트는 그 사실을 다시 알아내기 위해 거대한 트랜스크립트(transcript)를 읽어서는 안 됩니다. 대신 다음과 같은 압축된 사실을 확인해야 합니다:
"작업 X는 명령 Y를 실행하기 위해 승인을 기다리는 중입니다."
만약 다른 어시스턴트 실행이 현재 데스크톱을 제어하고 있다면, 새로운 실행이 채팅 기록을 통해 이를 추측해서는 안 됩니다. 대신 다음과 같은 리소스 점유 상태를 확인해야 합니다:
"데스크톱은 실행 R(run R)에 의해 점유되어 있습니다."
이 단 한 번의 변화로 상태 질문, 취소, 후속 작업 및 동시 실행(concurrent runs)이 훨씬 덜 취약해졌습니다. 어시스턴트는 더 이상 마지막 몇 개의 메시지로부터 시스템 상태를 추론할 필요가 없습니다. 시스템이 어시스턴트에게 상태 모델(state model)을 제공하기 때문입니다.
메모리는 단순히 더 긴 컨텍스트가 아니다
저는 또한 "기억하는 것"이 프롬프트에 더 많은 채팅 기록을 밀어 넣는 것과 같지 않다는 것을 배웠습니다.
이 어시스턴트에게 메모리는 파일 기반이며 범위(scoped)가 지정되어 있습니다. 워크플로, 사실, 상시 지시 사항 또는 참조를 저장할 수 있습니다. 다음의 유사한 요청이 들어오면, 프롬프트에는 작은 메모리 인덱스(memory index)만 전달됩니다. 만약 어시스턴트가 특정 항목이 중요하다고 판단하면, 명시적으로 그 본문을 호출(recall)합니다.
이를 통해 기본 컨텍스트(default context)를 작게 유지하면서도, 어시스턴트가 다음과 같은 것들을 학습할 수 있게 합니다:
- 데스크톱 출판 (Desktop Publishing) 흐름이 최종적으로 어떻게 작동하는지
- 사용자가 보존하기를 원하는 프로젝트 규칙이 무엇인지
- 내부 문서가 어디에 위치하는지
- 무엇을 다시 시도하지 말아야 하는지
절차적 메모리 (Procedure memories)의 경우, 규칙은 '확인 후 신뢰 (verify-then-trust)'입니다. 기억된 단계들을 시도하되, UI가 여전히 일치하는지 확인하십시오. 만약 변경되었다면, 다시 탐색하고 성공 후에 메모리를 업데이트하십시오.
이것이 제가 실용적인 어시스턴트가 진화하기를 바라는 방식에 더 가깝습니다. 거대한 트랜스크립트 (Transcript)를 키우는 것이 아니라, 성공적인 작업을 재사용 가능한 단위로 증류 (Distilling)하는 방식 말입니다.
이것이 로컬 AI 도구에 중요한 이유
로컬 AI 툴링은 특정한 방식으로 복잡합니다.
사용자는 Claude Code, Codex CLI, Gemini CLI, OpenClaw, 브라우저 세션, 데스크톱 앱, Telegram 채널, 그리고 여러 제공자 (Provider) 계정을 가지고 있을 수 있습니다. 어려운 점은 단지 하나의 모델 호출을 만드는 것이 아닙니다. 어려운 점은 어시스턴트를 모든 메시지를 가로채는 불투명한 감독관으로 만들지 않으면서, 이 모든 조각들을 조율된 상태로 유지하는 것입니다.
그것이 CliGate가 여전히 직접적인 런타임 경로 (Runtime path)를 유지하는 이유입니다. 사용자가 이미 Codex 세션과 대화 중이라면, 메시지는 그곳으로 바로 전달될 수 있습니다. 어시스턴트 컨트롤 플레인 (Control plane)은 명시적인 조율, 백그라운드 작업, 메모리, 정책, 데스크톱 작업, 그리고 교차 채널 워크플로 (Cross-channel workflows)를 위한 것입니다.
이 분리는 화려하지는 않지만, 인상적인 데모와 제가 계속 실행해 둘 수 있는 도구 사이의 차이를 만듭니다.
요점 (The takeaway)
저는 예전에 이렇게 물었습니다: 어떻게 하면 어시스턴트 루프를 더 똑똑하게 만들 수 있을까?
이제 저는 이렇게 묻습니다: 어떤 플레인 (Plane)이 이 책임을 맡아야 하는가?
이 질문은 많은 우발적 복잡성 (Accidental complexity)을 방지해 주었습니다. 이 질문 덕분에 제공자 라우팅 (Provider routing)을 어시스턴트 루프에서 분리하고, 실행은 전용 런타임 (Runtimes) 내부에서 수행하며, 관찰 (Observations)은 가공되지 않은 로그에서 분리하고, 메모리는 무제한의 채팅 기록에서 분리할 수 있게 되었습니다.
프로젝트는 여기서 오픈 소스로 공개되어 있습니다: CliGate.
만약 여러분이 기존 도구들을 중심으로 에이전트 (Agents)를 구축하고 있다면, 모든 것을 하나의 루프 안에 넣고 있습니까, 아니면 컨트롤, 실행, 관찰, 그리고 메모리를 분리하기 시작했습니까?
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기