
The Niners: 9개의 CLI가 어떻게 나의 AI 에이전트를 강력한 경쟁자로 만들었나
요약
MCP(Model Context Protocol) 방식의 도구 호출 대신, 타입이 지정된 9개의 CLI 도구를 활용하여 AI 에이전트의 성능을 혁신적으로 개선한 사례를 다룹니다. 방대한 스키마 토큰 소모 문제를 해결하고, 복합적인 작업을 효율적으로 수행하기 위한 CLI 오케스트레이션 전략을 제시합니다.
핵심 포인트
- MCP 방식의 과도한 토큰 소모 및 컨텍스트 로드 문제 지적
- 도구 호출 대신 도구를 오케스트레이션하는 코드 기반 에이전트 방식 제안
- 타입이 지정된 CLI를 통한 복합 작업 수행 및 효율적인 도구 활용
- Printing Press CLIs 컨벤션을 적용한 기계 우선 설계 패턴
베이 지역에서, 레드 앤 골드(red and gold)를 향한 사랑을 담아.
저는 베이 지역(Bay Area)에 살고 있어서, 계약상 두 가지를 믿어야만 합니다. 라이프스타일로서의 안개, 그리고 Niners입니다. 이번 시즌, 저의 축구 팀은 9개의 명령줄 도구(command-line tools)이며, 이들은 올해 그 어떤 모델 업그레이드보다도 제 AI 에이전트가 할 수 있는 일을 변화시켰습니다.
이것은 9개의 타입이 지정된(typed) CLI 로스터가, 제가 One Year of Building on Kiro에서 작성했던 솔루션 아키텍트용 AI 팀원인 Neo를 유망한 신인에서 플레이오프 경쟁자로 어떻게 변화시켰는지에 대한 이야기입니다.
그 회고록에서 교훈 #5는 단 한 줄이었습니다: "복합적인 작업을 위해서는 타입이 지정된 CLI를 선호하고, 도구 호출(tool calls)은 대화를 위해 남겨두어라." 이 포스트는 그 한 줄의 교훈을 플레이오프 단계로 끌어올린 내용입니다.
문제점: 나의 에이전트는 느린 공격을 펼치고 있었다
Neo는 대부분의 에이전트가 그러하듯, MCP 서버 스택에 연결된 상태로 시작되었습니다. 작동은 했지만, 매 플레이마다 허들(huddle, 작전 회의)을 해야 했습니다. 즉, 방대한 도구 표면(tool surface)을 컨텍스트(context)에 로드하고, 고정된 형태의 도구를 호출하고, 생각한 뒤, 다시 호출하는 과정이었습니다. 마지막 벤치마크에서, 라우터에 연결된 3개의 MCP 서버는 실제 작업이 시작되기도 전에 86,221개의 스키마(schemas) 토큰을 소모했습니다. "이 고객에게 무슨 일이 일어나고 있나요?"라는 질문에 답하는 데 5번의 MCP 호출이 필요했지만, 그에 상응하는 복합 CLI(compound CLI)는 단 한 번의 호출만 사용했습니다.
업계는 지난 1년 동안 몇 가지 방향으로 이 문제를 해결하기 위해 수렴해 왔습니다. 도구를 하나씩 호출하는 대신, 도구를 오케스트레이션(orchestrate)하기 위한 코드를 작성하는 에이전트 방식이며, 이는 중간 결과물을 컨텍스트 윈도우(context window)에서 완전히 제외하는 방식입니다. Anthropic이 이에 대해 작성했고, Cloudflare는 이를 위한 모드를 구축했습니다. "CLI vs MCP" 논쟁은 올봄 내내 제 피드를 채웠습니다.
중요한 점은 이렇습니다: 터미널은 이미 1978년에 이 문제를 해결해 두었습니다. 작은 프로그램들, 텍스트 스트림(Text streams), 종료 코드(Exit codes), 파이프(Pipes)와 같은 것들 말이죠.
그래서 저는 팀을 구성했습니다.
공을 돌려야 할 곳: 9개의 도구 로스터(roster)는 제가 구현한 것이지만, 그 공유된 CLI 컨트랙트(contract)는 Printing Press CLIs에서 영감을 받았습니다. 자기 기술적 명령 카탈로그(self-describing command catalogs), 일관된 에이전트 모드(agent mode), 런타임 의도 해석(runtime intent resolution), 타입화된 종료 동작(typed exit behavior), 그리고 아웃 오브 밴드 전달(out-of-band delivery)과 같은 그들의 기계 우선 컨벤션(machine-first conventions)은 제가 Neo의 툴킷 전체에 적용한 패턴이 되었습니다.
벤치마크가 실제로 측정한 것
저는 동일한 라이브 프로덕션 백엔드(live production backends)를 대상으로 두 경로를 모두 실행했습니다. 한쪽에는 실제 MCP stdio JSON-RPC를, 다른 한쪽에는 직접적인 CLI 서브프로세스(subprocesses)를 사용했으며, 각 실험군당 5회 반복하였고, 토큰은 바이트-대-토큰 추정치 대신 cl100k_base를 사용하여 계산했습니다.
결과는 단순히 "CLI는 좋고, MCP는 나쁘다"는 것보다 훨씬 유용했습니다:
- 상주 컨텍스트(Resident context)가 결정적인 차이였습니다. 라우터의 3개 MCP 서버는 매 턴마다 148개의 도구와 86,221개의 스키마(schemas) 토큰을 노출했습니다. 이는 200k 컨텍스트 윈도우(context window)의 43.1%에 달하는 양입니다. 반면 CLI 카탈로그는 필요할 때만 로드되었습니다.
- 일반적인 호출 지연 시간(latency)은 무승부였습니다. 신원(Identity), 편지함(inbox), 이메일 검색의 차이는 대략 10-70ms였습니다. 네트워크 시간이 지배적이었으며, CLI는 매 호출마다 약 60ms의 프로세스 생성(process-spawn) 비용을 지불했습니다.
- 가공되지 않은 CLI 페이로드(payloads)는 더 나쁠 수도 있었습니다. 투영되지 않은(Unprojected) 이메일 결과는 CLI가 업스트림 엔벨로프(upstream envelope)를 반환했기 때문에 2.4-7.2배 더 컸습니다. 하지만
--select옵션은 CRM 응답을 64.5% 줄였고, 복합 계정 핑거프린트(compound account fingerprint)는 5번의 MCP 호출에 걸친 5,367개 토큰 대비 689개의 토큰만을 사용했습니다. - 대화 길이에 따라 경제성이 달라졌습니다. 총 3번의 호출이 포함된 10턴 세션 모델링에서, CLI 경로는 토큰을 37.7배 적게 사용했습니다. 이는 캐싱되지 않은 상한선(upper bound)입니다. 프롬프트 캐싱(prompt caching)을 통해 격차는 줄어들 것이며, 저는 이 환경에서 캐시를 측정할 수 없었습니다.
따라서 모든 CLI 호출이 더 빠르거나 작다는 주장이 아닙니다. 승리는 온디맨드 탐색(on-demand discovery), 소스 투영(source projection), 복합 명령(compound commands), 그리고 중간 데이터를 모델의 컨텍스트에서 제외하는 방식에서 옵니다.
로스터(The roster)
제 에이전트가 매일 작업하는 각 도메인을 담당하는 9개의 CLI입니다. 포지션별 구성은 다음과 같습니다:
- QB1 — 메모리 CLI (the memory CLI). 로컬 SQLite 브레인입니다. 저장, 검색 (키워드/시맨틱/하이브리드), 제자리 편집(edit in place), 그리고 새로운 기술인 워크 그래프(work graph)를 지원합니다. 메모리 사이에 유형화된 엣지(typed edges)를 생성하여, "이 고객과 연결된 모든 것을 보여줘"라는 요청을 12번의 검색이 아닌 단 한 번의 그래프 순회(graph traversal)로 처리합니다. 또한 스케줄러 역할도 수행하여, 모든 반복적인 프롬프트는 명령어 하나로 크론 잡(cron job)이 됩니다.
- RB — CRM CLI (the CRM CLI). 5번의 API 왕복(round trips)을 하나의 명령어로 대체하는 복합 읽기(Compound reads)를 수행합니다 (
fingerprint= 계정 + 팀 + 진행 중인 딜 + 지출액을 서버 측에서 조인). - WR1 — Slack CLI (the Slack CLI). 189개의 명령어를 지원합니다. 검색, 히스토리, 포스팅, 리액션, 파일 업로드가 가능합니다.
- WR2 — 이메일 + 캘린더 CLI (the email + calendar CLI). 받은 편지함, 전체 쿼리 DSL을 이용한 검색, ID 기반의 전체 메시지 본문 확인, 캘린더 보기, 회의 생성/응답 기능을 제공합니다. 또한 명시적인 인간의 승인 없이는 외부 이메일 발송을 거부하는 가드레일(guardrail)이 설정되어 있습니다.
- TE — 문서 CLI (the docs CLI). 문서 라이브러리(SharePoint) 전체에 대한 전문 검색(Full-text search), 드라이브 탐색(drive walks), 다운로드를 지원합니다.
- OL — 작업 CLI (the tasks CLI). Asana를 지원하며, "수정"을 위해 삭제 후 재생성하는 방식이 아닌 제자리 업데이트(in-place updates)를 포함하여 57개의 명령어를 제공합니다.
- DB — 영업 콘텐츠 CLI (the sales-content CLI). 승인된 콘텐츠 라이브러리에 대해 읽기 전용 검색 및 LLM 즉시 답변 기능을 제공합니다.
- LB — 전문가 CLI (the expert CLI). 큐레이션된 지식 저장소(knowledge vault)로부터 인용이 뒷받침된 심층적인 답변을 제공합니다. 속도는 느리지만(초 단위가 아닌 분 단위) 권위가 있습니다. 정답이 반드시 맞아야 할 때 투입하는 러닝 스토퍼(run-stopper)와 같습니다.
- 스페셜 팀(Special teams) — 현장 운영 오케스트레이터 (the field-ops orchestrator). 다른 CLI들을 호출하는 복합 플레이(Compound plays)를 수행합니다. 아침 브리핑을 위한 명령어 하나, 회의 준비를 위한 명령어 하나, 그리고 특정 기업에 대한 공개 신호 조사(SEC 공시, GitHub 발자국, Wikipedia, Hacker News)를 위한 명령어가 준비되어 있습니다.
총 397개의 명령어. 이 중 단 하나도 필요할 때까지 모델의 컨텍스트 (Context)에 로드되지 않습니다.
무엇이 CLI를 에이전트 준비 상태(agent-ready)로 만드는가
에이전트에게 bash를 쥐여주고 운에 맡기는 것은 전략이 아닙니다. 이 명단에 있는 모든 CLI는 동일한 계약 (Contract)을 따르며, 이것이 바로 실제 핵심 비결입니다:
1. 에이전트 모드를 위한 단일 플래그 (Flag). --agent = JSON 출력, 대화형 프롬프트 없음, 색상 없음, 그리고 자동 승인. 압축된 출력 (Compact output)은 의도된 계약의 일부이지만, 벤치마크 과정에서 실제 결함이 발견되었습니다: 여러 이메일 및 CRM 경로에서 --compact가 아무런 동작도 하지 않는 (no-op) 현상이 있었습니다. 현재는 --select와 복합 명령어 (Compound commands)가 신뢰할 수 있는 형태 정의 프리미티브 (Shaping primitives)입니다.
2. 자기 기술 (Self-description). 모든 CLI는 agent-context --json을 제공합니다. 이는 모든 명령어, 플래그, 그리고 읽기 전용 여부를 담은 기계 판독 가능 (Machine-readable) 카탈로그입니다. 에이전트는 플래그의 형태를 절대 추측하지 않습니다. 바이너리 (Binary)에게 직접 물어봅니다.
3. 런타임 도구 검색 (Runtime tool search). 대부분은 which "<자연어 의도>"를 제공합니다. 이는 "오래된 작업 찾기"를 적절한 명령어로 매핑하는 내장된 리졸버 (Resolver)이며, 타입이 지정된 종료 코드 (Exit code, 2 = 확신할 수 있는 일치 항목 없음, 기계 판독 가능한 "모름")를 반환합니다. 이것이 모두가 열광하는 "도구 검색 (Tool search)" 패턴이며, 각 바이너리별로 구현되어 있습니다.
4. 대역 외 결과 (Out-of-band results). --deliver file:/tmp/x.json은 출력을 디스크로 원자적 (Atomically)으로 라우팅합니다. 에이전트는 중간 데이터가 모델을 거치지 않고 CLI → jq → CLI를 체이닝 (Chaining)합니다. --select id,name,status는 소스 단계에서 필드를 투영 (Project)합니다. 이것이 토큰 효율성 (Token-efficiency)을 위한 전략입니다: 모델은 가공되지 않은 피드 (Raw feed)가 아니라 컴파일된 답변을 보게 됩니다.
5. 재시도 안전 시맨틱 (Retry-safe semantics). --dry-run, --idempotent, --ignore-missing. 에이전트 루프 (Agent loops)는 작업을 재시도합니다; 도구들은 중복 생성하는 대신 무시하고 넘어갑니다.
6. 오프라인 모드 (Offline mode). sync는 모든 것을 로컬 SQLite로 미러링 (Mirroring)합니다; --data-source local은 미러를 대상으로 실행됩니다. 데모는 오후 2시에 SSO(Single Sign-On)가 만료되어도 중단되지 않습니다.
플레이북: 복합 프롬프트 (Composite prompts)
재미있는 점은 하나의 프롬프트가 전체 로스터(roster)로 퍼져나갈(fan out) 때입니다. 실제 플레이북에서 제가 가장 좋아하는 것들은 다음과 같습니다:
웨스트 코스트 오펜스 (The West Coast Offense) (9개 모두, 하나의 프롬프트):
"고객 QBR(분기별 비즈니스 리뷰)을 준비해줘 — CRM 지문(fingerprint), 해당 고객과의 이메일 스레드 및 캘린더 기록, Slack에서 팀원들이 나누는 대화, 문서 라이브러리, 미결 작업(open tasks), 해당 유스케이스(use case)에 대해 승인된 메시징을 가져오고, 전문가 에이전트에게 그들의 아키텍처(architecture)에 대해 물어본 뒤, 공개 신호(public-signal) 조사를 추가해. 그런 다음 하나의 보고서(dossier)로 컴파일하여 내 워크 그래프(work graph)에 링크해줘."
9개의 도구, 병렬 팬아웃(parallel fan-out), 디스크 상의 중간 JSON, 컨텍스트 내의 하나의 컴파일된 요약본. 빠른 작업들은 몇 초 만에 끝나지만, 전문가 작업은 몇 분이 걸릴 수 있습니다. 따라서 모든 경로가 동일한 시간을 소요하는 것처럼 가장하는 대신, 보고서는 점진적으로 채워집니다.
투 미닛 드릴 (The two-minute drill):
"출장 주간을 위해 내 업무를 정리해줘 — 내 여행 일정과 겹치는 캘린더 충돌을 찾고, 내가 부재중일 때 마감되는 작업들의 기한을 연기하며, 내가 검토할 수 있도록 회의 거절 초안을 작성하고, 결정 사항들을 메모리(memory)에 저장해줘."
필름 룸 (The film room):
"오늘 아침 브리핑에서 왜 X라고 주장했지?" — 메모리 CLI의
bisect기능은 컴파일된 모든 주장을 소스 메모리로 추적하며, 워크 그래프(work graph)는 무엇이 연결되어 있는지 보여줍니다. 출처(Provenance)가 일급 쿼리(first-class query)로 취급됩니다.
시즌 전체 통계 시트 (The season-long stat sheet):
"내가 실제로 수행한 내용을 바탕으로 분기별 검토 보고서를 작성해줘" — 완료된 작업, 체결된 계약, 게시물, 저장된 결과물 등 시즌 동안 자동으로 분류된 네임스페이스(namespaces)에서 모두 가져옵니다.
박스 스코어 (The box score)
측정된 박스 스코어:
- 라우터의 상주 MCP 스키마: 턴당 86,221 토큰; CLI 경로는 필요할 때만 카탈로그를 로드함
- 기본적인 신원 확인, 편지함(inbox), 검색 지연 시간: 사실상 동일함
- 계정 지문(Account fingerprint): 고정된 형태의 MCP 도구를 통한 5회 호출, 5,367 토큰, 1.667초 대비 CLI를 통한 1회 호출, 689 토큰, 약 0.78초
- 필드 투영(Field projection):
--select사용 시 282 → 100 토큰으로 64.5% 감소 - 10턴 세션 모델: 캐싱되지 않은 상한값 기준, CLI 사용 시 토큰 소모량 37.7배 감소
- 에이전트는 여전히 평면(plane) 위에서 작동하며, 반복되는 워크플로우는 여전히 cron 작업이 되고, 실패는 여전히 내가 직접 재현할 수 있는 셸 명령어로 남음
마지막 항목이 벤치마크보다 더 중요합니다. CLI는 검사(inspectable)가 가능합니다. 에이전트와 나는 동일한 방식으로 디버깅합니다.
당신만의 '나이너스(niners)'를 구축하세요
9개가 꼭 필요하지는 않습니다. 필요한 것은 계약(contract)입니다:
- 에이전트가 매일 접하는 3~4개의 시스템을 선택하세요
- 각 시스템을 JSON을 사용하고, 프롬프트를 사용하지 않으며, 스스로를 설명할 수 있는 CLI로 래핑(wrap)하세요
- 에이전트가 런타임에 명령어를 검색할 수 있도록 의도 해결사(intent resolver)를 추가하세요
- 중간 결과는 컨텍스트 윈도우(context window)가 아닌 파일을 통해 라우팅하세요
- 가장 빈번하게 발생하는 다중 호출 시퀀스를 위해 복합 명령어(compound commands)를 배포하세요
모델은 계속해서 좋아지고 있습니다. 하지만 내 에이전트가 얻은 가장 큰 능력의 도약은 모델의 출시가 아니었습니다. 그것은 바로 훌륭한 로스터(roster), 공유된 계약, 그리고 플레이북(playbook)이었습니다.
Go Niners. 🏈
Neo는 도메인 하위 에이전트(sub-agents), 로컬 우선 메모리(local-first memory), 그리고 이제 9개의 CLI 툴킷을 갖춘 라우터 에이전트입니다. 하나의 Kiro CLI 에이전트에서 함대로 성장한 과정에 대한 시즌 1 요약은 One Year of Building on Kiro를 읽어보세요. 질문이나 로스터 제안은 댓글로 환영합니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

