5장. 운영·비용·안전

학습 목표

  • 최대 턴 수, 예산 한도, 진전 없는 루프 탐지로 폭주와 무한 루프를 막는다.
  • 타임아웃과 재시도를 오류 유형과 멱등성에 맞게 설계하고, 재시도 폭증을 막는다.
  • 되돌림 가능성과 영향 범위로 사람 승인 지점을 고르고, 승인을 실행 내용에 묶는다.
  • 훅·콜백·가드레일을 실행 경로에 배치하고 각 장치가 닿는 범위를 안다.
  • 샌드박스와 최소 권한으로 피해 범위를 가두고, 사고 대응 순서를 세운다.

들어가며

어느 금요일 밤, 리서치 에이전트가 같은 검색을 수백 번 반복했습니다. 검색 결과 형식이 바뀌어 에이전트가 결과를 읽지 못했고, 읽지 못하니 다시 검색했습니다. 턴 제한은 꺼져 있었고, 비용 경보는 월요일 아침에야 메일함에서 발견되었습니다. 다른 회사에서는 정반대 일이 있었습니다. 모든 행동에 사람 승인을 걸었더니 하루 수백 건의 승인 요청이 쏟아졌고, 승인자들은 몇 초 만에 버튼을 눌렀습니다. 결국 위험한 요청 하나가 아무도 읽지 않은 채 통과했습니다.

두 사고의 공통점은 안전을 모델의 판단이나 사람의 주의력에 기댔다는 것입니다. 이 장은 안전을 시스템에 넣는 법을 다룹니다. 멈춤 조건·예산·타임아웃·재시도·과부하 통제로 폭주를 막고, 위험한 곳에만 승인을 두고, 훅·콜백·가드레일·샌드박스로 강제력을 실행 경로에 심은 뒤, 사고 대응 순서를 정리합니다.

핵심 개념

멈춤 조건은 시스템에 넣는다

"작업이 끝나면 멈춰라"는 지시는 권고일 뿐입니다. 멈춤은 루프를 돌리는 코드가 강제해야 합니다. OpenAI Agents SDK의 러너는 최종 출력이 나오면 끝나고, handoff나 도구 호출이 생기면 다시 도는 루프입니다. max_turns로 턴 수를 제한하며, 넘으면 MaxTurnsExceeded 예외가 발생합니다. max_turns=None을 넘기면 제한이 꺼지므로, 운영에서는 공용 실행 래퍼가 상한을 강제하게 하세요. error_handlers에 "max_turns" 처리기를 두면 예외 대신 "요청을 좁혀 다시 시도해 주세요" 같은 통제된 최종 출력을 돌려줄 수 있습니다.

result = await Runner.run(agent, "이번 주 정산 차이를 점검해", max_turns=15)

턴 제한만으로는 부족합니다. 한 턴에 비싼 도구를 여러 번 부를 수 있고, 하위 에이전트가 각자 턴을 쓰고, 같은 검색이 반복될 수 있습니다.

장치 막는 것
최대 턴 수 끝없는 루프
실행당 토큰·비용 한도 턴 수로 잡히지 않는 비용 폭주
하위 작업 수·재위임 깊이 상한 워커가 워커를 낳는 증식
진전 없는 반복 탐지 같은 시도를 되풀이하는 정체
전체 마감 시간 개별 호출은 정상인데 전체가 늘어지는 작업

멈출 때는 이유와 부분 결과, 다음 선택지를 남겨야 사용자도 운영자도 다음 결정을 내릴 수 있습니다.

예산 한도의 층

비용 한도는 여러 층에 겹쳐 겁니다. 한 층이 뚫려도 다음 층이 막도록입니다.

층 예 걸렸을 때
실행 요청 한 건의 토큰·도구 호출·금액 상한 그 실행을 멈추고 부분 결과 반환
사용자·작업 하루 사용량 경고 후 차단, 또는 저렴한 경로로 전환
테넌트·팀 고객사나 팀별 월 예산 다른 테넌트에 번지지 않게 격리
조직·제공자 계정 제공자 콘솔의 지출·속도 한도 최후의 안전망

제공자 쪽 한도도 활용하세요. Claude API에서는 조직과 워크스페이스 단위로 지출 한도를 직접 정할 수 있고, 워크스페이스마다 지출·속도 한도를 낮게 걸어 한 워크스페이스의 과다 사용이 다른 곳을 막지 않게 할 수 있습니다(기본 워크스페이스는 제외). 직접 정한 지출 한도에 이르면 대개 400 오류가(Claude Code 워크스페이스는 retry-after가 있는 429일 수 있음), 사용 등급의 월 지출 상한에 이르면 retry-after 없는 429가 옵니다. 후자는 SDK 자동 재시도를 포함한 어떤 재시도도 접근이 재개될 때까지 실패하므로, 오류 내용으로 구분해 재시도 대신 운영자 알림으로 보내세요.

OWASP Top 10 for LLM Applications 2025는 이런 위험을 무제한 소비(Unbounded Consumption, LLM10)로 분류하고, 과도한 호출로 비용을 폭증시키는 지갑 거부(denial of wallet) 공격을 예로 듭니다. 대책은 입력 크기 검증, 사용자별 속도 제한과 할당량, 타임아웃과 처리량 조절, 사용량 모니터링, 일부 기능을 유지하며 줄이는 우아한 기능 축소입니다.

여러 워커가 동시에 돌 때는 사후 정산만으로 한도를 지키기 어렵습니다. 워커를 띄우기 전에 예상 비용을 예약하고 끝난 뒤 정산하면, 동시에 출발한 워커들이 합쳐서 한도를 넘는 일을 줄일 수 있습니다.

진전 없는 루프를 탐지한다

호출 횟수가 적어도 같은 실패를 반복한다면 그 작업은 가치를 만들지 못하고 있습니다. 진전을 재는 신호는 다음과 같습니다.

  1. 같은 도구를 같은 인자로 연속 N번 호출했다.
  2. 같은 오류 메시지가 연속으로 돌아왔다.
  3. 최근 M턴 동안 새 근거, 새 파일, 상태 변화가 없었다.
  4. 실패한 뒤에도 같은 도구와 인자 조합으로 다시 시도한다.

신호가 잡히면 실행을 멈추고, 반복된 호출과 오류, 지금까지의 산출물을 묶어 사람에게 넘깁니다. 이 탐지는 도구 래퍼나 루프 코드에서 하므로 모델이 지시를 따르든 말든 작동합니다.

타임아웃과 마감 시간

타임아웃은 클라이언트가 요청 완료를 기다리는 최대 시간입니다. Amazon Builders' Library의 글은 타임아웃을 감으로 정하지 말고, 허용할 거짓 타임아웃(곧 끝났을 요청을 기다리다 포기하는 경우) 비율(예: 0.1%)을 먼저 정한 뒤 그 백분위(예: p99.9)의 하위 서비스 지연을 기준으로 삼으라고 권합니다. 원격 호출에는 연결 타임아웃과 요청 타임아웃을 모두 둡니다.

에이전트에는 작업 전체의 마감 시간도 필요합니다. 단계마다 타임아웃이 정상이어도 재시도와 단계가 쌓이면 작업이 끝없이 늘어지므로, 남은 시간을 단계에 나눠 주고 시간이 부족하면 새 단계를 시작하지 않습니다. MCP 사양도 모든 요청에 타임아웃을 두라고 권하며, 진행 알림을 받을 때 시계를 다시 잴 수는 있어도 최대 타임아웃은 늘 강제하라고 덧붙입니다. 모델 호출 쪽에서는 Claude API 문서가 10분을 넘을 수 있는 긴 요청에 스트리밍이나 배치 API를 쓰라고 권합니다.

재시도

재시도는 일시적 실패를 회복하지만, Amazon의 표현대로 "이기적"입니다. 성공 확률을 높이려고 서버 용량을 더 씁니다. 과부하 상황의 재시도는 과부하를 키웁니다. 원칙은 다음과 같습니다.

원칙 내용
오류 유형 구분 일시적 오류(속도 제한, 과부하, 연결 끊김)만 재시도한다. 입력 오류와 업무 규칙 위반은 그대로 재시도하지 않고 오류 정보로 회복한다
지수 백오프와 지터 대기 시간을 지수적으로 늘리되 상한을 두고, 무작위성(지터)을 더해 재시도가 한꺼번에 몰리지 않게 한다
횟수 제한 시도 횟수에 상한을 둔다
한 계층에서만 계층마다 따로 재시도하면 곱으로 불어난다. Amazon의 글은 다섯 겹의 호출에서 층마다 세 번씩 시도하면 데이터베이스 부하가 243배(3⁵)로 는다고 경고한다
서버의 대기 지시 존중 retry-after가 있으면 그만큼 기다린다
멱등성 부작용이 있는 API는 멱등성을 제공할 때만 안전하게 재시도할 수 있다

SDK의 기본 재시도도 계산에 넣으세요. Claude의 공식 SDK는 연결 오류, 속도 제한, 5xx 같은 일시적 실패를 기본 두 번, 지수 백오프로 재시도하고 retry-after를 따릅니다. 그 위에 도구 래퍼가 세 번, 에이전트가 다시 세 번 재시도하면 한 번의 실패가 수십 번의 호출이 됩니다. 어느 계층이 재시도를 맡을지 하나로 정하세요. 실행 여부를 알 수 없는 실패(2장)는 재시도보다 처리 결과 조회가 먼저입니다.

동시성과 과부하 통제

한 작업의 폭주가 다른 사용자의 정상 업무까지 막아서는 안 됩니다. 이를 위한 장치는 네 가지입니다.

  • 동시성 제한: 조직·사용자·도구별로 동시에 도는 실행과 외부 호출 수를 제한한다.
  • 대기열과 유입 제한: 대기열이 길어지면 새 작업 접수를 늦추거나 우선순위를 낮춘다. 대기에도 만료 시간을 둔다.
  • 회로 차단기(circuit breaker): 하위 서비스의 실패율이 임계값을 넘으면 일정 시간 호출 자체를 막아, 실패할 호출로 자원을 낭비하지 않고 하위 서비스가 회복할 시간을 준다. 전기 차단기가 과전류에 회로를 끊는 것과 같다. 평소에는 닫힘(통과), 차단 중에는 열림, 일정 시간 뒤 시험 호출을 보내는 반열림을 거쳐 성공하면 다시 닫힌다.
  • 우아한 기능 축소: 처리를 못 할 때는 "접수했고 처리되면 알린다"처럼 일부 기능만 유지한다.

내부 한도는 고정 구간마다 초기화하기보다 꾸준히 채워지는 토큰 버킷 방식(Claude API도 속도 제한에 이 방식을 쓴다)이 순간 폭주를 다루기 쉽습니다.

사람 승인 지점

모든 행동에 승인을 걸면 승인자는 곧 읽지 않고 누릅니다. 승인 피로입니다. OpenAI 가이드는 도구마다 읽기·쓰기 여부, 되돌림 가능성, 필요한 계정 권한, 금전적 영향으로 낮음·중간·높음 위험 등급을 매겨, 고위험 함수 실행 전 가드레일 검사나 사람 인계 같은 조치를 자동으로 연결하라고 권합니다. 사람 개입의 두 계기로는 실패 임계값 초과(여러 번 시도해도 의도를 이해하지 못함)와 고위험 행동(주문 취소, 큰 금액 환불, 결제)을 들고, 고위험 행동은 신뢰가 쌓일 때까지 사람이 감독하라고 합니다.

판단의 두 축은 되돌림 가능성과 영향 범위입니다.

영향이 작다(개인·내부) 영향이 크다(고객·외부·금전)
되돌릴 수 있다 자동 실행 한도 안에서 자동, 넘으면 승인
되돌리기 어렵다 요청한 사용자의 확인 후 실행 권한 있는 승인자의 승인 필수

승인 화면에는 실제로 실행할 내용(대상, 금액, 수신자, 건수), 그 근거, 트레이스 링크를 보여 줍니다. 승인 요청이 줄어야 승인 한 건 한 건이 진지하게 읽힙니다.

승인을 실행 내용에 묶는다

승인과 실행 사이에는 틈이 있습니다. 승인자가 본 금액과 실제 실행 인자가 다르거나, 승인 뒤 대상이 바뀌거나, 같은 승인으로 두 번 실행될 수 있습니다. 승인을 실행 내용에 결속하면 이 틈이 닫힙니다.

  1. 승인 요청에 실행 인자 전체를 담고, 키 순서를 고정한 정규화 직렬화의 해시나 미리보기 ID(2장)를 승인 기록에 저장한다.
  2. 실행 직전에 실제 인자가 승인된 내용과 같은지 서버에서 대조한다. 다르면 실행하지 않고 다시 승인을 받는다.
  3. 승인에 유효기간을 두고, 한 번 쓰면 소진되게 한다.
  4. 승인자의 권한도 실행 시점에 다시 확인한다. 승인 자체가 새 업무 권한을 주지는 않는다.

OpenAI Agents SDK에서는 도구에 needs_approval을 걸면 실행이 멈추고 승인 대기 항목이 결과의 interruptions에 담깁니다. 이를 RunState로 바꿔 승인 또는 거절한 뒤 같은 상태로 이어 실행합니다. 직렬화된 상태에는 대기 중인 도구 인자가 들어 있으므로, 문서 권고대로 서버가 통제하는 저장소에 두고 신뢰할 수 있는 스냅샷만 복원하세요. 로컬 ShellTool과 ApplyPatchTool은 승인이 선택 사항이라 needs_approval 기본값이 False라는 점도 기억해 둘 만합니다.

실행 경로에 두는 차단 장치

금지 사항은 프롬프트가 아니라 실행 경로에 둡니다. 도구가 실제로 실행되기 직전에 코드가 검사하고 막을 수 있어야 합니다.

프레임워크 실행 경로의 차단 장치
Claude Code PreToolUse 훅이 종료 코드 2로 끝나면 도구 호출이 막히고 훅의 표준 오류 출력이 차단 사유로 Claude에 전달된다. 이 차단은 권한 규칙 평가 전에 일어나 allow 규칙이 있어도 막힌다. 반대로 훅이 허용을 반환해도 deny·ask 규칙은 건너뛰지 못한다. 권한 규칙은 deny → ask → allow 순으로 평가되고 처음 맞는 규칙이 결과를 정한다. 단, 설치한 mod의 tool.check 처리기는 관리 설정이나 Team·Enterprise 로그인이 없는 환경에서 이 결과를 뒤집을 수 있다
Google ADK before_tool_callback이 값을 반환하면 실제 도구 실행을 건너뛰고 그 값을 도구 결과로 쓴다. None이면 그대로 실행된다. 보안 가드레일과 정책은 콜백보다 플러그인으로 구성하라고 권한다. 플러그인은 Runner에 한 번 등록해 모든 에이전트·도구·모델 호출에 적용되고, 객체 수준 콜백보다 먼저 실행되며, 값을 반환하면 뒤의 플러그인과 원래 동작을 건너뛴다
OpenAI Agents SDK 입력·출력 가드레일과 도구 가드레일. 위반을 알리는 신호(트립와이어)가 켜지면 예외로 실행을 멈춘다

ADK 플러그인의 단락(short-circuit) 규칙은 순서 문제를 낳습니다. Python에서는 플러그인이 등록 순서대로 돌고 처음으로 값을 반환한 플러그인에서 멈추므로, 결과를 바로 돌려주는 플러그인이 앞에 있으면 뒤의 검사는 실행되지 않습니다. 반드시 거쳐야 하는 검사의 위치를 등록 순서로 확인하세요.

가드레일의 적용 범위

OpenAI Agents SDK의 가드레일은 어디에 닿는지 정확히 알아야 합니다.

  • 입력 가드레일은 체인의 첫 에이전트에서만, 출력 가드레일은 최종 출력을 내는 에이전트에서만 돈다.
  • 입력 가드레일은 기본적으로 에이전트와 동시에 실행된다(run_in_parallel=True). 가드레일이 걸리기 전에 에이전트가 이미 토큰을 쓰고 도구를 실행했을 수 있다. 부작용이 있는 도구를 쓰는 에이전트라면 run_in_parallel=False로 가드레일이 끝난 뒤에 에이전트가 시작되게 한다. 문서도 비용 절감과 도구 부작용 방지에 이 방식을 권한다.
  • 도구 가드레일은 함수 도구(FunctionTool)를 감싼다. handoff 호출 자체에는 적용되지 않고, 호스팅 도구와 ShellTool·ApplyPatchTool 같은 내장 실행 도구는 이 파이프라인을 거치지 않으며, Agent.as_tool()은 지금 도구 가드레일 옵션을 직접 노출하지 않는다.

주의

"가드레일을 달았다"는 말만으로는 안전을 보장할 수 없습니다. 어느 에이전트의 어느 시점에, 어떤 종류의 도구 호출에 적용되는지 확인하세요. 빈틈은 대개 handoff, 호스팅 도구, 동시 실행에서 생깁니다.

샌드박스와 최소 권한

신뢰할 수 없는 입력을 다루거나 코드를 실행하는 에이전트는 모델의 판단이 아니라 환경으로 안전을 확보합니다. 샌드박스는 실행되는 프로그램이 닿을 수 있는 파일, 프로세스, 네트워크를 운영체제 수준에서 제한한 격리 환경입니다.

Claude Code의 샌드박스가 좋은 예입니다. 기본으로는 꺼져 있고, 켜면 셸 명령과 그 하위 프로세스에 운영체제가 강제하는 경계(macOS는 Seatbelt, Linux와 WSL2는 bubblewrap)가 적용됩니다. Windows 네이티브에서는 샌드박스 없이 실행되므로 WSL2 안에서 써야 합니다. 문서가 밝히는 경계의 모양은 다음과 같습니다.

  • 쓰기는 작업 디렉터리와 임시 디렉터리 등으로 제한되지만, 읽기는 기본적으로 ~/.ssh 같은 자격 증명 파일을 포함해 기기 대부분에 열려 있다. 막으려면 denyRead를 설정한다.
  • 네트워크는 허용 도메인 목록(처음에는 비어 있다)을 검사하는 프록시를 거친다. 문서는 github.com 같은 넓은 도메인을 허용하면 데이터 유출 경로가 될 수 있다고 경고한다.
  • 내장 파일·웹 도구는 샌드박스 밖에서 돌며 권한 규칙을 따른다. 명령 훅과 로컬 MCP 서버는 사용자의 전체 권한으로 실행되므로, 그 내부 동작까지 막으려면 별도 격리가 필요하다.
  • 명령이 샌드박스에서 실패하면 샌드박스 밖 재시도를 요청할 수 있는데, allowUnsandboxedCommands: false로 이 탈출구를 막을 수 있다. 샌드박스를 시작할 수 없을 때 아예 실행하지 않게 하려면 failIfUnavailable을 켠다.

문서는 권한 규칙과 샌드박스를 보완 계층으로 설명합니다. 프롬프트 인젝션이 모델의 판단을 우회하더라도 샌드박스 제한은 그대로 남기 때문입니다. 확인을 모두 건너뛰는 bypassPermissions 모드는 피해가 번지지 않는 컨테이너나 VM 같은 격리 환경에서만 쓰라고 경고합니다.

OWASP는 에이전트가 해로운 행동을 하게 되는 위험을 과도한 권한 위임(Excessive Agency, LLM06)으로 묶고, 뿌리를 과도한 기능·권한·자율성으로 봅니다. 대책은 확장 기능과 그 범위의 최소화, 열린 범위의 확장 피하기, 최소 권한, 사용자의 권한 맥락 안에서 실행, 영향이 큰 행동의 사람 승인, 그리고 허용 여부를 LLM이 아니라 하위 시스템이 판단하는 완전 중재(complete mediation)입니다.

프롬프트 인젝션과 권한 분리

프롬프트 인젝션은 입력이 모델의 행동을 의도와 다르게 바꾸는 취약점입니다(OWASP LLM01). 사용자가 직접 넣는 직접 인젝션과, 웹 페이지나 파일처럼 외부 출처의 내용에 숨은 간접 인젝션이 있습니다. 도구를 가진 에이전트에게는 간접 인젝션이 특히 위험합니다. 읽기만 하려던 문서가 행동을 지시하기 때문입니다.

OWASP가 드는 대책은 모델 행동을 제약하기, 기대하는 출력 형식을 정의하고 검증하기, 입출력 필터링, 권한 통제와 최소 권한, 고위험 행동에 사람 승인, 외부 내용을 분리하고 표시하기, 공격 시뮬레이션을 포함한 적대적 시험입니다. 설계의 핵심은 인젝션을 완벽히 탐지할 수 없다고 가정하는 것입니다. 그래서 신뢰할 수 없는 내용을 읽는 단계와 되돌릴 수 없는 행동을 하는 단계 사이에 권한 경계를 둡니다. 외부 내용을 읽은 뒤에는 외부 발송 도구에 사람 확인을 요구하거나, 읽기 전용 하위 에이전트가 요약만 넘기게 하는 식입니다.

사고 대응

에이전트가 잘못된 일괄 작업을 했다면 다음 순서를 지킵니다.

  1. 차단: 쓰기 도구나 에이전트 자체를 즉시 끊는다(킬 스위치). 배포 없이 바꿀 수 있는 설정값(기능 플래그)으로 도구별·테넌트별·전체 단위로 끊게 준비하고, 끊을 때 트레이스와 로그가 버려지지 않게 한다.
  2. 범위 확정: 트레이스와 감사 로그로 영향받은 대상과 시점을 정한다. 증거를 보존한다.
  3. 복구: 확정된 범위만 되돌리거나 보상한다. 프로그램 버전을 되돌리는 것과 외부에서 이미 일어난 효과를 복구하는 것은 별개다.
  4. 재발 방지 후 재개: 원인을 고치고, 사례를 회귀 평가에 넣어 통과를 확인한 뒤 권한을 단계적으로 다시 연다.

이 순서는 평소에 감사 로그, 트레이싱, 킬 스위치, 되돌릴 수 있는 쓰기 설계가 갖춰져 있어야 가능합니다. 조직 차원에서는 NIST AI 위험관리 프레임워크(AI RMF)가 출발점이 됩니다. 자발적 활용을 위해 만든 이 프레임워크는 거버넌스(Govern), 맥락 파악(Map), 측정(Measure), 관리(Manage)의 네 기능으로 AI 위험을 다루며, 생성형 AI 프로필(NIST AI 600-1)도 따로 있습니다. 법적 신고나 기록 의무는 업종과 관할에 따라 다르므로 별도로 확인해야 합니다.

깊이 읽기

부분 성공과 보상 작업

여러 외부 시스템을 차례로 바꾸는 작업은 중간에 실패하면 일부만 바뀐 상태가 남습니다. 데이터베이스 트랜잭션처럼 한꺼번에 취소할 수 없으므로, 단계마다 보상 작업(이미 일어난 효과를 상쇄하는 반대 작업)을 미리 정의합니다. 분산 시스템에서 사가(saga) 패턴이라 부르는 방식입니다. 예약을 만들었다면 예약 취소가, 크레딧을 줬다면 크레딧 회수가 보상입니다.

보상에는 한계가 있습니다. 보상 작업 자체가 실패할 수 있고, 알림 발송이나 송금처럼 사실상 회수할 수 없는 행동도 있습니다. 그래서 단계의 순서를 설계합니다. 되돌리기 쉬운 단계를 앞에, 되돌릴 수 없는 단계를 맨 뒤에 둡니다. 외부 알림은 모든 내부 변경이 확정된 뒤에 보냅니다. 단계별 확정 지점과 부분 성공 상태를 기록해 두면, 사고 대응의 범위 확정과 복구가 빨라집니다.

실행 중인 작업을 깨뜨리지 않는 배포

장기 실행 에이전트는 배포의 성격을 바꿉니다. 몇 시간짜리 작업이 도는 중에 도구 인자 형식이나 프롬프트를 바꾸면, 실행 중이던 작업이 새 코드에서 깨질 수 있습니다. Anthropic의 리서치 시스템은 실행 중인 에이전트를 방해하지 않으려고 이전 버전과 새 버전을 함께 띄워 두고 트래픽을 조금씩 옮기는 레인보 배포(rainbow deployment)를 쓴다고 적습니다.

설계 원칙은 셋입니다. 첫째, 모델·프롬프트·도구 정의·정책을 하나의 실행 구성 버전으로 묶어, 각 작업이 시작할 때의 버전을 기록합니다. 둘째, 진행 중인 작업은 그 버전으로 끝까지 돌게 하고, 새 작업만 새 버전으로 보냅니다. 셋째, 도구 인자 형식을 바꿀 때는 한동안 옛 형식과 새 형식을 모두 받습니다. 변경 전에는 실제 부작용 없이 새 버전을 나란히 돌려 행동 차이를 보는 그림자 실행으로 위험을 줄일 수 있습니다.

자율성을 단계적으로 넓힌다

되돌릴 수 없는 도구는 한 번에 열지 않습니다. 모의 실행으로 행동을 관찰하고, 실제 실행은 모든 건에 사람 승인을 붙여 시작한 뒤, 사람이 고친 비율과 사고 건수가 기준 아래로 유지되는 낮은 위험 구간부터 자동화를 넓힙니다. 넓히는 조건과 되돌리는 조건을 숫자로 정해 두세요. OpenAI 가이드의 말처럼 고위험 행동의 사람 감독은 신뢰가 쌓일 때까지 유지하는 것입니다.

실무 장면

나들이티켓: 티켓 오픈 날의 재시도 폭증

공연 예매 플랫폼 '나들이티켓'의 상담 에이전트는 예매 조회, 좌석 변경, 취소 접수를 돕는다. 인기 공연의 티켓 오픈 날, 외부 예매 시스템의 응답 지연이 평소 p99 0.8초에서 12초로 늘었다. 모델 SDK가 기본 재시도를 하고, 도구 래퍼가 세 번 더 시도하고, 에이전트가 실패를 보고 같은 도구를 다시 불렀다. 한 시간 동안 예매 시스템 호출은 평소의 9배, 상담 한 건당 비용은 5배가 되었고, 대기열이 쌓여 정상 문의까지 응답이 늦어졌다.

팀은 세 대안을 기각했다. 타임아웃을 60초로 늘리는 안은 연결을 오래 붙잡아 동시 처리량을 더 떨어뜨린다. 워커를 늘리는 안은 이미 느린 예매 시스템에 부하를 더한다. max_turns를 낮추는 안은 비용은 줄여도 재시도의 곱셈 구조를 그대로 둔다.

고친 내용은 다음과 같다. 재시도는 도구 래퍼 한 곳에서만, 지수 백오프와 지터, 최대 두 번으로 하고 에이전트에게는 "예매 시스템이 지연 중입니다. 다시 부르지 말고 접수 안내를 하세요"라는 회복 지시를 돌려준다. 예매 시스템 호출은 테넌트별 동시성 상한을 두고, 30초 동안 실패율이 50%를 넘으면 회로 차단기가 열려 2분간 호출을 막고, 그 뒤 시험 호출이 성공하면 다시 닫힌다. 차단기가 열린 동안 에이전트는 좌석 변경과 취소를 접수만 해 두고 처리되면 알림을 보낸다. 상담 한 건에는 전체 마감 90초와 토큰·도구 호출 상한을 두었다. 좌석 변경 요청에는 예매번호와 변경 요청 번호로 만든 멱등 키를 붙여, 재시도가 와도 접수가 한 번만 되게 했다.

다음 오픈 날 같은 지연이 다시 왔지만 예매 시스템 호출은 평소의 1.4배에 머물렀고, 정상 문의의 응답 시간은 거의 변하지 않았다.

여울정산: 승인을 줄여 승인을 살리기

정산 대행사 '여울정산'은 가맹점 정산 오류를 찾아 정정하는 에이전트를 시범 운영했다. 모든 정정에 승인을 걸자 승인 요청이 하루 400건을 넘었고, 평균 검토 시간은 4초까지 떨어졌다.

팀은 도구를 위험도로 나눴다. 조회와 보고서 작성은 자동, 10만 원 이하이면서 다음 정산에서 상계되는 정정은 일 한도 안에서 자동, 10만 원 초과 정정과 가맹점 외부 통지는 승인 필수로 정했다. 승인은 지침이 아니라 정정 도구 앞의 실행 단계에서 강제했고, 승인 화면에는 금액·근거·트레이스 링크를 함께 보여 줬다. 승인 기록에는 정정 내용의 해시를 저장해, 실행 직전 인자가 다르면 실행하지 않고 다시 승인을 받게 했다. 승인은 24시간 뒤 만료된다. 승인 요청은 하루 30건 남짓으로 줄었고 건당 1분 이상 실제로 읽는 검토가 자리 잡았다.

실행당 최대 턴 수와 비용 한도를 두고, 같은 인자로 다섯 번 넘게 조회하면 멈추고 알리게 했다. 한 달 뒤 원천 데이터 형식이 바뀌어 같은 조회가 반복됐지만, 반복 탐지가 3분 만에 실행을 멈췄다. 운영자는 킬 스위치로 정정 도구만 끄고 조회는 살려 둔 채 파서를 고쳤고, 이 사례를 회귀 세트에 넣은 뒤 정정 도구를 다시 열었다. 기각한 대안은 "승인자를 늘린다"였다. 요청이 400건이면 승인자가 늘어도 한 건에 쓰는 주의는 늘지 않는다.

자주 틀리는 지점

  1. "모든 행동에 승인을 걸면 가장 안전하다." 통제가 많을수록 안전해 보이지만, 승인 피로로 검토가 형식이 되면 위험한 요청도 통과합니다. 되돌림 가능성과 영향 범위로 고릅니다.
  2. "시스템 프롬프트에 금지 사항을 적었으니 막힌다." 모델이 대개 지시를 따르니 믿게 되지만, 지시는 권고입니다. 되돌릴 수 없는 행동은 실행 경로에서 막습니다.
  3. "입력 가드레일이 있으니 부작용 있는 도구도 안전하다." 입구에 검문소가 있으니 안심되지만, 기본 동시 실행에서는 가드레일이 걸리기 전에 도구가 실행됐을 수 있습니다.
  4. "재시도는 많을수록 성공률이 오른다." 한 번 더 하면 될 것 같지만, 과부하에서는 재시도가 부하를 키우고 계층마다 재시도하면 곱으로 불어납니다. 한 계층, 백오프와 지터, 상한을 둡니다.
  5. "턴 제한을 두었으니 비용 폭주는 없다." 루프가 끝나니 안심되지만, 한 턴 안의 비싼 호출, 하위 에이전트 증식, 반복 검색은 턴 수로 잡히지 않습니다.
  6. "샌드박스를 켰으니 비밀이 새지 않는다." 격리라는 말이 든든하지만, 읽기가 열려 있거나 넓은 도메인을 허용하면 유출 경로가 남습니다. 샌드박스 밖에서 도는 도구도 있습니다.
  7. "감사 로그를 남기면 승인은 필요 없다." 기록이 있으면 통제된 것 같지만, 로그는 사후 추적용입니다. 지워진 데이터를 되살리지 못합니다.
  8. "승인을 받았으니 그대로 실행하면 된다." 사람이 확인했으니 끝난 것 같지만, 승인과 실행 사이에 인자가 바뀔 수 있습니다. 승인을 실행 내용에 묶고 만료와 1회 사용을 둡니다.

한눈에 정리

항목 기억할 답
멈춤은 누가 강제하나 루프 코드. 턴 수 + 비용 + 하위 작업 수 + 진전 탐지 + 마감
예산 실행·사용자·테넌트·조직의 층, 제공자 한도는 최후의 안전망
타임아웃 거짓 타임아웃 비율과 지연 백분위로, 최대 타임아웃은 늘 강제
재시도 일시적 오류만, 한 계층, 지수 백오프+지터, 상한, 멱등성
과부하 동시성 제한, 회로 차단기, 우아한 기능 축소
승인 대상 되돌리기 어렵고 영향이 큰 행동. 위험 등급으로 자동 연결
승인 결속 내용 해시·미리보기 ID 대조, 만료, 1회 사용, 권한 재확인
가드레일 범위 입력은 첫 에이전트, 출력은 마지막 에이전트, 기본 동시 실행, 도구 가드레일은 함수 도구만
샌드박스 셸 명령에 OS 경계. 읽기·넓은 도메인·샌드박스 밖 도구를 따로 점검
사고 대응 차단 → 범위 확정 → 복구 → 재발 방지 후 단계적 재개

공식 문서로 확인하기

셀프 체크

5-1. 최대 턴 수만으로는 막을 수 없는 비용 폭주 두 가지를 들고, 각각을 막는 장치를 적어라.

5-2. 모델 SDK, 도구 래퍼, 에이전트가 각자 재시도를 하면 어떤 문제가 생기며, 권장하는 설계는 무엇인가?

5-3. 도구 호출이 타임아웃으로 끝났을 때 곧바로 실패로 기록하고 다시 보내면 안 되는 경우는 언제이며, 대신 무엇을 해야 하는가?

5-4. 모든 행동에 사람 승인을 걸면 오히려 위험해지는 이유를 설명하고, 승인 대상을 고르는 두 축을 들어라.

5-5. Claude Code에서 PreToolUse 훅의 차단(종료 코드 2)과 권한 규칙(allow, deny)은 서로 어떤 관계인가?

5-6. Claude Code 샌드박스가 감싸지 않는 것을 두 가지 들고, 그 부분은 무엇으로 통제하는지 적어라.

5-7. 사용자 한 명이 하루에 쓸 수 있는 에이전트 비용 한도를 정하고, 한도의 80%와 100%에 이르렀을 때 시스템이 각각 할 일을 설계하라.

실습 과제

실습

위험도 표와 멈춤 장치 넣기 (60분)

  1. 운영 중인 에이전트 하나의 모든 도구를 나열하고, 각각 되돌림 가능성(가능·어려움·불가)과 영향 범위(개인·고객·외부)를 표시한다.
  2. 되돌리기 어렵거나 외부에 영향을 주는 도구에 실행 단계의 승인(훅, 콜백, 도구 래퍼 중 하나)을 걸고, 승인 기록에 실행 인자의 해시를 남긴다.
  3. 최대 턴 수, 실행당 비용 한도, 같은 인자 반복 탐지를 설정한다.
  4. 같은 도구를 계속 부르도록 유도하는 입력, 승인 후 인자를 바꾸는 입력으로 각각 시험한다.

완료 기준: 승인 대상 도구가 승인 없이 실행되지 않고, 승인 후 인자가 바뀐 호출이 거절되며, 반복 유도 입력에서 실행이 한도 안에 멈추고 그 이유가 로그에 남으면 완료다.

실습

재시도와 비용 한도 시뮬레이션 (45분)

  1. 도구 하나를 감싸는 모의 서버를 만들어, 설정에 따라 지연과 일시적 오류(속도 제한, 과부하)를 일으키게 한다.
  2. 지금 구조에서 SDK·래퍼·에이전트의 재시도 횟수를 곱해, 실패 한 번이 최대 몇 번의 호출이 되는지 계산하고 실제로 측정한다.
  3. 재시도를 한 계층으로 모으고 지수 백오프와 지터, 상한, retry-after 존중을 넣는다.
  4. 실행당 비용 한도와 사용자별 일 한도를 걸고, 80%에서 경고, 100%에서 차단되는지 확인한다.

완료 기준: 변경 전후의 "실패 한 번당 최대 호출 수"가 측정값으로 표에 있고, 한도 도달 시 경고와 차단이 각각 한 번 이상 기록되면 완료다.