−25%

Windows 연간 결제, 10월 31일까지. 요금제 보기

EQVPS
시작하기

샌드박스에서 첫 에이전트 작업: LLM이 코드를 쓰고 microVM이 실행한다

AI 코드 실행의 가장 작은 쓸모 있는 루프를 만듭니다. 모델이 Python 스크립트를 쓰고, Firecracker 샌드박스가 실행하고, 오류는 스크립트가 동작할 때까지 모델에게 돌아갑니다. 약 40줄이며, 코드 없이 MCP로 같은 작업을 하는 방법도 담았습니다.

코드를 쓰는 에이전트는 그 코드를 안전하게 실행하고 결과를 알려 주는 무언가가 있어야 쓸모가 있습니다. 이 가이드는 그 루프를 처음부터 끝까지 만듭니다. 모델이 Python 스크립트를 쓰고, 샌드박스가 실행하고, 실패하면 오류가 모델에게 돌아갑니다. 그다음 같은 작업을 코드 한 줄 없이 MCP로 해 봅니다.

Python 3.8+, EQVPS 계정 토큰(계정 연결 참고), 그리고 모델의 API 키가 필요합니다.

1. 먼저 지출 한도 설정하기

에이전트는 샌드박스를 스스로 만드므로 시작 전에 상한을 정해 두세요. 실험이라면 하루 2달러, 한 달 20달러면 충분합니다.

curl -X PUT https://api.eqvps.com/api/v1/eqvps/sandboxes/budget \
  -H "Authorization: Bearer $EQVPS_API_KEY" -H "Content-Type: application/json" \
  -d '{"daily_usd": 2, "monthly_usd": 20}'

한도에 도달하면 새 샌드박스는 429 budget_exceeded를 받고, 실행 중인 임시 샌드박스는 삭제됩니다. 자세한 내용은 샌드박스 한도와 과금에 있습니다.

2. 라이브러리 두 개 설치

pip install eqvps anthropic
export EQVPS_API_KEY="your-eqvps-token"
export ANTHROPIC_API_KEY="your-model-key"

예제는 한 곳의 모델 제공자를 쓰지만, 그에 의존하는 부분은 없습니다. 모델과 대화하는 것은 ask() 함수뿐입니다.

3. 작성-실행-수정 루프

아래 내용을 agent.py로 저장합니다.

import re
import anthropic
from eqvps import Sandbox

TASK = "Count the prime numbers below 1,000,000 and print only the number."
client = anthropic.Anthropic()

def ask(messages):
    msg = client.messages.create(model="claude-sonnet-5", max_tokens=2000, messages=messages)
    return msg.content[0].text

def extract_code(text):
    m = re.search(r"`{3}(?:python)?\n(.*?)`{3}", text, re.S)
    return m.group(1) if m else text

messages = [{"role": "user", "content": "Write a Python 3 script for this task. "
             "Reply with a single code block and nothing else.\n\nTask: " + TASK}]

with Sandbox.create(tariff="small", idle_timeout=120) as sb:
    for attempt in range(1, 4):
        reply = ask(messages)
        r = sb.run(extract_code(reply), timeout=55)
        print(f"attempt {attempt}: exit code {r.exit_code}")
        if r.ok:
            print(r.stdout.strip())
            break
        messages += [
            {"role": "assistant", "content": reply},
            {"role": "user", "content": f"The script failed with exit code {r.exit_code}.\n"
             f"stderr:\n{r.stderr[-3000:]}\nFix it. Reply with a single code block."},
        ]

실행합니다.

python3 agent.py

일반적인 실행에서는 attempt 1: exit code 0에 이어 78498이 출력됩니다. 첫 스크립트가 실패하면 오류를 고친 두 번째 시도가 보입니다.

각 부분이 하는 일:

  • Sandbox.create(..., idle_timeout=120)는 약 1초 만에 microVM을 띄웁니다. 스크립트가 중간에 죽어도 샌드박스는 2분간 활동이 없으면 스스로 삭제됩니다.
  • sb.run(code, timeout=55)는 코드를 실행하고 종료 코드, stdout, stderr를 반환합니다. 코드가 죽는 것은 예외가 아니라 정상적인 결과이므로 루프가 이를 모델에게 보여 줄 수 있습니다.
  • r.stderr[-3000:]는 오류의 끝부분만 보냅니다. 큰 스크립트의 전체 traceback은 모델의 컨텍스트를 크게 낭비할 수 있습니다.
  • 시도는 최대 3번이라는 것은 의도된 제한입니다. 세 번 안에 스크립트를 고치지 못한 모델에게 필요한 것은 대개 네 번째 시도가 아니라 더 나은 작업 설명입니다.

4. 작업이 55초보다 오래 걸릴 때

동기 호출은 최대 55초입니다. 더 긴 작업은 백그라운드로 시작하고 기다리세요.

task = sb.run(code, background=True)
result = task.wait(timeout=1800)

wait가 타임아웃에 걸려도 작업은 계속 실행되며, sb.task(task_id)로 다시 이어서 다룰 수 있습니다.

5. 같은 작업을 MCP로, 코드 없이

Claude Desktop이나 Cursor 같은 MCP 클라이언트에서 EQVPS MCP 서버를 쓰고 있다면, 에이전트에게는 이미 샌드박스 도구가 있습니다. create_sandbox, run_code, exec_command, upload_file, download_file, kill_sandbox입니다. 프롬프트 하나면 됩니다.

작은 임시 샌드박스를 만들어 줘. 1,000,000 미만의 소수 개수를 세는 Python 스크립트를 써서 거기서 실행하고, 실패하면 고치고, 결과를 알려 준 다음 샌드박스를 삭제해 줘.

에이전트는 위 스크립트와 같은 호출을 합니다. 어떤 도구를 확인 없이 허용해도 되는지는 MCP 보호 장치에서 다룹니다.

든 비용

샌드박스는 1분보다 훨씬 짧게 존재했고 최소 60초로 과금되었습니다. small에서 0.00055달러입니다. 모델 호출이 샌드박스보다 더 비쌉니다. 새 계정의 1달러 체험 크레딧이면 이런 실행을 약 1,800번 할 수 있습니다.

다음으로 볼 것

자주 묻는 질문

모델이 만든 코드를 그냥 내 컴퓨터에서 돌리면 안 되나요?

무엇을 할지 모르기 때문입니다. 생성된 코드는 파일을 지우거나, 키를 읽거나, 끝없이 돌 수 있습니다. 샌드박스에서는 자체 커널을 가진 별도의 microVM에서 실행되고, 끝나면 샌드박스가 삭제됩니다.

예제의 모델 말고 다른 모델로도 되나요?

됩니다. 루프에 필요한 것은 메시지를 받아 텍스트를 돌려주는 함수 하나뿐입니다. ask() 함수를 로컬 모델을 포함한 어떤 채팅 API로든 바꾸면 됩니다.

에이전트가 샌드박스를 끝없이 만드는 것은 어떻게 막나요?

시작하기 전에 일일·월간 지출 한도를 설정하세요. 한도에 도달하면 새 샌드박스는 429 budget_exceeded로 거부됩니다. 또한 한 계정에서는 명령이 동시에 두 개까지만 실행되므로 폭주한 루프가 불어날 수 없습니다.

생성된 코드가 인터넷에 접속할 수 있나요?

네. 코드가 패키지를 설치하고 데이터를 받을 수 있도록 샌드박스는 외부 인터넷에 접속할 수 있습니다. 들어오는 포트는 없습니다. 프롬프트나 코드에 비밀 정보를 넣지 말고, 스크립트에 꼭 필요한 것만 환경 변수로 전달하세요.

댓글

아직 댓글이 없습니다. 첫 번째가 되세요.

댓글 남기기

댓글은 표시되기 전에 검토됩니다.