AI Sparkup

최신 AI 쉽게 깊게 따라잡기⚡

에이전트 하네스를 샌드박스 밖에 두면 뭐가 달라지나

여러 엔지니어가 같은 에이전트를 공유하는 순간, “노트북에서 잘 돌아가던” 아키텍처가 조용히 무너지기 시작합니다. AI 에이전트 인프라 스타트업 Mendral이 이 문제를 직접 겪으며 정리한 설계 이야기입니다.

사진 출처: Mendral

출처: The Agent Harness Belongs Outside the Sandbox – Mendral

에이전트 하네스란 무엇인가

에이전트 하네스(agent harness)는 LLM을 구동하는 루프입니다. 프롬프트를 보내고, 응답을 받고, 모델이 요청한 도구를 실행하고, 결과를 다시 모델에 넘기는 과정을 반복하는 것이죠. 모든 프로덕션 에이전트에는 이 루프가 있습니다. 핵심 질문은 이 루프를 어디에서 실행하느냐입니다.

두 가지 선택지가 있습니다.

하네스를 샌드박스 안에 두는 방식은 루프가 코드가 실행되는 컨테이너 안에 함께 존재합니다. LLM 호출, 도구 실행, 스킬·메모리 파일 모두 같은 컨테이너 안에서 처리되죠. Claude Code를 노트북에서 실행할 때가 바로 이 방식입니다. 단일 사용자 환경에서는 단순하고 잘 작동합니다.

하네스를 샌드박스 밖에 두는 방식은 루프가 백엔드에서 실행됩니다. 도구를 실행할 때만 샌드박스에 API로 요청을 보내고, 결과를 받아옵니다. 루프 자체는 샌드박스에 진입하지 않습니다.

밖에 두면 뭐가 좋은가

Mendral은 수십 명의 엔지니어가 같은 에이전트를 공유하는 멀티유저 환경을 운영합니다. 그 환경에서 바깥 방식이 주는 이점은 세 가지입니다.

자격증명이 샌드박스 밖에 머문다. LLM API 키, 사용자 토큰, 데이터베이스 접근 정보는 루프가 가집니다. 샌드박스에는 작업에 필요한 환경만 존재하고, 탈취할 자격증명 자체가 없습니다.

샌드박스를 필요할 때만 켤 수 있다. 에이전트가 하는 일의 상당 부분—생각하기, API 호출, CI 대기—은 샌드박스가 없어도 됩니다. 하네스가 밖에 있으면 명령 실행이 필요한 순간에만 샌드박스를 켜고, 그 외엔 suspend 상태로 둘 수 있습니다. Mendral은 Blaxel을 사용해 25ms 안에 재개(resume)합니다. 에이전트 입장에서는 샌드박스가 꺼져 있었는지 알 수 없는 수준입니다.

샌드박스가 죽어도 세션이 살아남는다. 루프가 밖에 있으면 샌드박스는 교체 가능한 부품이 됩니다. 하나가 죽으면 새로 프로비저닝하고 계속 진행합니다. 루프가 안에 있을 땐 샌드박스가 곧 세션이기 때문에, 샌드박스가 죽으면 세션 전체가 날아갑니다.

가장 까다로운 문제: 파일시스템

Claude Code 같은 현대 에이전트 하네스는 파일시스템을 전제로 설계되어 있습니다. 스킬은 .claude/skills/foo.md에, 메모리는 .claude/memory/MEMORY.md에 저장됩니다. 에이전트는 소스코드를 다루는 것과 똑같은 read/write 도구로 이것들을 읽고 씁니다.

노트북에서는 자연스럽습니다. 하지만 하네스가 샌드박스 밖으로 나오는 순간 두 가지 문제가 생깁니다.

첫째, 샌드박스는 일시적입니다. 에이전트가 메모리 파일에 뭔가를 썼는데 샌드박스가 교체되면, 그 파일은 사라집니다.

둘째, 멀티유저 환경에서는 분산 파일시스템 문제가 됩니다. 같은 조직의 엔지니어 세 명이 동시에 에이전트를 쓰면, 각자의 샌드박스가 같은 메모리 파일에 쓰려 합니다. 충돌 해소, 정합성 보장, 캐시 무효화가 줄줄이 따라옵니다.

Mendral의 해법: 파일시스템 가상화

Mendral은 파일인 척 하는 것을 포기하는 대신, 파일처럼 보이게 만들기로 했습니다.

하네스가 파일 접근을 중간에서 가로채서 경로에 따라 라우팅합니다.

  1. /workspace/ 아래 경로 → 샌드박스로 RPC 전송 (코드 파일, 기존 방식 그대로)
  2. /skills/, /memory/ 아래 경로 → Postgres 데이터베이스로 SQL 쿼리

에이전트 입장에서는 파일을 읽고 쓰는 것처럼 보입니다. 실제로는 메모리 쓰기가 데이터베이스 트랜잭션이고, 같은 조직의 다른 세션은 그 즉시 동일한 메모리를 읽습니다.

“그냥 memory_read, memory_write 같은 도구를 따로 추가하면 되지 않나”라는 질문이 자연스럽게 나옵니다. Mendral도 처음엔 그렇게 했습니다. 문제는 도구가 늘어날수록 모델의 주의가 분산되고, 비슷한 기능의 도구(readmemory_read) 사이에서 모델이 잘못 선택하는 일이 생긴다는 점입니다.

더 본질적인 이유도 있습니다. Anthropic을 비롯한 프론티어 모델 개발사들은 Claude Code와 유사한 인터페이스로 강화학습을 진행합니다. 그 인터페이스는 read(path), write(path, content), edit(path, old, new)입니다. 이 API 표면을 그대로 유지할수록 모델이 학습한 대로 잘 작동합니다. 인터페이스를 바꾸는 순간 모델은 학습되지 않은 영역에서 동작하게 됩니다.

아직 풀리지 않은 것들

Mendral도 솔직하게 인정합니다. 완벽하지 않은 부분이 남아 있습니다.

bash 도구는 가상화 레이어를 우회합니다. 에이전트가 bash/skills/ 경로에 접근하면, 거기엔 아무것도 없습니다. 시스템 프롬프트로 에이전트에게 알리고 tree-sitter로 bash 호출을 파싱해 막으려 하지만 완벽하지 않습니다.

정합성 문제도 남아 있습니다. 두 세션이 동시에 같은 메모리를 업데이트하면 어떻게 되는가. 현재는 키당 마지막 쓰기가 이기는(last-writer-wins) 방식으로 처리합니다.

그리고 Claude Code의 규약이 계속 바뀐다는 점도 과제입니다. 새로운 패턴(서브에이전트, 플랜, 백그라운드 태스크)이 나올 때마다 로컬 파일시스템을 전제로 설계되어 있고, 가상화 레이어가 따라잡는 데 항상 시차가 생깁니다.

단일 사용자 에이전트를 만든다면 이 고민은 대부분 불필요합니다. 하지만 팀 단위로 에이전트를 공유하는 순간, 아키텍처의 선택이 달라져야 하는 이유를 Mendral의 경험이 잘 보여줍니다.

참고자료: Mendral – LLMs are good at SQL


AI Sparkup 구독하기

최신 게시물 요약과 더 심층적인 정보를 이메일로 받아 보세요! (무료)

Comments

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다