샌드박스 에이전트
샌드박스 에이전트 (Sandbox Agents)
에이전트에게 파일을 다루고, 명령을 실행하고, 상태를 유지하는 '작업 공간'이 필요할 때 쓰는 샌드박스 개념을 다뤄요. 샌드박스가 무엇인지, 언제 써야 하는지, 작업 공간을 어떻게 만들고 이어가고 기억을 남기는지 배워요.
출처: 문서
본문
샌드박스(sandbox)는 에이전트에게 격리된 Unix 계열 실행 환경을 제공해요. 파일시스템, 셸, 설치된 패키지, 마운트된 데이터, 열린 포트, 스냅샷, 그리고 외부 시스템에 대한 통제된 접근까지 모두 갖추고 있죠.
모델이 그런 작업 공간을 필요로 하는데 프롬프트 컨텍스트만 받는다면, 에이전트 워크플로는 금방 깨지기 쉬워져요. 큰 문서 세트나 만들어진 산출물, 명령, 미리보기, 이어서 할 수 있는 작업 같은 것들은 에이전트가 직접 살펴보고 바꿀 수 있는 환경이 필요해요.
샌드박스 에이전트는 TypeScript와 Python Agents SDK에서 모두 사용할 수 있어요.
이 가이드는 Agents SDK에서의 샌드박스를 다뤄요. 여기서는 여러분의 애플리케이션이 실행 루프(harness)를 돌리는 구조예요. OpenAI가 관리하는 harness를 쓰려면 Agents API: 샌드박스 연결하기를 참고하세요.
샌드박스는 에이전트가 파일을 조작하거나, 명령을 실행하거나, 데이터 룸을 마운트하거나, 산출물을 만들거나, 서비스를 노출하거나, 나중에 상태가 있는 작업을 이어갈 때 쓰면 돼요.
harness와 compute의 구분
여기서 핵심은 harness와 compute 사이의 경계예요.
- harness는 모델 주변의 제어 평면(control plane)이에요. 에이전트 루프, 모델 호출, 툴 라우팅, 핸드오프, 승인, 트레이싱, 복구, 실행 상태를 소유하죠.
- compute는 샌드박스 실행 평면이에요. 모델이 지시한 작업이 여기서 파일을 읽고 쓰고, 명령을 실행하고, 의존성을 설치하고, 마운트된 스토리지를 사용하고, 포트를 열고, 상태를 스냅샷으로 남겨요.
이 두 경계를 분리해 두면, 애플리케이션은 민감한 제어 평면 작업을 신뢰할 수 있는 인프라에 두고 샌드박스는 제공자별 실행에 집중할 수 있어요. 샌드박스는 좁은 자격 증명과 마운트로 파일에 대해 코드를 돌릴 수 있고, harness는 인증·결제·감사 로그·사람 검토·복구 상태를 어떤 단일 컨테이너 밖에 보관할 수 있죠.

프로토타입에서는 harness를 샌드박스 안에서 돌리는 게 편리할 수 있지만, 그러면 오케스트레이션과 모델 지시 실행이 같은 compute 경계에 놓여요.

반면 harness를 여러분의 인프라에서 돌리면, 샌드박스가 제공자별·상태 있는 실행을 처리할 수 있어요.
샌드박스를 언제 써야 하나요?
에이전트의 답변이 단순히 프롬프트 컨텍스트를 추론하는 것 이상으로 샌드박스 작업 공간에서 이루어진 작업에 달려 있을 때 샌드박스를 쓰세요.
흔한 어려움은 이렇습니다:
- 작업이 단일 프롬프트가 아니라 문서 디렉터리가 필요해요.
- 에이전트가 나중에 애플리케이션이 살펴볼 수 있게 파일을 써야 해요.
- 작업 완료를 위해 명령, 패키지, 스크립트가 필요해요.
- Markdown, CSV, JSONL, 스크린샷, 생성된 웹사이트 같은 산출물을 만들어요.
- 서비스, 노트북, 리포트 미리보기를 열린 포트에서 실행해야 해요.
- 작업이 사람 검토를 위해 멈췄다가 같은 작업 공간에서 이어져요.
만약 워크플로가 짧은 모델 응답만 필요하고 영속적 작업 공간이 필요 없다면, Responses API를 직접 호출하거나 샌드박스 없는 기본 Agents SDK 런타임을 쓰세요.
셸 접근이 가끔 쓰는 하나의 툴일 뿐이라면, Using tools의 호스팅 셸 툴로 시작하세요. 작업 공간 격리, 샌드박스 제공자 선택, 이어갈 수 있는 파일시스템 상태가 제품 설계의 일부일 때 샌드박스 에이전트를 쓰는 겁니다.
샌드박스가 더해주는 것
SandboxAgent는 여전히 하나의 Agent예요. instructions, prompt, tools, handoffs, MCP 서버, 모델 설정, 구조화된 출력, 가드레일, 훅 같은 일반적인 에이전트 표면을 그대로 유지해요. 바뀌는 건 실행 경계예요. 러너는 파일·명령·포트·제공자별 격리를 소유한 라이브 샌드박스 세션에 대해 에이전트를 준비하죠.
| 구성 요소 | 소유하는 것 | 설계 질문 |
|---|---|---|
SandboxAgent |
에이전트 정의 + 샌드박스 기본값 | 이 에이전트가 무엇을 해야 하고, 어떤 샌드박스 기본값이 따라다녀야 할까요? |
Manifest |
새 세션 작업 공간 계약 | 어떤 파일·디렉터리·repo·마운트·환경·사용자·그룹이 작업 공간에 처음부터 있어야 할까요? |
| Capabilities | 에이전트에 붙은 샌드박스 고유 동작 | 이 에이전트가 어떤 샌드박스 툴·지시·런타임 동작을 필요로 할까요? |
| 샌드박스 클라이언트 | 제공자 통합 | 라이브 작업 공간이 어디서 돌아야 할까요: Unix-local, Docker, 아니면 호스팅 제공자? |
| 샌드박스 세션 | 라이브 실행 환경 | 명령은 어디서 실행되고, 파일은 어디서 바뀌고, 포트는 어디서 열리고, 제공자 상태는 어디에 살까요? |
| 샌드박스 실행 설정 | 실행별 세션 원본·클라이언트 옵션·새 입력 | 이 실행이 샌드박스 세션을 주입하거나, 이어나가거나, 새로 만들어야 할까요? |
| 저장된 상태 | RunState, 직렬화된 세션 상태, 스냅샷 |
이후 실행이 작업에 어떻게 다시 연결하거나 새 작업 공간을 어떻게 시드할까요? |
샌드박스 고유 기본값은 SandboxAgent에 속하고, 실행별 샌드박스 세션 선택은 해당 실행의 샌드박스 설정에 속해요.
샌드박스 에이전트는 턴(turn)의 의미를 바꾸지 않아요. 턴은 여전히 모델 단계이지, 단일 셸 명령이나 샌드박스 동작이 아니에요. 일부 작업은 샌드박스 실행 계층 안에 머물 수 있지만, 에이전트 런타임은 샌드박스 작업이 일어난 뒤 다시 모델 응답이 필요할 때만 다음 턴을 소비해요.
작업 공간 만들기
Manifest는 새 샌드박스 작업 공간에 원하는 시작 내용과 배치를 서술해요. 에이전트가 봐야 할 파일, repo, 입력 산출물, 헬퍼 파일, 마운트, 출력 디렉터리, 환경 설정을 여기에 담으세요.
매니페스트를 새 세션 계약으로 다뤄야 해요. 모든 라이브 샌드박스의 완전한 진실의 원천은 아니예요. 실행의 유효 작업 공간은 재사용된 라이브 샌드박스 세션이나 직렬화된 샌드박스 세션 상태, 실행 시 선택된 스냅샷에서 올 수도 있죠.
매니페스트 항목 경로는 작업 공간 상대 경로예요. 절대 경로가 될 수 없고 ..로 작업 공간을 벗어날 수도 없어요. 덕분에 로컬·Docker·호스팅 클라이언트 전반에서 작업 공간 계약을 휴대(portable)하게 유지할 수 있어요.
| 매니페스트 입력 | 용도 |
|---|---|
File, Dir |
작은 합성 입력, 헬퍼 파일, 출력 디렉터리. |
| 로컬 파일 / 디렉터리 | 샌드박스로 옮길 호스트 파일이나 디렉터리. |
| Git repo | 작업 공간으로 가져올 저장소. |
S3Mount, GCSMount, R2Mount, AzureBlobMount, BoxMount, S3FilesMount |
샌드박스 안에서 쓸 수 있게 할 외부 스토리지. |
environment |
샌드박스가 시작할 때 필요한 환경 변수. |
users와 groups |
계정 프로비저닝을 지원하는 제공자를 위한 샌드박스 로컬 OS 계정·그룹. |
좋은 매니페스트 설계는 이러합니다:
- repo, 입력 산출물, 출력 디렉터리를 매니페스트에 넣으세요.
- 더 긴 작업 명세와 repo 로컬 지시는
repo/task.md나AGENTS.md같은 작업 공간 파일에 넣으세요. - 지시에서는
repo/task.md나output/report.md같은 상대 작업 공간 경로를 쓰세요. - 마운트된 스토리지는 에이전트가 읽거나 써야 할 입력으로 한정하세요.
- 마운트 항목을 임시 작업 공간 항목으로 취급하세요. 스냅샷·영속화 흐름은 마운트된 원격 스토리지를 저장된 작업 공간 내용으로 복사하지 않고 건너뜁니다.
파일과 스토리지 마운트
유용한 데이터는 이미 다른 곳에 있는 경우가 많아요. 큰 문서를 컨텍스트에 붙여넣는 대신 샌드박스에 마운트하고 에이전트가 파일로 작업하게 하세요.
예를 들면:
- 실사(due-diligence) 데이터 룸을 마운트하고 인용된 요약을 만들어 달라고 요청하세요.
- 지원 내보내기를 마운트하고 이슈를 리포트로 묶어 달라고 요청하세요.
- 생성된 산출물을 마운트해서 다른 시스템이 검토하게 하세요.
제공자 통합은 각자의 마운트 헬퍼, 자격 증명 처리, 영속화 동작을 노출해요. 애플리케이션 계약은 동일하게 유지하세요. 에이전트가 써야 할 입력만 마운트하고, 어디서 읽고 쓰면 되는지 알려주고, 산출물을 쓰기 전에 확인하는 겁니다.
시크릿과 자격 증명 다루기
샌드박스 자격 증명은 프롬프트 내용이 아니라 런타임 설정으로 취급하세요. 에이전트가 패키지 매니저, 스토리지 마운트, 제공자 API용 자격 증명이 필요할 수는 있지만, 그 자격 증명이 사용자 프롬프트, 에이전트 지시, 작업 파일, 커밋된 매니페스트, 생성된 산출물에 나타나면 안 돼요.
규칙을 정리하면:
- 호스팅 샌드박스 제공자에서는 제공자 고유 시크릿 시스템을 우선하세요.
- 클라우드 스토리지 자격 증명은 그게 필요한 마운트·제공자 옵션에 한정하세요.
- 샌드박스 프로세스가 시작 시 필요로 하는 값에는
Manifest.environment를 쓰고, 지우고 다시 만들고 싶은 민감·생성 항목은 임시(ephemeral)로 표시하세요. - 시크릿, 생성된 마운트 설정, 로컬 토큰, 실행 후 남으면 안 되는 파일 저장을 피하세요.
- 특히 에이전트가 비공개 문서나 마운트된 스토리지를 읽을 수 있을 때는 샌드박스 밖으로 옮기기 전에 산출물을 검토하세요.
SDK는 매니페스트 환경 값과 제공자별 마운트 자격 증명을 지원해요. 일반 시크릿 저장소 통합은 제공자별이라, 이 페이지는 계약에 집중해요. 즉 여러분의 런타임이나 샌드박스 제공자가 지시로 모델에게 가르치는 대신 자격 증명을 주입해야 한다는 원칙이에요.
에이전트에 기능(Capability) 부여하기
Capabilities는 샌드박스 고유 동작을 SandboxAgent에 붙입니다. 실행이 시작되기 전에 작업 공간을 만들거나, 샌드박스별 지시를 붙이거나, 라이브 샌드박스 세션에 묶이는 툴을 노출하거나, 그 에이전트의 모델 동작·입력 처리를 조정할 수 있어요.
| Capability | 언제 추가하나요 | 참고 |
|---|---|---|
Shell |
에이전트가 셸 접근이 필요할 때. | 명령 실행을 더하며, 샌드박스 클라이언트가 지원하면 대화형 입력도 지원. |
Filesystem |
에이전트가 파일을 편집하거나 로컬 이미지를 확인해야 할 때. | apply_patch와 view_image를 추가. 패치 경로는 작업 공간 루트 상대 경로. |
Skills |
샌드박스에서 스킬 발견·자료화를 원할 때. | .agents나 .agents/skills를 수동 마운트하는 것보다 이걸 우선하세요. |
Memory |
후속 실행이 메모리 산출물을 읽거나 생성해야 할 때. | Shell이 필요. 라이브 메모리 업데이트는 Filesystem도 필요. |
Compaction |
장시간 흐름에서 컨텍스트 정리가 필요할 때. | 컴팩션 항목 뒤의 모델 동작·입력 처리를 조정. |
기본적으로 SandboxAgent는 filesystem, shell, compaction 기능을 포함해요. capabilities 목록을 전달하면 기본 목록을 대체하므로, 에이전트가 여전히 필요한 기본 기능을 포함해야 해요.
내장 기능이 맞을 때는 그걸 우선 쓰세요. 내장 기능이 다루지 못하는 샌드박스별 툴·지시 표면이 필요할 때만 커스텀 capability를 작성해야 해요.
스킬 불러오기
일부 작업은 에이전트가 시작하기 전에 반복 가능한 지시, 스크립트, 참조, 자산이 필요해요. Skills capability를 써서 에이전트가 실행 중에 그 작업 컨텍스트를 발견하게 하세요.
스킬 불러오기
import {
Capabilities,
SandboxAgent,
gitRepo,
skills,
} from "@openai/agents/sandbox";
const agent = new SandboxAgent({
name: "Tax prep assistant",
instructions: "Use the mounted skill before preparing the return.",
capabilities: [
...Capabilities.default(),
skills({
from: gitRepo({
repo: "owner/tax-prep-skills",
ref: "main",
}),
}),
],
});
from agents.sandbox import SandboxAgent
from agents.sandbox.capabilities import Capabilities, Skills
from agents.sandbox.entries import GitRepo
agent = SandboxAgent(
name="Tax prep assistant",
instructions="Use the mounted skill before preparing the return.",
capabilities=Capabilities.default()
+ [
Skills(from_=GitRepo(repo="owner/tax-prep-skills", ref="main")),
],
)
스킬 원본(source)은 어떻게 자료화할지에 따라 고르세요:
- 큰 로컬 스킬 디렉터리라서 모델이 먼저 인덱스를 발견하고 필요한 것만 불러오게 하고 싶다면 지연(lazy) 로컬 디렉터리 원본을 쓰세요.
- 작은 로컬 번들을 미리 올리고 싶다면 로컬 디렉터리 원본을 쓰세요.
- 스킬 번들에 자체 릴리스 주기가 있거나 많은 샌드박스가 쓴다면 Git repo 원본을 쓰세요.
미리보기와 포트 노출
때로 산출물이 파일이 아니라 실행 중인 프로세스예요. 에이전트가 로컬 앱, 노트북, 리포트 서버, 브라우저 미리보기, 그 외 샌드박스 밖에서 확인해야 할 서비스를 만들 때는 노출된 포트를 쓰세요.
포트 설정은 제공자별이지만 제품 계약은 같아요. 에이전트가 샌드박스 안에서 서비스를 시작하고, 샌드박스 클라이언트가 포트를 노출하며, 애플리케이션이 결과 미리보기 URL을 공유하거나 확인하는 겁니다.
샌드박스 에이전트 실행하기
가장 짧은 유용한 샌드박스 루프는 이렇게 진행돼요:
- 작업 공간을 서술하는
Manifest를 만든다. - 모델이 필요로 하는 기능을 갖춘
SandboxAgent를 만든다. - 작업이 돌아야 할 환경에 맞는 샌드박스 클라이언트를 고른다.
- 실행별 샌드박스 설정으로 에이전트를 실행한다.
- 애플리케이션에 중요한 산출물을 확인·복사·재개·스냅샷한다.
로컬 개발(macOS나 Linux)에서는 Unix-local로 시작하세요. 러너가 에이전트의 기본 매니페스트에서 임시 작업 공간을 만들고 실행 후 정리할 수 있어서 가장 작은 로컬 루프를 얻을 수 있어요.
Unix-local 샌드박스 에이전트 실행하기
import { run } from "@openai/agents";
import { Manifest, SandboxAgent, file, shell } from "@openai/agents/sandbox";
import { UnixLocalSandboxClient } from "@openai/agents/sandbox/local";
const manifest = new Manifest({
entries: {
"account_brief.md": file({
content:
"# Northwind Health\n\n" +
"- Segment: Mid-market healthcare analytics provider.\n" +
"- Renewal date: 2026-04-15.\n",
}),
"implementation_risks.md": file({
content:
"# Delivery risks\n\n" +
"- Security questionnaire is not complete.\n" +
"- Procurement requires final legal language by April 1.\n",
}),
},
});
const agent = new SandboxAgent({
name: "Renewal Packet Analyst",
model: "gpt-6-astra",
instructions:
"Review the workspace before answering. Keep the response concise, " +
"business-focused, and cite the file names that support each conclusion.",
defaultManifest: manifest,
capabilities: [shell()],
});
const result = await run(
agent,
"Summarize the renewal blockers and recommend the next two actions.",
{
sandbox: {
client: new UnixLocalSandboxClient(),
},
}
);
console.log(result.finalOutput);
import asyncio
from agents import Runner
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.capabilities import Shell
from agents.sandbox.entries import File
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient
manifest = Manifest(
entries={
"account_brief.md": File(
content=(
b"# Northwind Health\n\n"
b"- Segment: Mid-market healthcare analytics provider.\n"
b"- Renewal date: 2026-04-15.\n"
)
),
"implementation_risks.md": File(
content=(
b"# Delivery risks\n\n"
b"- Security questionnaire is not complete.\n"
b"- Procurement requires final legal language by April 1.\n"
)
),
}
)
agent = SandboxAgent(
name="Renewal Packet Analyst",
model="gpt-6-astra",
instructions=(
"Review the workspace before answering. Keep the response concise, "
"business-focused, and cite the file names that support each conclusion."
),
default_manifest=manifest,
capabilities=[Shell()],
)
async def main():
result = await Runner.run(
agent,
"Summarize the renewal blockers and recommend the next two actions.",
run_config=RunConfig(
sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()),
workflow_name="Unix-local sandbox review",
),
)
print(result.final_output)
asyncio.run(main())
완전한 로컬 예시는 TypeScript sandbox agent quickstart와 Python unix_local_runner.py를 참고하세요.
제공자 바꾸기
제공자는 에이전트 정의가 아니라 실행 설정의 일부예요. SandboxAgent, 매니페스트, 기능은 그대로 두고, 원하는 환경에 맞춰 샌드박스 클라이언트와 제공자 옵션만 바꾸면 돼요.
아래 예시는 로컬 컨테이너 격리를 위해 Docker를 쓴 경우예요. 호스팅 제공자도 각자의 클라이언트 클래스와 옵션으로 같은 패턴을 따라요.
Docker로 전환하기
import { run } from "@openai/agents";
import { SandboxAgent } from "@openai/agents/sandbox";
import { DockerSandboxClient } from "@openai/agents/sandbox/local";
const agent = new SandboxAgent({
name: "Workspace reviewer",
model: "gpt-6-astra",
instructions: "Inspect the sandbox workspace before answering.",
});
const result = await run(agent, "Inspect the workspace.", {
sandbox: {
client: new DockerSandboxClient({
image: "node:22-bookworm-slim",
}),
},
});
console.log(result.finalOutput);
from docker import from_env as docker_from_env
from agents import Runner
from agents.run import RunConfig
from agents.sandbox import SandboxRunConfig
from agents.sandbox.config import DEFAULT_PYTHON_SANDBOX_IMAGE
from agents.sandbox.sandboxes.docker import (
DockerSandboxClient,
DockerSandboxClientOptions,
)
docker_run_config = RunConfig(
sandbox=SandboxRunConfig(
client=DockerSandboxClient(docker_from_env()),
options=DockerSandboxClientOptions(image=DEFAULT_PYTHON_SANDBOX_IMAGE),
),
workflow_name="Docker sandbox review",
)
result = await Runner.run(
agent,
"Summarize the renewal blockers and recommend the next two actions.",
run_config=docker_run_config,
)
실행 가능한 예시는 TypeScript sandbox clients guide와 basic example, Python은 basic.py(제공자 선택), docker_runner.py(Docker), main.py(데이터 룸 흐름)를 SDK 저장소에서 확인하세요.
고급 패턴
기본 루프가 동작하면, 샌드박스는 더 많은 프롬프트 컨텍스트 대신 샌드박스 작업 공간이 필요한 워크플로에서 유용해져요. 아래 예시들은 별도 API가 아니라 워크플로 패턴이에요. 같은 harness가 워크플로를 라우팅·일시 중지·재개·트레이싱하는 동안, 각 샌드박스는 필요한 파일·툴·포트에 가깝게 실행을 유지하죠.
| 예시 | 설명 |
|---|---|
| Data room Q&A | 마운트된 데이터 룸 위에서 질문에 답합니다. |
| Data room table extraction | 마운트된 데이터 룸에서 테이블을 추출합니다. |
| Repository code review | repo를 복제·검사하고 코드 리뷰 산출물을 만듭니다. |
| Vision website clone | Vision API와 스크린샷 피드백으로 웹사이트를 복제합니다. |
| Sandbox resume | 기존 샌드박스에서 작업을 이어갑니다. |
미래 작업 재개하거나 시드하기
유용한 에이전트 작업은 자주 한 요청보다 오래 살아요. 사용자가 산출물을 검토하거나, 한 단계가 승인을 기다리거나, 다음 단계가 이후 이벤트에 달려 있죠.
세 가지 상태 개념을 구분해 두세요:
| 상태 표면 | 복원하는 것 | 언제 쓰나요 |
|---|---|---|
RunState |
모델 항목, 툴 상태, 승인, 활성 에이전트 위치 같은 harness 쪽 상태. | 러너가 일시 중지 사이에서 워크플로를 이어가야 할 때. |
| 세션 상태 | 클라이언트가 다시 연결할 수 있는 직렬화된 샌드박스 세션. | 여러분의 앱이나 작업 시스템이 제공자 세션 상태를 직접 저장할 때. |
snapshot |
새 샌드박스 세션을 시드할 수 있는 저장된 작업 공간 내용. | 새 실행이 빈 작업 공간이 아니라 저장된 파일·산출물에서 시작해야 할 때. |
실제로 러너는 샌드박스 세션을 이 순서로 결정해요:
- 라이브 샌드박스 세션을 넘기면 그 세션을 직접 재사용한다.
- 그렇지 않고 실행이
RunState에서 재개 중이면 저장된 샌드박스 세션 상태에서 재개한다. - 그렇지 않고 명시적 직렬화 샌드박스 상태를 넘기면 그 상태에서 재개한다.
- 그렇지 않으면 새 샌드박스 세션을 만든다. 새 세션에는 실행별 매니페스트가 있으면 그것을, 없으면 에이전트의 기본 매니페스트를 쓴다.
샌드박스 재개 예시는 멈춘 세션 상태를 직렬화하고, 같은 클라이언트로 재개한 뒤, 재개된 세션을 다음 실행에 다시 넘겨요:
샌드박스 상태 직렬화하고 재개하기
import { run } from "@openai/agents";
import { Manifest, SandboxAgent } from "@openai/agents/sandbox";
import { UnixLocalSandboxClient } from "@openai/agents/sandbox/local";
const manifest = new Manifest();
const client = new UnixLocalSandboxClient({
snapshot: { type: "local", baseDir: "/tmp/my-sandbox-snapshots" },
});
const agent = new SandboxAgent({
name: "Workspace builder",
model: "gpt-6-astra",
instructions: "Inspect the sandbox workspace before answering.",
});
const session = await client.create({ manifest });
let conversation = [];
let frozenSessionState;
try {
const firstResult = await run(agent, "Build the first version of the app.", {
maxTurns: 20,
sandbox: { session },
});
conversation = firstResult.history;
frozenSessionState = await client.serializeSessionState?.(session.state);
} finally {
await session.close?.();
}
if (!frozenSessionState || !client.deserializeSessionState || !client.resume) {
throw new Error("Sandbox client does not support session resume.");
}
const resumedSession = await client.resume(
await client.deserializeSessionState(frozenSessionState)
);
try {
conversation.push({
role: "user",
content: "Continue from the existing workspace and add tests.",
});
await run(agent, conversation, {
maxTurns: 20,
sandbox: { session: resumedSession },
});
} finally {
await resumedSession.close?.();
}
async with session:
first_result = await Runner.run(
agent,
"Build the first version of the app.",
max_turns=20,
run_config=RunConfig(
sandbox=SandboxRunConfig(session=session),
workflow_name="Sandbox resume example",
),
)
conversation = first_result.to_input_list()
frozen_session_state = client.deserialize_session_state(
client.serialize_session_state(session.state)
)
conversation.append(
{
"role": "user",
"content": "Continue from the existing workspace and add tests.",
}
)
resumed_session = await client.resume(frozen_session_state)
try:
async with resumed_session:
second_result = await Runner.run(
agent,
conversation,
max_turns=20,
run_config=RunConfig(
sandbox=SandboxRunConfig(session=resumed_session),
workflow_name="Sandbox resume example",
),
)
finally:
await client.delete(resumed_session)
manifest나 snapshot 같은 새 세션 입력은 러너가 새 샌드박스 세션을 만들 때만 적용돼요. 라이브 session을 주입하면, 기능 처리가 호환되는 비마운트 항목을 추가할 수는 있지만, 이미 실행 중인 샌드박스의 루트·환경·사용자·그룹을 바꾸거나, 기존 항목을 제거하거나, 항목 유형을 교체하거나, 마운트 항목을 추가·변경할 수 없어요.
이 구분 덕분에 harness는 에이전트 루프를 재개하고, 샌드박스 제공자는 작업 공간을 복원하거나 다시 만드는 겁니다. 이 경로의 현재 샘플 코드는 TypeScript resume session state example와 Python main.py, sandbox_agent_with_remote_snapshot.py에 있어요.
실행 간 기억(메모리) 유지하기
샌드박스 메모리를 쓰면 이후 샌드박스 에이전트 실행이 이전 실행에서 배워요. 이는 SDK가 관리하는 대화형 Session 메모리와는 달라요. 세션은 메시지 기록을 보존하고, 샌드박스 메모리는 이전 작업 공간 실행에서 유용한 교훈을 에이전트가 나중에 읽을 수 있는 파일로 distilled 하죠.
메모리는 에이전트가 모든 이전 턴을 다시 재생하지 않고도 사용자 선호, 수정 사항, 프로젝트별 교훈, 작업 요약을 이어가길 바랄 때 써요. 재개와 스냅샷은 작업 공간 상태를 보존하고, 메모리는 작업 공간에서 일어난 일에 대한 재사용 가능한 안내를 보존해요.
샌드박스 메모리 활성화하기
import {
Manifest,
SandboxAgent,
filesystem,
memory,
shell,
} from "@openai/agents/sandbox";
const manifest = new Manifest();
const agent = new SandboxAgent({
name: "Memory-enabled reviewer",
instructions:
"Inspect the workspace and retain useful lessons for follow-up runs.",
defaultManifest: manifest,
capabilities: [memory(), filesystem(), shell()],
});
from agents.sandbox.capabilities import Filesystem, Memory, Shell
agent = SandboxAgent(
name="Memory-enabled reviewer",
instructions="Inspect the workspace and retain useful lessons for follow-up runs.",
default_manifest=manifest,
capabilities=[Memory(), Filesystem(), Shell()],
)
메모리는 기본적으로 읽기와 생성을 모두 활성화해요. 메모리 읽기는 에이전트가 메모리 파일을 검색하고 여는 데 셸 접근이 필요해요. 기본적으로 라이브 메모리 업데이트도 파일시스템 접근이 필요해서, 에이전트가 오래된 메모리를 고치거나 사용자가 요청할 때 메모리를 업데이트할 수 있어요.
메모리 읽기는 점진적 공개(progressive disclosure) 를 사용해요. SDK는 실행 시작 시 memory_summary.md를 주입하고, 이전 작업이 관련 있어 보이면 에이전트가 MEMORY.md를 검색하며, 더 자세한 내용이 필요할 때만 rollout 요약을 엽니다.
| 메모리 모드 | 언제 쓰나요 |
|---|---|
| 기본 읽기/쓰기 | 에이전트가 기존 메모리를 읽고 새 메모리를 생성해야 할 때. |
| 읽기 전용 메모리 | 에이전트가 메모리를 읽지만 실행 후 새 메모리를 생성하지 않아야 할 때. |
| 생성 전용 메모리 | 기존 메모리를 쓰지 않고 실행이 메모리를 생성해야 할 때. |
| 읽기 설정 | 라이브 업데이트를 비활성화해야 할 때. |
| 생성 설정 | 추가 프롬프트 같은 생성 방식을 조정해야 할 때. |
| 배치 설정 | 같은 샌드박스 작업 공간 안에서 에이전트별로 격리된 메모리 배치가 필요할 때. |
기본적으로 메모리 산출물은 샌드박스 작업 공간에 살아요:
workspace/
sessions/
<rollout-id>.jsonl
memories/
memory_summary.md
MEMORY.md
raw_memories.md
phase_two_selection.json
raw_memories/
<rollout-id>.md
rollout_summaries/
<rollout-id>_<slug>.md
skills/
런타임은 샌드박스 세션 동안 실행 세그먼트를 덧붙여요. 세션이 닫히면 메모리 생성이 먼저 대화 요약과 원시 메모리를 추출하고, 그 원시 메모리를 MEMORY.md와 memory_summary.md로 통합해요. 이후 실행에서 메모리를 재사용하려면 같은 라이브 샌드박스 세션을 유지하거나, 세션 상태에서 재개하거나, 스냅샷에서 시작하거나, S3 같은 영속 스토리지를 마운트해서 설정된 메모리 디렉터리를 보존하세요.
다중 턴 샌드박스 채팅에서는 안정적인 SDK 세션을 같은 라이브 샌드박스 세션과 함께 쓰세요. 메모리는 실행을 명시적 대화 ID, 그다음 SDK 세션 ID, 그다음 실행 그룹 ID, 마지막으로 생성된 실행별 ID로 그룹화해요. 샌드박스 세션 ID는 라이브 작업 공간을 식별하는 것이지 메모리 대화 ID가 아니에요.
실행 가능한 예시는 TypeScript memory guide, Python은 로컬 스냅샷 흐름용 memory.py, S3 백업 메모리용 memory_s3.py, 에이전트 간 분리 메모리 배치용 memory_multi_agent_multiturn.py를 참고하세요.
샌드박스 에이전트 조합하기
샌드박스 에이전트는 SDK의 나머지와 잘 조합돼요.
- 핸드오프(handoff): 샌드박스가 아닌 접수 에이전트가 워크로드에서 작업 공간이 많이 필요한 부분만 샌드박스 에이전트에 위임해야 할 때 써요. 최상위 실행은 계속되지만, 다음 턴에서는 샌드박스 에이전트가 활성 에이전트가 돼요.
- 에이전트를 툴로: 바깥 오케스트레이터가 하나 이상의 샌드박스 에이전트를 중첩 툴로 호출해야 할 때 써요. 각 샌드박스 툴 에이전트는 자신만의 샌드박스 실행 설정·클라이언트·매니페스트·제공자 옵션을 가질 수 있어요.
예시는 handoffs.py와 sandbox_agents_as_tools.py를 참고하세요.
샌드박스 제공자
빠른 로컬 반복에는 Unix-local, 로컬 컨테이너 격리를 원하면 Docker로 시작하세요. 관리형 실행, 제공자별 격리, 확장, 미리보기, 스토리지 마운트, 스냅샷, 애플리케이션 서버 밖에 두어야 할 자격 증명이 필요할 때는 호스팅 제공자로 옮겨가세요.
제공자별 설정, 자격 증명, 격리, 스토리지, 미리보기, 영속화 동작은 제공자 문서를 확인하세요.
| 제공자 | SDK 클라이언트 | 문서 및 예시 |
|---|---|---|
| Blaxel | BlaxelSandboxClient |
Sandbox overview |
| Cloudflare | CloudflareSandboxClient |
Sandbox documentation OpenAI Agents tutorial Sandbox Bridge examples |
| Daytona | DaytonaSandboxClient |
Sandbox documentation OpenAI Agents SDK guide |
| Docker | DockerSandboxClient |
Docker documentation TypeScript Docker SDK example Python Docker SDK example |
| E2B | E2BSandboxClient |
Sandbox documentation OpenAI Agents SDK guide Launch blog |
| Modal | ModalSandboxClient |
Sandbox guide Integration blog Example repo Modal extension reference |
| Runloop | RunloopSandboxClient |
Devbox overview Tunnels |
| Unix-local | UnixLocalSandboxClient |
TypeScript local SDK example Python local SDK example |
| Vercel | VercelSandboxClient |
Sandbox documentation OpenAI Agents SDK guide FastAPI template Sample app |
더 알아보기 (Learn more)
관련 문서: Agents API: 샌드박스 연결, Tools 사용 가이드, 그리고 SDK 저장소의 샌드박스 예시(handoffs.py, sandbox_agents_as_tools.py)를 참고하세요.