파일을 도구보다 앞에: Knock Agent의 가상 파일시스템과 bash 설계
Knock의 Chris Bell이 쓴 “Files over tools: how we built the Knock Agent using a virtual file system and bash”를 바탕으로, 제품 내 AI Agent를 어떻게 설계했는지 한국어로 요약·해설했다.
핵심은 “도구를 많이 주는 것”이 아니라, Agent가 탐색하고 수정할 수 있는 작업 공간을 주는 것이다.
Knock는 2026년 3월, 고객 메시징 리소스를 관리하는 Knock Agent를 출시했다. 이 Agent는 워크플로, 템플릿, 오디언스, 세그먼트, 성과 질문 등을 다룬다. 대시보드, Slack, API, MCP 서버에서 호출할 수 있다.
흥미로운 점은 Agent의 중심 설계다. Knock는 관리 API의 기능을 하나하나 tool로 노출하는 대신, 계정의 리소스를 가상 파일시스템으로 표현하고, Agent가 bash로 그 위를 탐색·수정하게 했다.
문제: tool-per-type은 금방 커진다
초기 프로토타입은 워크플로 전용이었다. 예를 들어 “워크플로에 delay step 추가”, “이메일 템플릿을 블록 언어로 작성” 같은 management API primitive를 각각 tool로 만들었다. 각 tool 설명에는 사용법, 예외, 예시, 입력 필드가 들어갔다.
이 방식은 작동했다. 하지만 확장성이 낮았다. Knock의 모든 리소스를 다루려면 management API의 리소스마다 tool이 필요해지고, Agent의 context window는 tool 설명으로 비대해진다. 별도의 tool router를 만들거나 Agent용 스크립팅 언어를 도입하지 않으면 유지하기 어려운 구조였다.
전환: 적은 tool, 더 많은 file
Knock가 참고한 아이디어는 Vercel의 “filesystem and bash” Agent 패턴이다. 도메인 객체를 파일시스템에 매핑하면, Agent는 익숙한 방식으로 탐색할 수 있다. ls, read_file, 검색, 작은 스크립트가 곧 context retrieval 수단이 된다.
Knock에는 이미 CLI가 있었다. 고객은 Knock 리소스를 로컬로 내려받아 템플릿과 컴포넌트를 파일로 다룰 수 있었다. 그래서 Agent 설계도 자연스럽게 같은 철학으로 갔다.
가상 bash와 가상 파일시스템
Vercel의 just-bash는 Linux 이미지를 띄우지 않고도 Agent에게 Unix 스타일 파일시스템과 bash 환경을 제공하려는 시도였다. Knock는 Elixir 기반 회사였기 때문에 TypeScript 구현을 그대로 쓰기보다 Elixir 버전의 bash interpreter와 virtual filesystem을 만들었다.
이 작업은 agentic coding에 잘 맞는 목표였다. 지원해야 할 command 표면이 명확하고, jq, ls, cat 같은 명령에 대한 test suite가 있었기 때문이다. 결과적으로 Knock는 Elixir 생태계에서 동작하는 just-bash와 보안 모델, 테스트 세트를 갖추게 됐다.
왜 full sandbox가 아니었나
당연한 질문이 있다. Agent에게 진짜 컴퓨터, 즉 full sandbox를 주면 되지 않을까?
Knock의 판단은 “지금 단계에서는 과하다”였다. sandbox를 시작하는 데는 성능 비용이 있고, 앱 밖에서 변경된 내용과 내부 상태를 동기화하는 문제도 생긴다. 지금 필요한 것은 대부분 in-memory virtual filesystem과 bash로 충분했다.
다만 이 결정은 영구적인 것이 아니다. Agent가 Python 같은 더 풍부한 scripting language를 직접 써야 하는 단계가 오면, virtual sandbox를 real sandbox로 바꿀 수 있다. 중요한 것은 “brain”과 “hands”를 분리해 두는 것이다. Agent loop는 그대로 두고 실행 환경만 교체할 수 있어야 한다.
구조: lazy-loaded filesystem-backed agent
Knock Agent는 Anthropic API 위에서 일반적인 agent loop를 돌린다. 이 loop는 Elixir의 Oban을 이용한 durable workflow 위에 있고, 상태는 Postgres에 저장된다. 세션 안에는 sandbox가 붙는다. 이 sandbox는 Elixir cluster 안에서 실행되는 프로세스이며, 내부에 just-bash instance와 virtual filesystem을 가진다.
filesystem은 처음부터 모든 큰 리소스를 다 싣지 않는다. 먼저 계정의 메타데이터와 catalog를 올린다.
|- account.json # account context
|- channels.json # available channels
|- workflows.json # workflow catalog
|- ... # other resources
Agent가 특정 workflow나 template을 자세히 다뤄야 하면 load_resources tool로 필요한 리소스를 bulk load한다.
workflows/
some-workflow/
workflow.json
steps/
email_1/
template.html
in_app_1/
template.md
세션의 sandbox는 오래 살아 있다. 따라서 여러 턴에 걸쳐 워크플로와 템플릿을 함께 수정하는 대화에서 이미 불러온 context를 계속 활용할 수 있다.
Agent에게 준 핵심 tool
Knock가 노출한 tool은 많지 않다.
bash: bash command 실행read_file: 파일 읽기.cat보다 선호edit_file: 문자열 치환 기반의 targeted editwrite_file: 파일 전체 쓰기upsert_resource: filesystem의 변경을 Knock 계정에 반영
여기서 중요한 것은 upsert_resource다. Agent가 파일을 수정한 뒤, 해당 bundle을 읽어 management API와 같은 내부 경로로 Knock 리소스에 저장한다. 즉, 파일은 작업 공간이고 API는 commit 경로다.
skill: product knowledge를 prompt 밖으로 빼기
처음에는 워크플로와 템플릿에 대한 제품 지식을 system prompt에 넣었다. 하지만 제품 범위가 넓어질수록 이 방식은 지속 불가능하다. 그래서 Knock는 리소스별 지식을 skill로 분리했다.
skills/
workflows/
SKILL.md
references/
template-editing.md
broadcasts/
guides/
각 skill에는 해당 리소스를 다룰 때 필요한 지식, 주의할 함정, 편집 규칙이 들어간다. Agent는 리소스를 수정하기 전에 관련 skill을 읽도록 유도된다. 고객 정의 skill도 같은 구조로 추가할 수 있다.
CLI over tools
모든 데이터를 정적 파일로 표현할 수는 없다. 로그처럼 실행 시점에 질의해야 하는 데이터도 있다. Knock는 이런 영역을 위해 tool을 계속 늘리지 않고, Agent용 CLI command인 knock를 just-bash에 등록했다.
예를 들어 Agent는 knock api_logs list, knock messages list 같은 명령으로 API 로그나 workflow run 로그를 조회한다. CLI가 각 resource에 --help를 제공하면 Agent는 추가 설명 없이도 사용법을 탐색할 수 있다.
이 대목이 중요하다. 제품 기능을 Agent tool로 노출할지, CLI로 노출할지는 단순한 구현 취향이 아니다. CLI는 사람이 쓰는 운영 표면과 Agent가 쓰는 운영 표면을 하나로 합친다. 문서화, 권한, 테스트, 디버깅 경로가 겹친다.
관측성과 평가
Knock는 Agent loop의 prompt, action, response를 event log로 남긴다. 이 로그는 메모리에 buffer됐다가 비동기로 Postgres에 저장되고, 다음 턴 context를 위해 압축·포맷된다. 클라이언트에는 NDJSON stream으로 전달된다.
관측성은 OpenTelemetry trace로 구현하고, Honeycomb과 Braintrust로 보낸다. Honeycomb은 서비스 trace 관점, Braintrust는 Agent session 분석 관점에 더 유용하게 쓴다.
평가 시스템은 Anthropic의 Agent eval 글에서 영향을 받은 자체 eval이다. trajectory를 보고, 결과물이 기대 속성이나 형태를 만족하는지 확인하고, LLM-as-judge로 점수화한다. nightly eval을 돌리고 pass^k로 common task의 정확도를 본다.
내가 읽은 핵심
이 글에서 가장 중요한 문장은 “files over tools”다. Agent 제품을 만들 때 흔한 실수는 API endpoint를 전부 tool로 바꾸는 것이다. 하지만 tool이 너무 많아지면 Agent는 사용법을 이해하기 위해 context를 낭비하고, 개발팀은 tool schema를 유지보수하느라 지친다.
파일시스템은 오래된 인터페이스다. 탐색, 읽기, 수정, diff, commit이라는 작업 모델이 이미 있다. Agent는 이 모델에 강하다. 특히 고객 계정처럼 구조화된 도메인 객체가 많고, 일부는 크고, 일부는 지연 로딩해야 하는 제품에서는 파일시스템이 context retrieval layer 역할을 한다.
정리하면 Knock의 설계는 세 가지 원칙으로 볼 수 있다.
- Context는 파일로 발견하게 한다. Agent가 필요한 범위만 탐색하고 읽게 한다.
- Action은 bash/CLI로 통합한다. tool을 무한히 늘리지 말고 composable command surface를 준다.
- Persistence는 명시적 commit으로 분리한다. 파일 수정과 실제 계정 반영을
upsert_resource로 분리한다.
AI Agent 제품을 만들 때의 체크리스트
- 내 도메인 객체를 파일/디렉터리 구조로 표현할 수 있는가?
- 처음부터 전체 데이터를 넣지 않고 catalog만 올린 뒤 lazy load할 수 있는가?
- API endpoint를 tool로 늘리는 대신 CLI command로 묶을 수 있는가?
- Agent가 변경한 파일과 실제 저장 반영을 분리할 수 있는가?
- resource별 규칙을 system prompt가 아니라 skill 파일로 관리할 수 있는가?
- event log, trace, eval을 처음부터 남길 수 있는가?
출처
- Chris Bell, “Files over tools: how we built the Knock Agent using a virtual file system and bash”, Knock, 2026-07-09.
- Vercel, “How to build agents with filesystems and bash”.
- Anthropic, “Decoupling the hands from the brain”.
- Anthropic, “Demystifying evals for AI agents”.