왜 당신의 AI 에이전트는 쉘이 필요할까 (그리고 안전하게 쉘을 제공하는 방법)
I’m happy to translate the article for you, but I’ll need the text you’d like translated. Could you please paste the content (or the portion you want translated) here? I’ll keep the source line, formatting, markdown, and any code blocks unchanged as you requested.
Claude Code — A New Take on Agent Architecture
Claude Code는 에이전트 아키텍처에 대한 제 생각을 바꾸었습니다. 50개의 다양한 MCP 서버와 맞춤형 통합을 탑재한 에이전트보다 성능이 뛰어나며, 내부 구조는 놀라울 정도로 단순합니다:
- 도구가 난잡하지 않음.
- 맥락을 잡아먹는 거대한 스키마 정의가 없음.
- 파일 시스템과 Bash만 사용.
AI 커뮤니티는 왜 이것이 이렇게 잘 작동하는지를 점차 파악하고 있으며, 우리 중 많은 이들이 자체 에이전트에 부착해 온 복잡성을 재고하고 있습니다.
두 가지 “기본” 접근 방식
데이터와 함께 작업해야 하는 에이전트를 구축할 때, 대부분은 다음 세 가지 패턴 중 하나를 기본으로 사용합니다:
- Prompt stuffing – 모든 것을 컨텍스트 윈도우에 넣고 최선을 기대합니다.
- Tool libraries – MCP 서버에 연결하고, 사용자 정의 도구를 정의하며, 에이전트가 필요한 것을 가져올 수 있는 방법을 제공합니다.
- Vector search – 데이터를 임베딩하고, 의미적 유사성을 실행하며, 검색 결과가 관련 있기를 기도합니다.
이러한 접근 방식은 작동하지만, 각각 트레이드오프가 있습니다:
| 접근 방식 | 장점 | 단점 |
|---|---|---|
| Prompt stuffing | 간단하고 추가 인프라가 필요 없음 | 토큰 제한에 빠르게 도달함 |
| Vector search | 의미적 유사성에 뛰어남 | 구조화된 데이터의 정확한 값 처리에 어려움 |
| Tool libraries | 기능 문제를 해결함 | 새로운 도구마다 스키마 정의가 추가되고, 모델이 고려해야 할 옵션이 늘어나며, 실패 가능성이 커집니다 |
세 번째 옵션 – 파일시스템 + Bash
왜 합리적인가
- 학습 데이터 – LLM은 수십억 줄의 코드와 수많은 예시(디렉터리 탐색, 파일 검색, 복잡한 코드베이스에서 상태 관리 등)를 학습했습니다.
- 네이티브 기본 명령 –
grep,cat,find,awk와 같은 명령은 모델의 “네이티브 언어”에 해당하므로 별도로 가르칠 필요가 없습니다.
Vercel 팀은 내부 에이전트를 재구축하면서 이를 발견했습니다. 두 가지—파일시스템 도구와 Bash 도구—만으로 대부분의 맞춤형 툴링을 대체함으로써 다음을 달성했습니다:
- 영업 통화 요약 에이전트 비용을 ≈ $1.00 per call에서 ≈ $0.25 on Claude Opus로 절감.
- 출력 품질 향상.
Bash가 지원되는 에이전트가 할 수 있는 일
- 무엇이든 연결 –
curlAPI, 데이터베이스용 CLI 툴, 클라우드 서비스, Kubernetes 등. - 자신만의 컨텍스트 저장·조회 – 결과를 파일에 기록하고, 일시 중지 후 나중에 다시 이어서 작업; 파일시스템이 작업 메모리가 됩니다.
- 정밀 검색 –
grep -r "pricing objection" transcripts/는 정확히 일치하는 항목을 반환하며, 유사도 점수를 사용하지 않습니다. - 자연스러운 데이터 계층 구조 – 고객 레코드, 티켓 히스토리, CRM 내보내기 등을 디렉터리 구조에 깔끔히 매핑.
- 완전한 디버깅 가능성 – 읽힌 파일, 실행된 명령, 기록된 출력 모두를 확인할 수 있습니다.
직관: 에이전트가 코드베이스를 탐색해 버그를 찾을 수 있다면, 비즈니스 데이터를 같은 방식으로 탐색할 수 있습니다. 쉘 명령을 실행할 수 있다면 이미 사용 중인 거의 모든 시스템과 상호작용할 수 있습니다.
보안 – Bash 접근 샌드박싱
AI 에이전트에게 제한 없는 Bash 접근 권한을 부여하는 것은 무섭습니다. 하나의 환각된 rm -rf만으로도 큰 문제가 발생합니다. 해결책은 샌드박싱입니다:
- 에이전트의 명령을 프로덕션 시스템에 영향을 미치지 못하는 격리된 환경에서 실행합니다.
- 에이전트가 생각할 수 있는 것(마운트된 디렉터리)과 실제로 할 수 있는 것(샌드박스 실행기)을 구분합니다.
아키텍처 개요
Agent receives task
↓
Explores filesystem (ls, find)
↓
Searches for relevant content (grep, cat)
↓
Sends context + request to LLM
↓
Returns structured output
Bash 실행은 격리된 샌드박스에서 이루어지며, 위험 없이 네이티브 파일 시스템 작업의 힘을 제공합니다.
샌드박스 요구 사항
| 요구 사항 | 설명 |
|---|---|
| 프로세스 격리 | 명령이 호스트를 탈출할 수 없는 격리된 환경에서 실행됩니다. WebAssembly 런타임은 설계상 메모리‑안전 실행을 제공하므로 이상적입니다. |
| 디렉터리 마운트 | 에이전트가 필요로 하는 디렉터리만 노출합니다(예: /project를 /workspace로 마운트). 그 외의 모든 것은 에이전트 관점에서 존재하지 않습니다. |
| 세션 지속성 | 다단계 워크플로우를 위해 명령 간에 구성 및 상태를 유지하되, 다른 세션으로 흐르지 않게 합니다. |
| 가시적인 실행 경로 | stdout, stderr 및 전체 명령 기록을 캡처하여 정확히 어떤 일이 있었는지 감사할 수 있습니다. |
보안 모델은 다층 방어입니다: 에이전트가 예상치 못한 명령을 생성하더라도, 샌드박스가 실제 발생할 수 있는 일을 제한합니다.
MCP (Model Context Protocol)에 대한 간단한 메모
AI 툴링 분야를 따라오고 있다면 MCP에 대해 들어봤을 것입니다. Anthropic이 2024년 말에 출시했으며, 에이전트를 외부 도구 및 데이터와 연결하는 사실상의 표준이 빠르게 자리 잡았습니다.
- MCP 이전: 각 페어링(예: GitHub ↔ 에이전트, Slack ↔ 에이전트, DB ↔ 에이전트)마다 맞춤형 통합이 필요했습니다.
- MCP 이후: 각 도구마다 하나의 MCP 서버를, 각 에이전트마다 하나의 MCP 클라이언트를 구축하면 N × M 통합 문제를 N + M 문제로 전환할 수 있습니다.
TL;DR
- 에이전트를 위한 보편적이고 네이티브한 도구 세트로 filesystem + Bash를 사용하세요.
- 프로덕션을 안전하게 유지하기 위해 Bash 실행을 Sandbox 처리하세요.
- 필요한 나머지 외부 도구 연결을 위해 MCP를 활용하세요.
이 접근 방식은 도구의 산재를 없애고 비용을 절감하며 투명하고 디버깅이 가능하며 강력한 에이전트 아키텍처를 제공합니다.
왜 CLI 도구가 MCP보다 좋은가?
MCP 사용량이 늘어날수록 심각한 문제가 나타납니다: 도구 정의가 LLM의 컨텍스트 윈도우를 잡아먹는다. 각 도구마다 설명, 매개변수, 반환 스키마가 필요합니다. 수십 개의 MCP 서버와 수백 개의 도구에 연결하면 에이전트가 작업을 시작하기도 전에 토큰이 소진됩니다.
유닉스 철학이 승리한다
- 작고, 모듈화되고, 조합 가능 – CLI 도구는 텍스트를 입력받아 텍스트를 출력하고, 서로 체인으로 연결될 수 있습니다.
- 검증된 실전 경험 –
git,grep,curl,jq는 수십 년간 프로덕션에서 사용되어 왔습니다. - 안정적이고 문서화가 잘 됨 – 인터페이스가 거의 바뀌지 않습니다.
LLM은 훈련 과정에서 이러한 도구들을 수십억 번 보았습니다. 구문, 플래그, 일반적인 패턴, 오류 메시지를 이미 알고 있습니다. 이것은 “컨텍스트 내 학습”이 아니라, 깊이 내재된 지식입니다.
MCP vs. CLI
| 측면 | MCP | CLI |
|---|---|---|
| 학습 곡선 | 에이전트가 제공된 스키마(추가 기능)에서 각 도구를 배워야 함. | 에이전트가 이미 도구를 알고 있음(네이티브 이해). |
| 조합성 | 실제 파이프라인처럼 연결하기 어려워서 출력 → 입력 연결이 서툴다. | 간단한 파이프라인: grep "error" logs.txt | wc -l – 모델이 이를 구성하는 방법을 알고 있음. |
| 토큰 효율성 | 높음 – 각 도구 정의가 토큰을 추가한다. | 낮음 – 명령 텍스트만 필요. |
| 보안 | 서버당 맞춤형 샌드박스가 필요함. | 임의 쉘 접근에 대한 샌드박스가 필요함(실제 도전 과제). |
MCP는 배포와 발견 가능성 측면에서, 특히 비기술 사용자나 매우 도메인‑특화된 워크플로에 적합합니다. 하지만 실제로 에이전트에게 작업을 수행할 수 있는 능력을 부여한다면, CLI 도구가 더 우수합니다: 토큰 효율성이 높고, 조합이 쉽고, 모델이 이미 뛰어나게 다루는 영역과 일치하기 때문입니다.
핵심 문제: 안전한 CLI 접근
에이전트가 임의의 셸 명령을 실행하도록 허용하는 것은 위험합니다. 우리는 다음을 만족하는 샌드박스가 필요합니다:
- macOS, Linux, Windows 전반에 이식 가능해야 합니다.
- 강력한 격리를 제공해야 합니다 (WASM 또는 마이크로‑VM).
- 명령줄에서 쉽게 사용할 수 있어야 합니다.
Bashlet 소개
Bashlet은 AI 에이전트에게 샌드박스된 Bash 접근을 제공하는 오픈‑소스 도구입니다.
플랫폼과 보안 요구에 따라 여러 격리 백엔드를 지원합니다.
| 백엔드 | 특징 |
|---|---|
| Wasmer (WASM) | 크로스‑플랫폼, 가벼운 샌드박스. macOS, Linux, Windows에서 동작합니다. 시작 시간 ≈ 50 ms. |
| Firecracker (microVM) | 전체 Linux VM 격리를 제공해 하드웨어 수준 보안을 구현합니다. Linux 전용이며 KVM이 필요합니다. 부팅 시간 ≈ 125 ms. |
기본적으로 Bashlet은 사용 가능한 최적의 백엔드를 자동으로 선택합니다.
- Linux + KVM → Firecracker VM.
- 그 외 모든 플랫폼 → Wasmer WASM 샌드박스.
Basic Workflow
# 1️⃣ Create a session with a mounted directory
bashlet create --name demo --mount ./src:/workspace
# 2️⃣ Run commands in isolation
bashlet run demo "ls /workspace"
bashlet run demo "grep -r 'TODO' /workspace"
# 3️⃣ Terminate when done
bashlet terminate demo
One‑off Commands (no session management)
bashlet exec --mount ./src:/workspace "ls /workspace"
프리셋: 설정 반복 방지
많은 워크플로에서는 동일한 마운트, 환경 변수 및 설정 명령이 필요합니다. ~/.config/bashlet/config.toml에 한 번 정의하세요:
[presets.kubectl]
mounts = [
["/usr/local/bin/kubectl", "/usr/local/bin/kubectl", true],
["~/.kube", "/home/.kube", true]
]
env_vars = [["KUBECONFIG", "/home/.kube/config"]]
setup_commands = ["kubectl version --client"]
[presets.nodejs]
mounts = [["~/.npm", "/home/.npm", false]]
env_vars = [["NODE_ENV", "development"]]
workdir = "/app"
프리셋 사용하기
# 프리셋으로 세션 생성
bashlet create --name k8s-env --preset kubectl
# 프리셋을 이용한 일회성 명령
bashlet exec --preset kubectl "kubectl get pods"
# 세션이 없으면 자동 생성, 프리셋 적용 후 실행
bashlet run dev -C --preset nodejs "npm install"
백엔드‑특정 프리셋
워크로드를 항상 Firecracker에서 실행하고 싶나요? 프리셋에 backend(및 선택적으로 사용자 정의 rootfs_image)를 추가하세요:
[presets.dev-vm]
backend = "firecracker"
rootfs_image = "~/.bashlet/images/dev.ext4"
env_vars = [["EDITOR", "vim"]]
루트 파일 시스템에 대한 변경 사항은 세션 간에 지속됩니다—패키지를 한 번 설치하고 영원히 재사용하세요.
설치
Bashlet은 GitHub에서 사용할 수 있습니다. 단일 스크립트로 (Wasmer와 Linux용 Firecracker를 포함하여) 약 30 초 만에 설치됩니다:
curl -fsSL https://bashlet.dev/install.sh | sh
더 큰 그림
LLM이 코딩 능력을 향상시키면서, filesystem primitives 위에 구축된 에이전트도 자동으로 더 좋아집니다. 모델이 훈련된 도구를 활용함으로써—지속적인 유지보수가 필요한 맞춤형 도구와 싸우는 대신—에이전트 아키텍처를 간단하고 빠르며 안전하게 유지할 수 있습니다.
에이전트에게 훈련받은 도구를 제공하세요.