샌드박스 개념

샌드박스 개념 (Sandbox Concepts)

샌드박스 에이전트(Sandbox Agent)는 모델에게 실제 파일이 놓인 지속적 워크스페이스를 제공해서, 문서 검색·파일 편집·명령 실행 같은 작업을 안전하게 처리하게 해줘요. 이 페이지는 샌드박스 에이전트의 핵심 개념과 구성 요소, 그리고 실행 수명주기를 정리해 드릴게요.

출처: 문서

본문

Beta 기능: 샌드박스 에이전트는 베타 상태예요. 일반 공급 전에 API, 기본값, 지원되는 기능의 세부 사항이 바뀔 수 있고, 시간이 지나며 더 많은 고급 기능이 추가될 수 있어요.

현대 에이전트는 파일시스템 안의 실제 파일을 다룰 수 있을 때 가장 잘 동작해요. 샌드박스 에이전트(Sandbox Agent) 는 전문 도구와 셸 명령을 사용해서 큰 문서 집합을 검색하고 조작하며, 파일을 편집하고, 산출물(artifact)을 만들고, 명령을 실행할 수 있어요. 샌드박스는 모델에게 에이전트가 여러분을 대신해 작업할 수 있는 지속적 워크스페이스를 제공해요. Agents SDK의 샌드박스 에이전트는 에이전트를 샌드박스 환경과 짝지어 실행하기 쉽게 해주고, 올바른 파일을 파일시스템에 놓고 샌드박스를 조정해서 규모에 맞게 작업을 시작·중지·재개하기 쉽게 해줘요.

워크스페이스는 에이전트가 필요한 데이터를 중심으로 정의해요. GitHub 저장소, 로컬 파일·디렉터리, 합성 작업 파일, S3나 Azure Blob Storage 같은 원격 파일시스템, 그리고 여러분이 제공하는 다른 샌드박스 입력에서 시작할 수 있어요.

Sandbox agent harness with compute

SandboxAgent는 여전히 Agent예요. instructions, prompt, tools, handoffs, mcp_servers, model_settings, output_type, guardrail, hooks 같은 일반 에이전트 표면을 유지하고, 여전히 일반 Runner API로 실행돼요. 바뀌는 것은 실행 경계예요:

  • SandboxAgent는 에이전트 자체를 정의해요. 일반 에이전트 구성에 더해 default_manifest, base_instructions, run_as, 그리고 파일시스템 도구, 셸 접근, 스킬, 메모리, 컴팩션 같은 능력(capability) 같은 샌드박스별 기본값을 포함해요.
  • Manifest는 새 샌드박스 워크스페이스의 원하는 시작 내용과 배치를 선언해요. 파일, 저장소, 마운트, 환경을 포함해요.
  • 샌드박스 세션은 명령이 실행되고 파일이 바뀌는 실행 환경이에요. 세션이 제공하는 격리는 백엔드와 구성에 따라 달라져요.
  • SandboxRunConfig는 실행이 그 샌드박스 세션을 어떻게 얻는지 결정해요. 예를 들어 세션을 직접 주입하거나, 직렬화된 샌드박스 세션 상태에서 재연결하거나, 샌드박스 클라이언트를 통해 새 샌드박스 세션을 만들 수 있어요.
  • 저장된 샌드박스 상태와 스냅샷은 이후 실행이 이전 작업에 재연결하거나 저장된 내용으로 새 샌드박스 세션을 시드(seed)할 수 있게 해줘요.

Manifest새 세션 워크스페이스 계약이지, 모든 라이브 샌드박스의 완전한 진실 소스(source of truth)는 아니에요. 실행의 유효 워크스페이스는 대신 재사용된 샌드박스 세션, 직렬화된 샌드박스 세션 상태, 또는 실행 시점에 고른 스냅샷에서 올 수 있어요.

이 페이지 전체에서 "샌드박스 세션"은 샌드박스 클라이언트가 관리하는 라이브 실행 환경을 의미해요. Sessions에 설명된 SDK의 대화형 Session 인터페이스와는 달라요.

외부 런타임은 여전히 승인, 트레이싱, handoff, 실행 재개에 필요한 상태 추적을 소유해요. 샌드박스 세션은 백엔드를 통해 명령과 파일 변경을 관리해요. 백엔드가 어떤 격리 제어가 적용되는지 결정해요. 세션 자체가 OS 수준 격리를 보장하지는 않아요.

구성 요소가 어떻게 맞물리는지

샌드박스 실행은 에이전트 정의를 실행별 샌드박스 구성과 결합해요. 러너는 에이전트를 준비하고, 라이브 샌드박스 세션에 바인딩하며, 이후 실행을 위해 상태를 저장할 수 있어요.

flowchart LR
    agent["SandboxAgent<br/><small>full Agent + sandbox defaults</small>"]
    config["SandboxRunConfig<br/><small>client / session / resume inputs</small>"]
    runner["Runner<br/><small>prepare instructions<br/>bind capability tools</small>"]
    sandbox["sandbox session<br/><small>workspace where commands run<br/>and files change</small>"]
    saved["saved state / snapshot<br/><small>for resume or fresh-start later</small>"]

    agent --> runner
    config --> runner
    runner --> sandbox
    sandbox --> saved

샌드박스별 기본값은 SandboxAgent에 유지돼요. 실행별 샌드박스-세션 선택은 SandboxRunConfig에 남아요.

수명주기를 세 단계로 생각해 보세요:

  1. SandboxAgent, Manifest, 능력(capability)으로 에이전트와 새-워크스페이스 계약을 정의.
  2. SandboxRunConfig로 샌드박스 세션을 주입, 재개, 또는 생성해서 Runner에 실행을 맡김.
  3. 나중에 runner 관리 RunState, 명시적 샌드박스 session_state, 또는 저장된 워크스페이스 스냅샷에서 계속 진행.

셸 접근이 가끔 쓰는 도구 하나뿐이라면 tools 가이드의 호스팅 셸부터 시작하세요. 워크스페이스 격리, 샌드박스 클라이언트 선택, 샌드박스-세션 재개 동작이 설계의 일부일 때 샌드박스 에이전트를 찾으세요.

언제 사용할까

샌드박스 에이전트는 워크스페이스 중심 워크플로우에 잘 맞아요. 예를 들어:

  • 코딩과 디버깅. 예를 들어 GitHub 저장소의 이슈 보고서에 대한 자동 수정을 조정하고 표적 테스트를 실행.
  • 문서 처리와 편집. 예를 들어 사용자 금융 문서에서 정보를 추출하고 완성된 세금 신고서 초안을 작성.
  • 파일 기반 검토 또는 분석. 예를 들어 답하기 전에 온보딩 패킷, 생성된 보고서, 산출물 번들을 확인.
  • 별도 워크스페이스를 가진 멀티-에이전트 패턴. 예를 들어 각 검토자나 코딩 하위 에이전트에 자신의 워크스페이스를 부여.
  • 다단계 워크스페이스 작업. 예를 들어 한 실행에서 버그를 고치고 나중에 회귀 테스트를 추가하거나, 스냅샷이나 샌드박스 세션 상태에서 재개.

파일이나 상태 저장·변경 가능한 파일시스템에 접근할 필요가 없다면 계속 Agent를 사용하세요. 셸 접근이 가끔 있는 능력 하나뿐이라면 호스팅 셸을 추가하세요. 워크스페이스 경계 자체가 기능의 일부라면 샌드박스 에이전트를 사용하세요.

샌드박스 클라이언트 고르기

macOS 또는 Linux에서의 신뢰된 로컬 개발, 또는 외부 격리 환경 안에서는 UnixLocalSandboxClient를 사용하세요. Linux에서는 이 백엔드가 명령을 OS 수준 격리를 추가하지 않고 호스트 프로세스로 실행해요. macOS에서는 sandbox-exec로 파일시스템 제한을 적용하지만 네트워크 격리는 제공하지 않아요.

기본적으로 새 Unix-로컬 세션은 별도의 임시 워크스페이스를 받아요. 같은 커스텀 Manifest.root로 세션을 구성하면 그 세션들은 워크스페이스를 공유해요. 별도 세션이 OS 수준 격리를 보장하지는 않아요.

신뢰할 수 없는 명령(신뢰할 수 없는 입력의 영향을 받는 명령 포함)에는 적절히 구성된 DockerSandboxClient나 호스팅 제공자를 고르거나 외부 격리를 제공하세요. Windows에서는 Docker나 호스팅 제공자를 사용하세요. 로컬 백엔드를 고르기 전에 Unix-로컬 실행 제한을 참고하세요.

대부분의 경우 SandboxAgent 정의는 그대로 두고, SandboxRunConfig에서 샌드박스 클라이언트와 옵션만 바꿔요. 로컬, Docker, 호스팅, 원격 마운트 옵션은 샌드박스 클라이언트를 참고하세요.

핵심 구성 요소

계층 주요 SDK 구성 요소 무엇을 답하나
에이전트 정의 SandboxAgent, Manifest, 능력(capability) 어떤 에이전트가 실행되고, 어떤 새-세션 워크스페이스 계약에서 시작해야 하나?
샌드박스 실행 SandboxRunConfig, 샌드박스 클라이언트, 라이브 샌드박스 세션 이 실행은 어떻게 라이브 샌드박스 세션을 얻고, 작업은 어디에서 실행되나?
저장된 샌드박스 상태 RunState 샌드박스 페이로드, session_state, 스냅샷 이 워크플로우는 어떻게 이전 샌드박스 작업에 재연결하거나 저장된 내용으로 새 샌드박스 세션을 시드하나?

주요 SDK 구성 요소는 이렇게 계층에 매핑돼요:

구성 요소 소유하는 것 질문
SandboxAgent 에이전트 정의 이 에이전트는 무엇을 하고, 어떤 기본값이 함께 이동해야 하나?
Manifest 새-세션 워크스페이스 파일과 폴더 실행이 시작될 때 파일시스템에 어떤 파일과 폴더가 있어야 하나?
Capability 샌드박스 네이티브 동작 어떤 도구, 지시문 조각, 또는 런타임 동작이 이 에이전트에 붙어야 하나?
SandboxRunConfig 실행별 샌드박스 클라이언트와 샌드박스-세션 출처 이 실행은 샌드박스 세션을 주입, 재개, 생성해야 하나?
RunState runner 관리 저장 샌드박스 상태 이전 runner 관리 워크플로우를 재개하며 그 샌드박스 상태를 자동으로 가져오는 중인가?
SandboxRunConfig.session_state 명시적 직렬화 샌드박스 세션 상태 이미 RunState 밖에서 직렬화한 샌드박스 상태에서 재개하고 싶나?
SandboxRunConfig.snapshot 새 샌드박스 세션용 저장 워크스페이스 내용 새 샌드박스 세션이 저장된 파일과 산출물에서 시작해야 하나?

실용적인 설계 순서는 이래요:

  1. Manifest로 새-세션 워크스페이스 계약을 정의.
  2. SandboxAgent로 에이전트를 정의.
  3. 내장 또는 커스텀 능력(capability)을 추가.
  4. RunConfig(sandbox=SandboxRunConfig(...))에서 각 실행이 샌드박스 세션을 어떻게 얻을지 결정.

샌드박스 실행이 준비되는 방법

실행 시점에 러너는 그 정의를 구체적인 샌드박스 기반 실행으로 바꿔요:

  1. SandboxRunConfig에서 샌드박스 세션을 해석해요. session=...을 전달하면 그 라이브 샌드박스 세션을 재사용해요. 그렇지 않으면 client=...으로 세션을 생성하거나 재개해요.
  2. 실행의 유효 워크스페이스 입력을 결정해요. 실행이 샌드박스 세션을 주입하거나 재개하면 그 기존 샌드박스 상태가 우선해요. 그렇지 않으면 러너는 일회성 manifest 오버라이드나 agent.default_manifest에서 시작해요. 그래서 Manifest만으로는 매 실행의 최종 라이브 워크스페이스를 정의하지 못해요.
  3. 능력(capability)이 결과 manifest를 처리하게 해요. 최종 에이전트가 준비되기 전에 능력이 파일, 마운트, 또는 다른 워크스페이스 범위 동작을 추가할 수 있는 방법이에요.
  4. 고정된 순서로 최종 지시문을 만드세요. SDK의 기본 샌드박스 프롬프트(명시적으로 오버라이드하면 base_instructions), 그 다음 instructions, 그 다음 능력 지시문 조각, 그 다음 원격 마운트 정책 텍스트, 그 다음 렌더링된 파일시스템 트리.
  5. 능력 도구를 라이브 샌드박스 세션에 바인딩하고 준비된 에이전트를 일반 Runner API로 실행해요.

샌드박싱은 턴이 무엇을 의미하는지 바꾸지 않아요. 턴은 여전히 모델 스텝이지, 단일 셸 명령이나 샌드박스 동작이 아니에요. 샌드박스 측 작업과 턴 사이에 고정된 1:1 매핑은 없어요. 어떤 작업은 샌드박스 실행 계층 안에 머물고, 다른 동작은 도구 결과, 승인, 또는 다른 종류의 상태 같은 또 다른 모델 스텝이 필요한 정보를 반환해요. 실용적인 규칙으로, 샌드박스 작업 후 에이전트 런타임이 또 다른 모델 응답이 필요할 때만 다음 턴을 소비해요.

이런 준비 단계들이 default_manifest, instructions, base_instructions, capabilities, run_asSandboxAgent를 설계할 때 생각해 볼 주요 샌드박스별 옵션인 이유예요.

SandboxAgent 옵션

일반 Agent 필드 위에 추가되는 샌드박스별 옵션이에요:

옵션 최적 사용
default_manifest 러너가 만드는 새 샌드박스 세션의 기본 워크스페이스.
instructions SDK 샌드박스 프롬프트 뒤에 추가되는 추가 역할, 워크플로우, 성공 기준.
base_instructions SDK 샌드박스 프롬프트를 대체하는 고급 탈출구(escape hatch).
capabilities 이 에이전트와 함께 이동해야 하는 샌드박스 네이티브 도구와 동작.
run_as 셸 명령, 파일 읽기, 패치 같은 모델 직면 샌드박스 도구의 사용자 정체성.

샌드박스 클라이언트 선택, 샌드박스-세션 재사용, manifest 오버라이드, 스냅샷 선택은 에이전트가 아니라 SandboxRunConfig에 속해요.

default_manifest

default_manifest는 러너가 이 에이전트의 새 샌드박스 세션을 만들 때 사용하는 기본 Manifest예요. 에이전트가 보통 시작해야 하는 파일, 저장소, 보조 자료, 출력 디렉터리, 마운트를 넣는 데 쓰세요.

이것은 단지 기본값일 뿐이에요. 실행은 SandboxRunConfig(manifest=...)로 오버라이드할 수 있고, 재사용되거나 재개된 샌드박스 세션은 기존 워크스페이스 상태를 유지해요.

instructionsbase_instructions

instructions는 다른 프롬프트에서도 살아남아야 하는 짧은 규칙에 사용하세요. SandboxAgent에서는 이 지시문이 SDK의 샌드박스 기본 프롬프트 뒤에 추가되므로, 내장 샌드박스 지침을 유지하면서 자신의 역할, 워크플로우, 성공 기준을 더할 수 있어요.

base_instructions는 SDK 샌드박스 기본 프롬프트를 대체하고 싶을 때만 사용하세요. 대부분의 에이전트는 이것을 설정하지 않아야 해요.

넣을 곳 용도 예시
instructions 에이전트의 안정적인 역할, 워크플로우 규칙, 성공 기준. "온보딩 문서를 검사한 뒤 handoff하세요.", "최종 파일을 output/에 쓰세요."
base_instructions SDK 샌드박스 기본 프롬프트의 완전한 대체. 커스텀 저수준 샌드박스 래퍼 프롬프트.
사용자 프롬프트 이 실행의 일회성 요청. "이 워크스페이스를 요약하세요."
manifest의 워크스페이스 파일 더 긴 작업 명세, 저장소 로컬 지시문, 범위 제한 참고 자료. repo/task.md, 문서 번들, 샘플 패킷.

instructions의 좋은 사용 사례:

사용자의 일회성 작업을 instructions에 복사하거나, manifest에 있어야 할 긴 참고 자료를 넣거나, 내장 능력이 이미 주입하는 도구 문서를 반복하거나, 모델이 실행 시점에 필요 없는 로컬 설치 메모를 섞는 것은 피하세요.

instructions를 생략해도 SDK는 여전히 기본 샌드박스 프롬프트를 포함해요. 저수준 래퍼에는 충분하지만, 대부분의 사용자 직면 에이전트는 여전히 명시적 instructions를 제공해야 해요.

capabilities

능력(capability)은 샌드박스 네이티브 동작을 SandboxAgent에 붙여요. 실행이 시작되기 전에 워크스페이스를 만들고, 샌드박스별 지시문을 추가하고, 라이브 샌드박스 세션에 바인딩되는 도구를 노출하며, 그 에이전트의 모델 동작이나 입력 처리를 조정할 수 있어요.

내장 능력:

능력 언제 추가하나 참고
Shell 에이전트가 셸 접근이 필요할 때. exec_command를 추가하고, 샌드박스 클라이언트가 PTY 상호작용을 지원하면 write_stdin도 추가.
Filesystem 에이전트가 파일을 편집하거나 로컬 이미지를 검사해야 할 때. apply_patchview_image 추가. 상대 경로는 기본적으로 워크스페이스 루트를, 구성되면 SandboxRunConfig.cwd를 사용.
Skills 샌드박스에서 스킬 발견과 구체화(materialization)를 원할 때. .agents.agents/skills를 수동 마운트하는 것보다 이것을 선호. Skills가 스킬을 인덱싱하고 샌드박스에 구체화해줌.
Memory 후속 실행이 메모리 산출물을 읽거나 생성해야 할 때. Shell 필요. 실행 중 메모리 산출물을 갱신하려면 Filesystem도 필요.
Compaction 장수명 흐름이 컴팩션 항목 후 컨텍스트 트리밍이 필요할 때. 모델 샘플링과 입력 처리를 조정.

기본적으로 SandboxAgent.capabilitiesCapabilities.default()를 사용하며, 여기에는 Filesystem(), Shell(), Compaction()이 포함돼요. capabilities=[...]을 전달하면 그 목록이 기본을 대체하므로, 여전히 원하는 기본 능력은 포함하세요.

view_image 도구는 PNG, JPEG, GIF, WebP, BMP, TIFF 래스터 이미지를 파일 이름 확장자가 아니라 파일 콘텐츠로 식별해요. 래스터-이미지 확장자를 가진 파일 이름인데 콘텐츠가 지원되지 않으면 거부되고, 지원되는 래스터 콘텐츠는 파일 이름에 이미지 확장자가 없어도 로드될 수 있어요. .svg.svgz 파일에서는 파일 콘텐츠에서 SVG 마크업을 인식하는 것에 더해 파일 이름 기반 호환성도 유지해요.

스킬의 경우, 어떻게 구체화할지에 따라 출처를 고르세요:

  • Skills(lazy_from=LocalDirLazySkillSource(...))는 더 큰 로컬 스킬 디렉터리에 좋은 기본값이에요. 모델이 먼저 인덱스를 발견하고 필요한 것만 로드할 수 있으니까요.
  • LocalDirLazySkillSource(source=LocalDir(src=...))는 SDK 프로세스가 실행되는 파일시스템에서 읽어요. 샌드박스 이미지나 워크스페이스 안에만 존재하는 경로가 아니라 원래 호스트 측 스킬 디렉터리를 전달하세요.
  • Skills(from_=LocalDir(src=...))는 미리 스테이징하고 싶은 작은 로컬 번들에 더 좋아요.
  • Skills(from_=GitRepo(repo=..., ref=...))는 스킬 자체가 저장소에서 와야 할 때 맞는 선택이에요.

LocalDir.src는 SDK 호스트의 원본 경로예요. skills_pathload_skill이 호출될 때 스킬이 스테이징되는 샌드박스 워크스페이스 안의 상대 대상 경로예요.

스킬이 이미 .agents/skills/<name>/SKILL.md 같은 디스크에 있다면, LocalDir(...)를 그 소스 루트에 지정하고 여전히 Skills(...)로 노출하세요. 기존 워크스페이스 계약이 다른 인-샌드박스 레이아웃에 의존하지 않는 한 기본 skills_path=".agents"를 유지하세요.

내장 능력이 맞으면 그것을 선호하세요. 내장이 다루지 못하는 샌드박스별 도구나 지시문 표면이 필요할 때만 커스텀 능력을 작성하세요.

Manifest

Manifest는 새 샌드박스 세션의 워크스페이스를 설명해요. 워크스페이스 root를 설정하고, 파일·디렉터리를 선언하고, 로컬 파일을 복사하고, Git 저장소를 클론하고, 원격 저장소 마운트를 붙이고, 환경 변수를 설정하고, 사용자·그룹을 정의하며, 워크스페이스 밖의 특정 절대 경로에 접근을 부여할 수 있어요.

Manifest 항목 경로는 워크스페이스 상대예요. 절대 경로가 될 수 없고 ..로 워크스페이스를 벗어날 수 없어요. 이렇게 해서 워크스페이스 계약을 로컬, Docker, 호스팅 클라이언트에 걸쳐 이식 가능하게 유지해요.

작업이 시작되기 전에 에이전트가 필요한 자료에 manifest 항목을 사용하세요:

Manifest 항목 용도
File, Dir 작은 합성 입력, 보조 파일, 또는 출력 디렉터리.
LocalFile, LocalDir 샌드박스에 구체화해야 하는 호스트 파일·디렉터리.
GitRepo 워크스페이스로 가져와야 하는 저장소.
S3Mount, GCSMount, R2Mount, AzureBlobMount, BoxMount, S3FilesMount 같은 마운트 샌드박스 안에 나타나야 하는 외부 저장소.

Dir은 합성 자식 또는 출력 위치에서 샌드박스 워크스페이스 안에 디렉터리를 만들어요. 호스트 파일시스템에서는 읽지 않아요. 기존 호스트 디렉터리를 샌드박스 워크스페이스에 복사해야 한다면 LocalDir을 사용하세요.

LocalFile.srcLocalDir.src는 기본적으로 SDK 프로세스 작업 디렉터리 기준으로 해석돼요. extra_path_grants로 덮이지 않는 한 원본은 그 기준 디렉터리 아래에 있어야 해요. 이렇게 하면 로컬 소스 구체화를 나머지 샌드박스 manifest와 같은 호스트-경로 신뢰 경계 안에 유지해요.

마운트 항목은 노출할 저장소를, 마운트 전략은 샌드박스 백엔드가 그 저장소를 붙이는 방식을 설명해요. 마운트 옵션과 제공자 지원은 샌드박스 클라이언트를 참고하세요.

좋은 manifest 설계는 대개 워크스페이스 계약을 좁게 유지하고, 긴 작업 레시피는 repo/task.md 같은 워크스페이스 파일에 두고, 지시문에는 repo/task.mdoutput/report.md 같은 상대 워크스페이스 경로를 사용해요. 에이전트가 Filesystem 능력의 apply_patch 도구로 파일을 편집한다면, 패치 경로가 기본적으로 샌드박스 워크스페이스 루트 상대이거나 구성되면 SandboxRunConfig.cwd를 사용한다는 점을 기억하세요. 셸 workdir을 사용하지 않아요.

extra_path_grants는 에이전트가 워크스페이스 밖의 구체적인 절대 경로가 필요할 때나, manifest가 SDK 프로세스 작업 디렉터리 밖의 신뢰된 로컬 소스를 복사해야 할 때만 사용하세요. 예시로는 임시 도구 출력용 /tmp, 읽기 전용 런타임용 /opt/toolchain, 샌드박스에 구체화해야 하는 생성된 스킬 디렉터리 등이 있어요. 승인은 로컬 소스 구체화와 SDK 파일 API에 적용돼요. 백엔드가 파일시스템 정책을 강제할 수 있을 때는 셸 실행에도 적용돼요:

from agents.sandbox import Manifest, SandboxPathGrant

manifest = Manifest(
    extra_path_grants=(
        SandboxPathGrant(path="/tmp"),
        SandboxPathGrant(path="/opt/toolchain", read_only=True),
    ),
)

Docker가 컨테이너 안의 절대 POSIX path에 다른 절대 호스트 경로를 바인드-마운트해야 할 때 host_path를 설정하세요. UnixLocalSandboxClient는 두 경로가 같은 path-only 승인만 지원하고 host_path를 거부해요. 샌드박스가 수정하면 안 되는 호스트 데이터에는 read_only=True를 사용하거나, 복사로 충분하다면 LocalFile이나 LocalDir을 사용하세요.

Unix-로컬 경로 승인은 어떤 호스트 소스가 워크스페이스로 복사될 수 있는지, SDK 파일 API가 어떤 경로에 접근할 수 있는지를 관리해요. read_only=True는 SDK 파일 API가 승인된 경로에 쓰는 것을 막아요. Linux에서는 이 설정들이 임의의 셸 명령을 제한하지 않아요. 명령은 승인된 경로가 아니더라도 프로세스의 권한과 외부 격리가 허용하는 호스트 경로에 접근할 수 있어요. macOS 파일시스템 프로필과 Docker 바인드 마운트는 각자의 승인 제한을 명령에 적용해요.

extra_path_grants를 포함하는 manifest는 신뢰된 구성으로 취급하세요. 애플리케이션이 이미 그 호스트 경로를 승인하지 않는 한 모델 출력이나 다른 신뢰할 수 없는 페이로드에서 승인을 로드하지 마세요.

스냅샷과 persist_workspace()는 여전히 워크스페이스 루트만 포함해요. 추가 승인된 경로는 런타임 접근이지 지속적인 워크스페이스 상태가 아니에요.

Permissions

Permissions는 manifest 항목의 파일시스템 권한을 제어해요. 샌드박스가 구체화하는 파일에 관한 것이지, 모델 권한, 승인 정책, API 자격 증명에 관한 것이 아니에요.

기본적으로 manifest 항목은 소유자 읽기/쓰기/실행, 그룹·기타 읽기/실행이에요. 스테이징된 파일이 비공개, 읽기 전용, 또는 실행 가능이어야 할 때 오버라이드하세요:

from agents.sandbox import FileMode, Permissions
from agents.sandbox.entries import File

private_notes = File(
    content=b"internal notes",
    permissions=Permissions(
        owner=FileMode.READ | FileMode.WRITE,
        group=FileMode.NONE,
        other=FileMode.NONE,
    ),
)

Permissions는 별도의 소유자, 그룹, 기타 비트와 항목이 디렉터리인지 여부를 저장해요. 직접 만들거나, Permissions.from_str(...)로 모드 문자열에서 파싱하거나, Permissions.from_mode(...)로 OS 모드에서 파생할 수 있어요.

사용자(User)는 작업을 실행할 수 있는 샌드박스 정체성이에요. 그 정체성이 샌드박스에 존재하길 원하면 manifest에 User를 추가하고, 셸 명령, 파일 읽기, 패치 같은 모델 직면 샌드박스 도구가 그 사용자로 실행되어야 할 때 SandboxAgent.run_as를 설정하세요. run_as가 아직 manifest에 없는 사용자를 가리키면 러너가 유효 manifest에 그 사용자를 추가해 줘요.

from agents import Runner
from agents.run import RunConfig
from agents.sandbox import FileMode, Manifest, Permissions, SandboxAgent, SandboxRunConfig, User
from agents.sandbox.entries import Dir, LocalDir
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

analyst = User(name="analyst")

agent = SandboxAgent(
    name="Dataroom analyst",
    instructions="Review the files in `dataroom/` and write findings to `output/`.",
    default_manifest=Manifest(
        # 샌드박스 사용자를 선언해서 manifest 항목이 그에게 접근을 부여할 수 있게 합니다.
        users=[analyst],
        entries={
            "dataroom": LocalDir(
                src="./dataroom",
                # analyst가 마운트된 dataroom을 탐색·읽게 하지만 편집은 못하게 합니다.
                group=analyst,
                permissions=Permissions(
                    owner=FileMode.READ | FileMode.EXEC,
                    group=FileMode.READ | FileMode.EXEC,
                    other=FileMode.NONE,
                ),
            ),
            "output": Dir(
                # analyst에게 산출물을 위한 쓰기 가능한 scratch/output 디렉터리를 줍니다.
                group=analyst,
                permissions=Permissions(
                    owner=FileMode.ALL,
                    group=FileMode.ALL,
                    other=FileMode.NONE,
                ),
            ),
        },
    ),
    # 모델 직면 샌드박스 동작을 이 사용자로 실행해서 그 권한이 적용되게 합니다.
    run_as=analyst,
)

result = await Runner.run(
    agent,
    "Summarize the contracts and call out renewal dates.",
    run_config=RunConfig(
        sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()),
    ),
)

파일 수준 공유 규칙도 필요하다면 사용자를 manifest 그룹과 항목 group 메타데이터와 결합하세요. run_as 사용자는 샌드박스 네이티브 동작을 실행하는 사람을 제어하고, Permissions는 샌드박스가 워크스페이스를 구체화한 뒤 그 사용자가 어떤 파일을 읽고, 쓰고, 실행할 수 있는지를 제어해요.

SnapshotSpec

SnapshotSpec은 새 샌드박스 세션이 저장된 워크스페이스 내용을 어디에서 복원하고 어디로 다시 지속할지 말해줘요. 샌드박스 워크스페이스의 스냅샷 정책이고, session_state는 특정 샌드박스 백엔드를 재개하기 위한 직렬화된 연결 상태예요.

로컬 내구성 스냅샷에는 LocalSnapshotSpec을, 앱이 원격 스냅샷 클라이언트를 제공할 때는 RemoteSnapshotSpec을 사용하세요. 로컬 스냅샷 설정을 사용할 수 없을 때는 no-op 스냅샷이 폴백으로 사용되고, 고급 호출자는 워크스페이스 스냅샷 지속을 원하지 않을 때 명시적으로 하나를 사용할 수 있어요.

from pathlib import Path

from agents.run import RunConfig
from agents.sandbox import LocalSnapshotSpec, SandboxRunConfig
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

run_config = RunConfig(
    sandbox=SandboxRunConfig(
        client=UnixLocalSandboxClient(),
        snapshot=LocalSnapshotSpec(base_path=Path("/tmp/my-sandbox-snapshots")),
    )
)

러너가 새 샌드박스 세션을 만들면 샌드박스 클라이언트는 그 세션의 스냅샷 인스턴스를 만들어요. 시작 시 스냅샷이 복원 가능하면, 실행이 계속되기 전에 샌드박스가 저장된 워크스페이스 내용을 복원해요. 정리 시 runner 소유 샌드박스 세션은 워크스페이스를 보관하고 스냅샷을 통해 다시 지속해요.

snapshot을 생략하면 런타임은 가능할 때 기본 로컬 스냅샷 위치를 사용하려 해요. 그것을 설정할 수 없으면 no-op 스냅샷으로 폴백해요. 마운트된 경로와 임시 경로는 내구성 워크스페이스 내용으로 스냅샷에 복사되지 않아요.

샌드박스 수명주기

두 가지 수명주기 모드가 있어요: SDK 소유개발자 소유.

sequenceDiagram
    participant App
    participant Runner
    participant Client
    participant Sandbox

    App->>Runner: Runner.run(..., SandboxRunConfig(client=...))
    Runner->>Client: create or resume sandbox
    Client-->>Runner: sandbox session
    Runner->>Sandbox: start, run tools
    Runner->>Sandbox: stop and persist snapshot
    Runner->>Client: delete runner-owned resources

    App->>Client: create(...)
    Client-->>App: sandbox session
    App->>Sandbox: async with sandbox
    App->>Runner: Runner.run(..., SandboxRunConfig(session=sandbox))
    Runner->>Sandbox: run tools
    App->>Sandbox: cleanup on context exit / aclose()

샌드박스가 한 번의 실행 동안만 살아 있으면 되는 경우 SDK 소유 수명주기를 사용하세요. client와 옵션으로 manifest, snapshot, 그리고 필요한 클라이언트 options를 전달하면, 러너가 샌드박스를 생성·재개하고, 시작하고, 에이전트를 실행하고, 스냅샷 기반 워크스페이스 상태를 지속하고, 샌드박스 세션을 끝내고, 클라이언트가 runner 소유 리소스를 정리하게 해요.

result = await Runner.run(
    agent,
    "Inspect the workspace and summarize what changed.",
    run_config=RunConfig(
        sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()),
    ),
)

개발자 소유 수명주기는 샌드박스를 적극적으로(eagerly) 만들고, 여러 실행에 걸쳐 하나의 라이브 샌드박스를 재사용하고, 실행 후 파일을 검사하고, 직접 만든 샌드박스 위에서 스트리밍하고, 정리 시점을 정확히 결정하고 싶을 때 사용하세요. session=...을 전달하면 러너가 그 라이브 샌드박스를 사용하지만 대신 닫지는 않아요.

sandbox = await client.create(manifest=agent.default_manifest)

async with sandbox:
    run_config = RunConfig(sandbox=SandboxRunConfig(session=sandbox))
    await Runner.run(agent, "Analyze the files.", run_config=run_config)
    await Runner.run(agent, "Write the final report.", run_config=run_config)

컨텍스트 매니저는 일반적인 형태예요. 진입 시 샌드박스를 시작하고, 종료 시 세션 정리 수명주기를 실행해요. 앱이 컨텍스트 매니저를 사용할 수 없다면 수명주기 메서드를 직접 호출하세요:

sandbox = await client.create(
    manifest=agent.default_manifest,
    snapshot=LocalSnapshotSpec(base_path=Path("/tmp/my-sandbox-snapshots")),
)
try:
    await sandbox.start()
    await Runner.run(
        agent,
        "Analyze the files.",
        run_config=RunConfig(sandbox=SandboxRunConfig(session=sandbox)),
    )
    # 더 많은 작업을 하기 전에 라이브 워크스페이스의 체크포인트를 지속합니다.
    # `aclose()`도 `stop()`을 호출하므로, 이것은 명시적 중간 수명주기 저장에만 필요합니다.
    await sandbox.stop()
finally:
    await sandbox.aclose()

stop()은 스냅샷 기반 워크스페이스 내용만 지속하고 샌드박스를 해체하지 않아요. aclose()는 전체 세션 정리 경로예요. pre-stop 훅을 실행하고, stop()을 호출하고, 샌드박스 리소스를 종료하고, 세션 범위 의존성을 닫아요.

SandboxRunConfig 옵션

SandboxRunConfig는 샌드박스 세션이 어디에서 오는지, 새 세션을 어떻게 초기화해야 하는지 결정하는 실행별 옵션을 담아요.

샌드박스 출처

이 옵션들은 러너가 샌드박스 세션을 재사용, 재개, 또는 생성해야 하는지 결정해요:

옵션 언제 쓰나 참고
client 러너가 샌드박스 세션을 생성·재개·정리해 주길 원할 때. 라이브 샌드박스 session을 제공하지 않으면 필수.
session 직접 라이브 샌드박스 세션을 이미 만들었을 때. 호출자가 수명주기를 소유. 러너는 그 라이브 샌드박스 세션을 재사용.
session_state 직렬화된 샌드박스 세션 상태는 있지만 라이브 샌드박스 세션 객체는 없을 때. client 필요. 러너가 그 명시적 상태에서 재개하고 재개된 세션의 수명주기를 소유.

실제로 러너는 이 순서로 샌드박스 세션을 해석해요:

  1. run_config.sandbox.session을 주입하면 그 라이브 샌드박스 세션이 직접 재사용돼요.
  2. 그렇지 않고 RunState에서 실행을 재개하면 저장된 샌드박스 세션 상태가 재개돼요.
  3. 그렇지 않고 run_config.sandbox.session_state를 전달하면 러너가 그 명시적 직렬화 샌드박스 세션 상태에서 재개해요.
  4. 그렇지 않으면 러너가 새 샌드박스 세션을 만들어요. 새 세션에는 run_config.sandbox.manifest가 제공되면 그것을, 아니면 agent.default_manifest를 사용해요.

새-세션 입력

이 옵션들은 러너가 새 샌드박스 세션을 만들 때만 중요해요:

옵션 언제 쓰나 참고
manifest 일회성 새-세션 워크스페이스 오버라이드를 원할 때. 생략하면 agent.default_manifest로 폴백.
snapshot 새 샌드박스 세션이 스냅샷에서 시드되어야 할 때. 재개-유사 흐름이나 원격 스냅샷 클라이언트에 유용.
options 샌드박스 클라이언트가 생성-시점 옵션이 필요할 때. Docker 이미지, Modal 앱 이름, E2B 템플릿, 타임아웃 등 클라이언트별 설정에 일반적.

모델 직면 작업 디렉터리

여러 실행이 하나의 샌드박스 세션을 공유하지만 별도 하위 디렉터리에서 동작해야 할 때 cwd에 POSIX 워크스페이스 상대 디렉터리를 설정하세요. 러너가 cwd를 검증할 때 그 디렉터리는 존재해야 하고 구성된 샌드박스 사용자가 접근할 수 있어야 해요. 새 세션에서는 러너가 manifest를 먼저 구체화하므로 manifest가 이 검증 전에 디렉터리를 만들 수 있어요.

from agents import Runner
from agents.run import RunConfig
from agents.sandbox import SandboxRunConfig

result = await Runner.run(
    agent,
    "Work only on task A.",
    run_config=RunConfig(
        sandbox=SandboxRunConfig(
            session=shared_sandbox,
            cwd="tasks/task-a",
        ),
    ),
)

내장 exec_command, view_image, apply_patch 도구가 사용하는 상대 경로는 cwd에서 해석돼요. cwd 값 자체에 대해서는 절대 경로, .. 같은 부모 세그먼트, 빈 값이 거부돼요. 문자열 값은 슬래시(/)를 사용해야 해요. 상대 PurePath 값은 POSIX 형태로 정규화되지만, 절대 PurePath 값은 유효하지 않아요. 직접 BaseSandboxSession 파일 API는 여전히 워크스페이스 루트 상대이므로, cwdManifest.root나 세션의 기본 워크스페이스 경계를 바꾸지 않아요. 이 설정은 상대 경로 해석만 바꿔요. 실행을 cwd에 한정하거나 공유 세션의 워크스페이스 정책이 허용하는 다른 경로에 접근하는 것을 막지 않아요.

경로를 담는 커스텀 능력은 모델 제공 상대 경로를 해석할 때 바인딩된 SandboxWorkspaceScope를 적용해야 해요. 하나의 샌드박스 세션을 공유하면서 모델 직면 작업 디렉터리를 분리하는 두 동시 실행은 examples/sandbox/shared_session_workdirs.py를 참고하세요.

구체화 제어

concurrency_limits는 샌드박스 구체화 작업이 병렬로 실행될 수 있는 양을 제어해요. 큰 manifest나 로컬 디렉터리 복사에 더 촘촘한 리소스 제어가 필요할 때 SandboxConcurrencyLimits(manifest_entries=..., local_dir_files=...)를 사용하세요. 특정 한도를 비활성화하려면 어느 값을 None으로 설정하세요.

archive_limits는 아카이브 추출에 대한 SDK 측 리소스 검사를 제어해요. SDK 기본 임계값을 활성화하려면 archive_limits=SandboxArchiveLimits()를, 아카이브에 더 촘촘한 리소스 제어가 필요할 때는 SandboxArchiveLimits(max_input_bytes=..., max_extracted_bytes=..., max_members=...) 같은 명시적 값을 전달하세요. SDK 아카이브 리소스 제한 없이 기본 동작을 유지하려면 archive_limits=None을 두거나, 특정 한도만 비활성화하려면 개별 필드를 None으로 설정하세요.

몇 가지 의미를 기억해 둘 만해요:

  • 새 세션: manifest=snapshot=은 러너가 새 샌드박스 세션을 만들 때만 적용돼요.
  • 재개 vs 스냅샷: session_state=는 이전에 직렬화된 샌드박스 상태에 재연결하고, snapshot=은 저장된 워크스페이스 내용으로 새 샌드박스 세션을 시드해요.
  • 클라이언트별 옵션: options=는 샌드박스 클라이언트에 의존해요. Docker와 많은 호스팅 클라이언트는 그것을 필요로 해요.
  • 주입된 라이브 세션: 실행 중인 샌드박스 session을 전달하면 능력 기반 manifest 갱신이 호환되는 비-마운트 항목을 추가할 수 있어요. manifest.root, manifest.environment, manifest.users, manifest.groups를 바꾸거나, 기존 항목을 제거하거나, 항목 타입을 교체하거나, 마운트 항목을 추가·변경할 수는 없어요.
  • Runner API: SandboxAgent 실행은 여전히 일반 Runner.run(), Runner.run_sync(), Runner.run_streamed() API를 사용해요.

전체 예시: 코딩 작업

이 코딩 스타일 예시는 좋은 기본 시작점이에요:

import asyncio
from pathlib import Path

from agents import ModelSettings, Runner
from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxAgent, SandboxRunConfig
from agents.sandbox.capabilities import (
    Capabilities,
    LocalDirLazySkillSource,
    Skills,
)
from agents.sandbox.entries import LocalDir
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

EXAMPLE_DIR = Path(__file__).resolve().parent
HOST_REPO_DIR = EXAMPLE_DIR / "repo"
HOST_SKILLS_DIR = EXAMPLE_DIR / "skills"
TARGET_TEST_CMD = "sh tests/test_credit_note.sh"

def build_agent(model: str) -> SandboxAgent[None]:
    return SandboxAgent(
        name="Sandbox engineer",
        model=model,
        instructions=(
            "Inspect the repo, make the smallest correct change, run the most relevant checks, "
            "and summarize the file changes and risks. "
            "Read `repo/task.md` before editing files. Stay grounded in the repository, preserve "
            "existing behavior, and mention the exact verification command you ran. "
            "Use the `$credit-note-fixer` skill before editing files. "
            "This example leaves `SandboxRunConfig.cwd` unset, so `apply_patch` paths stay "
            "relative to the sandbox workspace root and edits still target `repo/...`."
        ),
        # 저장소와 작업 파일을 manifest에 넣습니다.
        default_manifest=Manifest(
            entries={
                "repo": LocalDir(src=HOST_REPO_DIR),
            }
        ),
        capabilities=Capabilities.default() + [
            Skills(
                lazy_from=LocalDirLazySkillSource(
                    # SDK 프로세스가 읽는 호스트 경로입니다.
                    # 요청된 스킬은 샌드박스의 `skills_path`에 복사됩니다.
                    source=LocalDir(src=HOST_SKILLS_DIR),
                )
            ),
        ],
        model_settings=ModelSettings(tool_choice="required"),
    )

async def main(model: str, prompt: str) -> None:
    result = await Runner.run(
        build_agent(model),
        prompt,
        run_config=RunConfig(
            sandbox=SandboxRunConfig(client=UnixLocalSandboxClient()),
            workflow_name="Sandbox coding example",
        ),
    )
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(
        main(
            model="gpt-5.6-sol",
            prompt=(
                "Open `repo/task.md`, use the `$credit-note-fixer` skill, fix the bug, "
                f"run `{TARGET_TEST_CMD}`, and summarize the change."
            ),
        )
    )

examples/sandbox/docs/coding_task.py 참고. 이 예시는 Unix-로컬 실행에서 결정적으로 검증될 수 있도록 작은 셸 기반 저장소를 사용해요. 실제 작업 저장소는 물론 Python, JavaScript, 또는 무엇이든 될 수 있어요.

일반적인 패턴

위의 전체 예시에서 시작하세요. 많은 경우 같은 SandboxAgent를 그대로 유지하면서 샌드박스 클라이언트, 샌드박스-세션 출처, 또는 워크스페이스 출처만 바꾸면 돼요.

샌드박스 클라이언트 전환

에이전트 정의는 그대로 두고 실행 구성만 바꾸세요. 컨테이너 격리나 이미지 일치(pariity)를 원하면 Docker를, 제공자 관리 실행을 원하면 호스팅 제공자를 사용하세요. 예시와 제공자 옵션은 샌드박스 클라이언트를 참고하세요.

워크스페이스 오버라이드

에이전트 정의는 그대로 두고 새-세션 manifest만 교체하세요:

from agents.run import RunConfig
from agents.sandbox import Manifest, SandboxRunConfig
from agents.sandbox.entries import GitRepo
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

run_config = RunConfig(
    sandbox=SandboxRunConfig(
        client=UnixLocalSandboxClient(),
        manifest=Manifest(
            entries={
                "repo": GitRepo(repo="openai/openai-agents-python", ref="main"),
            }
        ),
    ),
)

같은 에이전트 역할이 에이전트를 다시 만들지 않고 다른 저장소, 패킷, 작업 번들에 대해 실행되어야 할 때 사용하세요. 위의 검증된 코딩 예시가 일회성 오버라이드 대신 default_manifest로 같은 패턴을 보여줘요.

샌드박스 세션 주입

명시적 수명주기 제어, 실행 후 검사, 또는 출력 복사가 필요할 때 라이브 샌드박스 세션을 주입하세요:

from agents import Runner
from agents.run import RunConfig
from agents.sandbox import SandboxRunConfig
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

client = UnixLocalSandboxClient()
sandbox = await client.create(manifest=agent.default_manifest)

async with sandbox:
    result = await Runner.run(
        agent,
        prompt,
        run_config=RunConfig(
            sandbox=SandboxRunConfig(session=sandbox),
        ),
    )

실행 후 워크스페이스를 검사하거나 이미 시작된 샌드박스 세션 위에서 스트리밍하고 싶을 때 사용하세요. examples/sandbox/docs/coding_task.pyexamples/sandbox/docker/docker_runner.py를 참고하세요.

세션 상태에서 재개

RunState 밖에서 샌드박스 상태를 이미 직렬화했다면 러너가 그 상태에서 재연결하게 하세요:

from agents.run import RunConfig
from agents.sandbox import SandboxRunConfig

serialized = load_saved_payload()
restored_state = client.deserialize_session_state(serialized)

run_config = RunConfig(
    sandbox=SandboxRunConfig(
        client=client,
        session_state=restored_state,
    ),
)

샌드박스 상태가 자체 저장소나 작업 시스템에 있고 Runner가 그것에서 직접 재개하길 원할 때 사용하세요. 직렬화/역직렬화 흐름은 examples/sandbox/extensions/blaxel_runner.py를 참고하세요.

세션-상태 직렬화는 네이티브 host_path 값을 생략해요. 호스트 기반 승인을 재개하려면 SandboxRunConfig.manifestagent.default_manifest로 현재 신뢰된 manifest를 제공하세요. 그렇지 않으면 재개가 샌드박스가 시작되기 전에 실패해요. 직렬화된 것 또는 다른 신뢰할 수 없는 입력에서 호스트 경로를 유도하지 마세요.

세션-상태와 RunState 직렬화는 또한 클라우드 마운트 자격 증명, 자격 증명을 담은 헬퍼 구성, 인-컨테이너 자격 증명 노출 승인을 제거해요. 마운트된 세션 재개를 지원하는 백엔드에서, 상태에 삭제된(redacted) 마운트 권한이 포함되어 있다면 SandboxRunConfig.manifestagent.default_manifest로 현재 신뢰된 manifest를 제공하세요. "data"라는 마운트 항목이 마운트 범위 승인이 필요할 때는 재개 전에 trusted_manifest = trusted_manifest.with_in_container_mount_credential_exposure_acknowledged("data")로 복사된 manifest를 유지하세요. 광범위한 권한에는 trusted_manifest = trusted_manifest.with_in_container_mount_broad_credential_exposure_acknowledged("data")를, 마운트가 두 권한 클래스를 모두 사용할 때는 두 메서드를 모두 호출하세요. 승인이 필요한 마운트 경로를 모두 전달하세요. Agents SDK는 현재 신뢰된 manifest가 지속된 상태와 정확히 같은 자격 증명 없는 마운트 토폴로지를 가질 때만 자격 증명을 복원해요. 신뢰 구성이 누락되거나 일치하지 않으면 재개가 샌드박스 전에 실패해요. 직렬화된 상태는 그 자체로 어떤 권한도 부여하지 않아요. VercelSandboxClient는 마운트된 세션을 재개할 수 없으므로, 신뢰된 manifest로 새 샌드박스를 시작하세요.

스냅샷에서 시작

저장된 파일과 산출물로 새 샌드박스를 시드하세요:

from pathlib import Path

from agents.run import RunConfig
from agents.sandbox import LocalSnapshotSpec, SandboxRunConfig
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

run_config = RunConfig(
    sandbox=SandboxRunConfig(
        client=UnixLocalSandboxClient(),
        snapshot=LocalSnapshotSpec(base_path=Path("/tmp/my-sandbox-snapshot")),
    ),
)

새 샌드박스 세션을 만드는 실행이 agent.default_manifest만이 아니라 저장된 워크스페이스 내용에서 시작해야 할 때 사용하세요. 로컬 스냅샷 흐름은 examples/sandbox/memory.py를, 원격 스냅샷 클라이언트는 examples/sandbox/sandbox_agent_with_remote_snapshot.py를 참고하세요.

Git에서 스킬 로드

로컬 스킬 소스를 저장소 기반 소스로 교체하세요:

from agents.sandbox.capabilities import Capabilities, Skills
from agents.sandbox.entries import GitRepo

capabilities = Capabilities.default() + [
    Skills(from_=GitRepo(repo="sdcoffey/tax-prep-skills", ref="main")),
]

스킬 번들이 자체 릴리스 주기를 갖거나 샌드박스들 사이에 공유되어야 할 때 사용하세요. examples/sandbox/tax_prep.py 참고.

도구로 노출

도구-에이전트는 자신의 샌드박스 경계를 얻거나 부모 실행의 라이브 샌드박스를 재사용할 수 있어요. 재사용은 빠른 읽기 전용 탐색 에이전트에 유용해요. 부모 실행이 사용하는 정확한 워크스페이스를, 다른 샌드박스를 만들고, 하이드레이트하고, 스냅샷하는 비용 없이 검사할 수 있으니까요.

from agents import Runner
from agents.run import RunConfig
from agents.sandbox import FileMode, Manifest, Permissions, SandboxAgent, SandboxRunConfig, User
from agents.sandbox.entries import Dir, File
from agents.sandbox.sandboxes.unix_local import UnixLocalSandboxClient

coordinator = User(name="coordinator")
explorer = User(name="explorer")

manifest = Manifest(
    users=[coordinator, explorer],
    entries={
        "pricing_packet": Dir(
            group=coordinator,
            permissions=Permissions(
                owner=FileMode.ALL,
                group=FileMode.ALL,
                other=FileMode.READ | FileMode.EXEC,
                directory=True,
            ),
            children={
                "pricing.md": File(
                    content=b"Pricing packet contents...",
                    group=coordinator,
                    permissions=Permissions(
                        owner=FileMode.ALL,
                        group=FileMode.ALL,
                        other=FileMode.READ,
                    ),
                ),
            },
        ),
        "work": Dir(
            group=coordinator,
            permissions=Permissions(
                owner=FileMode.ALL,
                group=FileMode.ALL,
                other=FileMode.NONE,
                directory=True,
            ),
        ),
    },
)

pricing_explorer = SandboxAgent(
    name="Pricing Explorer",
    instructions="Read `pricing_packet/` and summarize commercial risk. Do not edit files.",
    run_as=explorer,
)

client = UnixLocalSandboxClient()
sandbox = await client.create(manifest=manifest)

async with sandbox:
    shared_run_config = RunConfig(
        sandbox=SandboxRunConfig(session=sandbox),
    )

    orchestrator = SandboxAgent(
        name="Revenue Operations Coordinator",
        instructions="Coordinate the review and write final notes to `work/`.",
        run_as=coordinator,
        tools=[
            pricing_explorer.as_tool(
                tool_name="review_pricing_packet",
                tool_description="Inspect the pricing packet and summarize commercial risk.",
                run_config=shared_run_config,
                max_turns=2,
            ),
        ],
    )

    result = await Runner.run(
        orchestrator,
        "Review the pricing packet, then write final notes to `work/summary.md`.",
        run_config=shared_run_config,
    )

여기서 부모 에이전트는 coordinator로 실행되고, 탐색 도구-에이전트는 같은 라이브 샌드박스 세션 안에서 explorer로 실행돼요. pricing_packet/ 항목은 other 사용자가 읽을 수 있으므로 탐색기가 빠르게 검사할 수 있지만 쓰기 비트는 없어요. work/ 디렉터리는 coordinator 사용자/그룹에만 있으므로, 부모는 최종 산출물을 쓸 수 있지만 탐색기는 읽기 전용으로 남아요.

도구-에이전트가 자신의 컨테이너가 필요할 때는 Docker 세션을 만드는 샌드박스 RunConfig를 주세요:

from docker import from_env as docker_from_env

from agents.run import RunConfig
from agents.sandbox import SandboxAgent, SandboxRunConfig
from agents.sandbox.sandboxes.docker import DockerSandboxClient, DockerSandboxClientOptions

rollout_agent = SandboxAgent(
    name="Rollout Reviewer",
    instructions="Inspect the rollout packet and summarize implementation risk.",
)

rollout_agent.as_tool(
    tool_name="review_rollout_risk",
    tool_description="Inspect the rollout packet and summarize implementation risk.",
    run_config=RunConfig(
        sandbox=SandboxRunConfig(
            client=DockerSandboxClient(docker_from_env()),
            options=DockerSandboxClientOptions(image="python:3.14-slim"),
        ),
    ),
)

도구-에이전트가 파일을 독립적으로 편집해야 할 때는 별도 워크스페이스를, 다른 백엔드나 이미지가 필요할 때는 별도 세션을 사용하세요. 신뢰할 수 없는 명령에는 필요한 격리를 제공하는 백엔드와 구성을 고르세요. 별도의 Unix-로컬 세션만으로는 Linux OS 격리를 제공하지 않아요. 별도 로컬 워크스페이스는 examples/sandbox/sandbox_agents_as_tools.py를 참고하세요.

로컬 도구와 MCP와 결합

같은 에이전트에서 일반 도구를 사용하면서 샌드박스 워크스페이스를 유지하세요:

from agents.sandbox import SandboxAgent
from agents.sandbox.capabilities import Shell

agent = SandboxAgent(
    name="Workspace reviewer",
    instructions="Inspect the workspace and call host tools when needed.",
    tools=[get_discount_approval_path],
    mcp_servers=[server],
    capabilities=[Shell()],
)

워크스페이스 검사가 에이전트 작업의 일부일 뿐일 때 사용하세요. examples/sandbox/sandbox_agent_with_tools.py 참고.

메모리

향후 샌드박스-에이전트 실행이 이전 실행에서 배워야 할 때 Memory 능력을 사용하세요. 메모리는 SDK의 대화형 Session 메모리와는 달라요. 레슨을 샌드박스 워크스페이스 안의 파일로 정제(distill)하고, 이후 실행이 그 파일을 읽을 수 있어요.

설정, 읽기/생성 동작, 멀티턴 대화, 레이아웃 격리는 에이전트 메모리를 참고하세요.

합성 패턴

단일 에이전트 패턴이 명확해지면 다음 설계 질문은 더 큰 시스템에서 샌드박스 경계가 어디에 있어야 하는지예요.

샌드박스 에이전트는 계속해서 SDK의 나머지와 합성돼요:

  • Handoffs: 비-샌드박스 인테이크 에이전트에서 문서 중심 작업을 샌드박스 검토자로 handoff.
  • Agents as tools: 여러 샌드박스 에이전트를 도구로 노출. 보통 각 Agent.as_tool(...) 호출에 run_config=RunConfig(sandbox=SandboxRunConfig(...))를 전달해서 각 도구가 자신의 세션을 갖게 함. 각 세션이 제공하는 격리는 백엔드와 구성이 결정해요.
  • MCP와 일반 함수 도구: 샌드박스 능력은 mcp_servers와 일반 Python 도구와 공존할 수 있어요.
  • Running agents: 샌드박스 실행도 일반 Runner API를 사용해요.

특히 흔한 두 패턴이 있어요:

  • 워크스페이스 격리가 필요한 부분에만 비-샌드박스 에이전트가 샌드박스 에이전트로 handoff.
  • 오케스트레이터가 여러 샌드박스 에이전트를 도구로 노출. 보통 각 Agent.as_tool(...) 호출에 별도 샌드박스 RunConfig를 두어 각 도구가 자신의 워크스페이스를 갖게 함.

턴과 샌드박스 실행

handoff와 agent-as-tool 호출을 따로 설명하는 게 도움이 돼요.

handoff에서는 여전히 하나의 최상위 실행과 하나의 최상위 턴 루프가 있어요. 활성 에이전트가 바뀌지만 실행이 중첩되지는 않아요. 비-샌드박스 인테이크 에이전트가 샌드박스 검토자로 handoff하면, 같은 실행에서 다음 모델 호출이 샌드박스 에이전트를 위해 준비되고, 그 샌드박스 에이전트가 다음 턴을 차지하게 돼요. 다시 말해 handoff는 같은 실행의 다음 턴을 어떤 에이전트가 소유하는지 바꿔요. examples/sandbox/handoffs.py 참고.

Agent.as_tool(...)에서는 관계가 달라요. 외부 오케스트레이터는 하나의 외부 턴으로 그 도구를 호출하기로 결정하고, 그 도구 호출이 샌드박스 에이전트의 중첩 실행을 시작해요. 중첩 실행은 자신의 턴 루프, max_turns, 승인, 그리고 보통 자신의 샌드박스 RunConfig를 가져요. 하나의 중첩 턴으로 끝날 수도 있고 여러 번 걸릴 수도 있어요. 외부 오케스트레이터 관점에서 그 모든 작업은 여전히 도구 호출 하나 뒤에 있으므로, 중첩 턴은 외부 실행의 턴 카운터를 늘리지 않아요. examples/sandbox/sandbox_agents_as_tools.py 참고.

승인 동작도 같은 분할을 따라요:

  • handoff에서는 샌드박스 에이전트가 이제 그 실행의 활성 에이전트이므로 승인이 같은 최상위 실행에 남아요.
  • Agent.as_tool(...)에서는 샌드박스 도구-에이전트 안에서 발생한 승인이 여전히 외부 실행에 표면화되지만, 저장된 중첩 실행 상태에서 오고, 외부 실행이 재개될 때 중첩 샌드박스 실행을 재개해요.

더 알아보기 (Learn more)