Skip to content

CH 11 · 도구와 샌드박스

분량약 3,430자소요 시간약 15분선행 지식CH 03–05를 이미 실행해 봤어야 합니다난이도재현 가능

본장 목표

앞선 장들에서 Agent가 작동하는 모습을 보셨습니다. 파일을 스스로 읽고, 명령을 실행하고, 웹을 검색하고, 민감한 작업에 대해서는 사용자에게 다이얼로그를 띄우기도 합니다. 이번 장에서는 "도구"와 "샌드박스"를 분해합니다. 도구가 어떻게 호출되는지, 어떤 검사를 거치는지, 샌드박스가 Agent를 어떻게 가두는지, 경계에 부딪히면 어떻게 되는지. 이 두 메커니즘을 이해하면, Agent가 왜 사용자 컴퓨터 위에서 "대담하게" 움직일 수 있는지, 그리고 실제로는 무엇을 건드릴 수 없는지가 명확해집니다.

도구: Agent의 "손"

dsh에서 Agent 자체는 아무것도 움직일 수 없습니다. 모든 "행동"은 도구를 통해 이루어집니다 — 파일 읽기, 명령 실행, 웹 검색, 어느 하나하나가 도구입니다. CH 08에서 도구는 "캐비닛 + 문지기"라고 했는데, 여기서는 한 단계 더 풀어보겠습니다.

시스템 안에서의 도구는 다음과 같은 모습입니다.

부분하는 일
schema모델이 보는 "사용 설명서": 이름, 설명, 매개변수 (JSON Schema)
실행 함수실제로 작업을 수행하는 코드
출력 선언완료 시 반드시 반환해야 하는 구조
스케줄링 메타데이터병렬 실행 가능 여부, 타임아웃, UI 표시 방식

핵심은 첫 번째 항목입니다. 모델의 눈에는 도구의 이름, 설명, 매개변수만 보입니다 — 실행 함수, 출력 선언, 타임아웃, 병렬 플래그는 모델에게 보이지 않습니다. 이것이 첫 번째 안전 계층입니다. 모델은 "이 도구가 존재하고, 매개변수는 이런 식으로 채운다"라는 것만 알 뿐, 내부 구현이 어떻게 되어 있는지 알 수 없고, 실행 로직을 우회할 수도 없습니다.

도구 호출 한 번: 확장 가능한 파이프라인

모델이 도구를 호출하라고 한 뒤, 곧바로 실행되지는 않습니다. 대신 확장 가능한 파이프라인을 거치게 됩니다. 공식 팀이 설계한 이 파이프라인은 모든 단계가 플러그인에 의해 가로채거나 강화될 수 있도록 만들어졌습니다 — 다시 한 번 "모든 것은 플러그인"입니다 (다이어그램에서는 선택 단계인 finalizeContent를 생략했습니다. 도구 자체의 파이프 끝 콜백이라 본 흐름에 영향을 주지 않습니다).

도구 호출 한 번: 모델 요청에서 권위 있는 결과까지 (도식)

순서대로 따라가 보겠습니다.

  1. 모델 요청 (Model request): 모델이 tool/call (도구 이름 + 매개변수)을 발행합니다. 먼저 매개변수가 검증되며, 잘못되면 곧바로 오류(INVALID_ARGS)가 던져지고 실행되지 않습니다.
  2. pre-execute: 첫 번째 검사 지점입니다. 여기서 호출에 대해 allow / deny / ask를 결정합니다 — UI에서 보시는 승인 다이얼로그가 바로 이 계층에서 일어납니다.
  3. guard: 단조(monotonic) 가드입니다. 한 단계가 경계를 슬쩍 완화할 수 없도록, 더 엄격해질 수는 있어도 느슨해질 수는 없습니다.
  4. execute: 실제로 실행합니다. 샌드박스는 이 단계에 걸립니다 — 명령이 실제로 실행되기 전에 파일 껍데기로 감쌉니다 (아래 참조).
  5. post-execute: 결과를 검사하고, 필요하면 교체할 수도 있습니다.
  6. result: 권위 있는 결과를 만들어 모델에 되먹이고, 다음 라운드로 들어갑니다.

모든 단계마다 플러그인이 후크를 걸 수 있습니다 — 그래서 뒤에서 플러그인을 작성할 때 "특정 도구 호출을 가로채는" 플러그인을 만들 수 있습니다 (CH 21에서 후크를 다룹니다). 이 파이프라인을 읽으면 승인, 샌드박스, 로그 "안전 부품"이 어디에 걸려 있는지 알 수 있습니다.

샌드박스: 명령을 감싸는 "파일 껍데기"

CH 04에서 세 가지 권한 등급(read-only / workspace-write / danger-full-access)을 UI 조작 관점에서 다루었습니다. 여기서는 메커니즘을 봅니다. 샌드박스는 파일 시스템 효과만 통제하며, 네트워크와 프로세스 가시성은 관할 밖입니다.

공식 팀은 샌드박스를 "정책"과 "백엔드" 두 개의 분리된 계층으로 설계했습니다.

샌드박스: 정책이 경계를 정하고, 백엔드가 강제합니다 (도식)

  • 정책 (SandboxPolicy): 매 호출마다 다시 파싱됩니다 — 모드 + workspace root. workspace root는 현재 세션의 cwd에서 파생됩니다.
  • 백엔드 (SandboxProvider): 현재 플랫폼에서 명령을 제한된 프로세스로 감쌉니다. 플랫폼마다 구현이 다릅니다 — Linux는 bwrap / Landlock (커널 레벨 비특권 접근 통제), macOS는 Seatbelt, Windows는 ACL로 제한된 토큰 러너입니다.

기억해 둘 만한 설계가 몇 가지 있습니다.

fail-closed: 이것이 보안의 핵심입니다. 현재 환경에 사용 가능한 샌드박스 백엔드가 없으면, 시스템은 곧바로 SANDBOX_UNAVAILABLE 오류를 보고하며, 절대로 "샌드박스 없이 알몸으로 실행"으로 조용히 강등하지 않습니다. 실행을 거부하는 편이 위험을 풀어버리는 것보다 낫습니다.

danger-full-access는 껍데기를 씌우지 않습니다: 제한 모드(read-only / workspace-write)만 샌드박스 래퍼를 거칩니다. 완전 권한 모드는 어떠한 파일 격리도 없이 원래 명령을 곧바로 spawn합니다 — 그래서 UI에서 세 번째 등급으로 전환할 때 한 번 더 확인하는 것입니다.

강제 무결성이 full / partial로 나뉩니다: 대부분의 경우 백엔드가 약속한 모든 파일 효과를 통제할 수 있습니다 (full). 하지만 오래된 Linux 커널 ABI나 일부 Windows 경계에서는 일부만 통제할 수 있고 (partial), 절대적인 보장이 필요한 시나리오라면 이 점을 알아 두어야 합니다. 평소 일반적인 사용에서는 기본값인 workspace-write가 충분히 안정적입니다.

직접 해보기: 눈으로 확인하기

1단계: 트래젝토리에서 도구 호출 보기

Web UI에서 어떤 동작을 하는 작업(예: CH 05의 리포지토리 요약 같은 종류)을 실행합니다. 실행이 끝난 뒤 트래젝토리 (Trajectory) 탭으로 전환하고, 아무 TOOL 행이나 클릭합니다. 오른쪽 패널에 핵심 네 탭이 있고, 위에서 본 도구 구조와 정확히 대응합니다.

  • Schema: 도구의 "사용 설명서" (이름, 설명, 매개변수)
  • Payload: 이번 호출에 실제로 보낸 매개변수
  • Result: 반환된 결과
  • Summary / Timing: 요약과 경과 시간

아래 그림은 실제 호출입니다. 왼쪽에서 타임라인의 TOOL 행(web_search)을 선택했고, 오른쪽 패널에는 모든 탭이 펼쳐져 있습니다. 중간에 네트워크 문제로 실패한 pwsh 명령 두 개가 보이며, Agent가 곧바로 web_search로 전환했습니다. 이것이 정확히 "도구가 실패하면 길을 바꿀 수 있다"의 실제 사례입니다.

트래젝토리의 도구 호출 상세: 왼쪽 TOOL 행 선택, 오른쪽 Schema / Payload / Result 탭 펼침

2단계: 승인 화면 보기

기본 workspace-write 권한에서, Agent에게 workspace 바깥에 파일을 쓰게 합니다. 여기서는 E:\software-workspace\doubaowork\doubao 아래에 인사 파일을 만들게 했습니다 — 그 디렉터리는 현재 워크스페이스에 포함되어 있지 않습니다.

Agent가 워크스페이스 바깥에 파일 쓰게 하기: 명령 입력

실제로 일어나는 일은 두 단계라는 점에 유의하세요.

  1. 첫 쓰기는 곧바로 벽에 부딪힙니다 — 트래젝토리에 Write · Error: [sandbox: file access denied under workspace-write mode]가 표시됩니다.
  2. Agent는 대상이 워크스페이스 바깥이라는 것을 깨닫고 스스로 승격(에스컬레이션)을 요청합니다. pre-execute 계층의 승인 다이얼로그는 바로 이 시점에야 뜹니다. 샌드박스를 danger-full-access로 올리고, 사유를 함께 표시합니다. 하단의 두 버튼은 **Deny (거부)**와 **Allow once (이번 한 번 허용)**입니다.

승격 승인 다이얼로그: 거부 / 이번 한 번 허용

"Allow once"를 누르면 이번 한 번의 쓰기만 허용되고, "Deny"를 누르면 다른 방법을 찾아야 합니다.

3단계: read-only로 전환하고 보기

입력창에 /permission을 입력해 read-only로 전환하면 (입력 후 UI에는 permission · preset read-only가 표시됩니다), Agent에게 파일을 쓰게 합니다. 결과는 위와 비슷하지만 한 가지 핵심 차이가 있습니다.

read-only 모드 쓰기 거부: 오류 + 승격 요청

  1. 첫 쓰기도 벽에 부딪힙니다 — 다만 오류 메시지가 다릅니다: Write · Error: [sandbox: file access denied under read-only mode].
  2. Agent도 승격을 요청하지만, 이번에는 대상이 escalate sandbox to workspace-write입니다. danger-full-access까지 끌어올릴 필요가 없고, 보통 쓰기 권한이면 충분합니다.

세 가지 "벽" 사례를 비교하면 한 가지가 분명해집니다. 어느 권한 등급이든, 쓰기가 실패하면 Agent는 먼저 벽에 부딪히고 나서야 다이얼로그를 띄웁니다. 차이는 오류 메시지(workspace-write 모드 / read-only 모드)와 요청하는 다음 등급(danger-full-access / workspace-write)에 있습니다. "Deny" 버튼은 항상 존재합니다 — 이것이 샌드박스 설계의 핵심입니다. Agent는 "요청"할 수 있지만, "줄 것인가 말 것인가"는 항상 사용자의 몫입니다.

이번 장에서 배운 것

다음 항목들을 스스로 완수할 수 있으면 합격입니다.

  • [ ] 도구가 어떤 부분들로 구성되는지, 그중 어떤 것이 모델에 보이고 어떤 것이 보이지 않는지 설명할 수 있다
  • [ ] 도구 실행 파이프라인(모델 요청 → pre-execute → guard → execute → post-execute → result)을 그리고, 승인 단계와 샌드박스가 각각 어디에 걸려 있는지 설명할 수 있다
  • [ ] 샌드박스가 "파일 시스템 효과만 통제한다"는 것이 무엇을 의미하는지, 왜 그런지 설명할 수 있다
  • [ ] fail-closed를 설명할 수 있다: 사용 가능한 샌드박스 백엔드가 없을 때 시스템이 하는 일 (SANDBOX_UNAVAILABLE 보고, 알몸으로 실행하지 않음)
  • [ ] 트래젝토리에서 도구 호출을 클릭해 열어 보고, Schema / Payload / Result를 이해한다
  • [ ] 권한 등급별로 쓰기 거부 시의 오류(workspace-write 모드 / read-only 모드)와 Agent가 요청하는 승격 대상(danger-full-access / workspace-write)을 말할 수 있다
  • [ ] 다음을 설명할 수 있다: 쓰기가 실패하면 Agent는 먼저 벽에 부딪히고 나서야 다이얼로그를 띄우지만, "Deny"는 항상 사용자의 손에 있다

Open Source · MIT · Community Driven