출력 손상을 수정하고 영속성을 구현했으며 TTL을 추가했습니다. 모두 v0.6에 포함되었습니다.
출처: Dev.to
신뢰할 수 없는 AI가 생성한 코드를 안전하게 실행하는 것은 명백히 어려운 문제입니다.
하지만 때때로 에이전트 워크플로우를 방해하는 문제들은 지루한 인프라 작업처럼 보이기도 합니다.
v0.6은 배관 작업으로 시작되었습니다:
- 영구적인 샌드박스 레지스트리
- TTL을 이용한 자동 정리
필요하지만 특별히 매력적인 작업은 아니었습니다.
그런데 테스트가 실패하기 시작했습니다.
출력 손상 문제
모든 실행이 다음과 같은 결과를 반환했습니다:
WARNING: Running pip as the 'root' user can result in broken permissions...
[notice] A new release of pip is available: 25.0.1 -> 26.1.2
hello world
실제 프로그램 출력은 의존성 설치 소음에 파묻혀 있었습니다.
터미널을 보는 사람에게는 짜증나는 일입니다.
하지만 실행 출력을 파싱하는 AI 에이전트에게는 완전히 깨진 상태입니다.
원인은 간단했습니다: 의존성 설치와 코드 실행이 하나의 Docker 호출에 연결돼 있었고, stderr가 stdout으로 리다이렉트되었습니다.
모든 것이 같은 스트림에 섞여 버린 것이죠.
해결책: Docker 호출을 두 번, 한 번이 아니라
우리는 작업을 분리했습니다.
호출 1: 의존성을 조용히 설치합니다.
subprocess.run(
[...dependency_install_command],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
호출 2: 사용자 명령을 실행하고 출력을 캡처합니다.
result = subprocess.run(
[...execution_command],
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
)
return result.stdout
작은 변화이지만 원칙은 중요합니다:
AI 에이전트를 위해 인프라를 구축할 때, 깨끗한 출력은 API 계약의 일부입니다.
에이전트는 반환된 내용을 파싱합니다. 설치 로그, 경고 및 런타임 출력은 하나의 구분되지 않은 스트림으로 취급될 수 없습니다.
영속성: 메모리 내 딕셔너리 대신 SQLite
원래 샌드박스 레지스트리는 파이썬 딕셔너리였습니다.
서비스를 재시작하면 모든 샌드박스 레코드가 사라졌습니다.
컨테이너는 여전히 존재할 수 있지만, Jhansi는 더 이상 이를 알지 못했습니다. 서비스 재시작 후 재연결을 기대하는 어떤 에이전트 워크플로우도 실패하게 됩니다.
우리는 다음을 고려했습니다:
- JSON: 간단하지만 부분 쓰기와 충돌 시 손상에 취약
- Redis: 기본 TTL 지원 및 좋은 운영 모델 제공, 하지만 자체 호스팅을 위한 또 다른 서비스 필요
- SQLite: 내구성 있고 트랜잭션을 지원하며 파이썬에 기본 포함
우리는 SQLite를 선택했습니다.
스키마는 의도적으로 작게 설계했습니다:
CREATE TABLE IF NOT EXISTS sandboxes (
id TEXT PRIMARY KEY,
language TEXT NOT NULL,
container_id TEXT,
workspace_path TEXT,
status TEXT NOT NULL,
created_at TEXT NOT NULL,
expires_at TEXT NOT NULL
);
ORM도 없고, 마이그레이션 프레임워크도 없습니다.
그냥 SQLite가 잘하는 일을 그대로 수행하게 한 겁니다.
TTL: 생성 시점이 아니라 마지막 활성 시점 기준
각 샌드박스는 expires_at 값을 가지며, 초기값은 생성 후 1시간 뒤로 설정됩니다.
중요한 결정은 모든 실행이 시계를 리셋한다는 점입니다:
new_expires = (
datetime.now(timezone.utc)
+ timedelta(seconds=TTL_SECONDS)
)
registry.update_expires_at(
sandbox_id,
new_expires,
)
백그라운드 작업이 60초마다 실행되어 만료된 샌드박스를 제거합니다.
이렇게 하면 TTL이 활동 기반이 되고, 시간 기반이 아닙니다.
에이전트가 20분 분석 동안 수십 번의 작은 실행을 수행할 수 있습니다. 생성 시점 기반 TTL은 활성 워크플로우 중에 샌드박스를 종료시킬 수 있지만, 마지막 활성 시점 기반 TTL은 그렇지 않습니다.
활성 샌드박스는 계속 사용할 수 있고, 유휴 샌드박스만 정리됩니다.
이것이 열어주는 가능성
영속성과 활동 기반 TTL 덕분에 Jhansi 샌드박스는 신뢰할 수 있는 실행 프리미티브가 되고 있습니다:
- 샌드박스를 한 번 생성한다.
- 필요할 때마다 반복해서 사용한다.
- 서비스 재시작에도 살아남는다.
- 활성 작업이 에이전트 아래에서 사라지지 않는다.
이것이 장기 실행 에이전트 워크플로우가 필요로 하는 기반입니다.
v0.7에서 기대할 수 있는 다음 기능: Server‑Sent Events를 통한 스트리밍 실행.
전체 명령이 끝날 때까지 출력을 기다릴 필요가 없습니다.
Jhansi는 AI가 생성한 코드를 안전하게 실행할 수 있는 오픈소스 클라우드 샌드박스입니다.
다음 명령으로 자체 호스팅할 수 있습니다:
docker compose up
AI 에이전트에게 필요한 것은 실행이며, 자격 증명이 아닙니다.
이 문제가 공감된다면 스타를 눌러 주세요: https://github.com/jhansi-io/petri