Anthropic은 신뢰성 높고, 해석 가능하며, 제어 가능한 AI 시스템 개발을 추구하는 AI 안전·연구 기업입니다.
Claude 관리형 에이전트를 시작하려면 공식 문서를 참고하세요.
Anthropic 엔지니어링 블로그에서 꾸준히 다뤄온 주제가 있습니다. 효과적인 에이전트를 구축하고, 장기 작업을 위한 하네스(harness)를 설계하는 방법입니다. 이 주제들을 관통하는 공통점이 하나 있는데, 하네스에는 "Claude가 스스로 할 수 없는 일"에 대한 가정이 내재되어 있다는 점입니다. 그런데 모델이 발전하면서 이런 가정들은 금세 낡아버릴 수 있기 때문에, 주기적으로 재검토해야 합니다.
한 가지 사례를 들어보겠습니다. 이전 연구에서 확인한 바, Claude Sonnet 4.5는 컨텍스트 한계가 가까워진다고 감지하면 작업을 조기에 마무리하는 경향이 있었습니다. "컨텍스트 불안(context anxiety)"이라 불리는 행동입니다. 당시에는 하네스에 컨텍스트 초기화 기능을 추가해 이 문제를 해결했습니다. 그런데 동일한 하네스를 Claude Opus 4.5에 적용해보니, 그 행동 자체가 사라져 있었습니다. 초기화 기능이 불필요한 군더더기로 남아버린 것입니다.
하네스는 앞으로도 계속 진화할 것입니다. 그래서 저희는 관리형 에이전트(Managed Agents)를 구축했습니다. Claude 플랫폼에서 제공하는 호스팅 서비스로, 특정 구현 방식에 종속되지 않는 소수의 인터페이스를 통해 장기 에이전트를 대신 실행해드립니다. 오늘 저희가 운영하는 구현 방식이 바뀌어도 인터페이스는 그대로 유지됩니다.
관리형 에이전트를 구축하는 과정에서, 컴퓨팅 분야의 오래된 난제를 다시 마주했습니다. "아직 생각하지도 못한 프로그램들"을 위한 시스템을 어떻게 설계할 것인가의 문제입니다. 수십 년 전, 운영체제는 하드웨어를 추상화—프로세스, 파일—하는 방식으로 이 문제를 해결했습니다. 당시 존재하지도 않던 프로그램들이 쓸 수 있을 만큼 범용적인 추상화였습니다. 그 추상화는 하드웨어보다 오래 살아남았습니다. read() 명령어는 1970년대의 디스크 팩을 쓰든 최신 SSD를 쓰든 동일하게 동작합니다. 상위 추상화는 안정적으로 유지되는 동안, 하위 구현은 자유롭게 교체되었습니다.
관리형 에이전트도 동일한 패턴을 따릅니다. 에이전트의 구성 요소를 추상화했습니다. 세션(발생한 모든 일을 기록하는 추가 전용 로그), 하네스(Claude를 호출하고 Claude의 도구 호출을 관련 인프라로 라우팅하는 루프), 샌드박스(Claude가 코드를 실행하고 파일을 편집하는 실행 환경)입니다. 이 구조 덕분에 각 구성 요소의 구현체를 서로 영향 없이 독립적으로 교체할 수 있습니다. 저희가 의견을 갖는 것은 인터페이스의 형태이지, 그 뒤에서 무엇이 실행되는지가 아닙니다.

처음에는 에이전트의 모든 구성 요소를 단일 컨테이너에 담았습니다. 세션, 에이전트 하네스, 샌드박스가 모두 하나의 환경을 공유하는 방식이었습니다. 파일 편집이 직접 시스템 콜로 처리되고, 서비스 경계를 설계할 필요가 없다는 장점이 있었습니다.
그러나 모든 것을 단일 컨테이너로 묶은 탓에, 오래된 인프라 문제에 부딪혔습니다. 반려동물을 키우게 된 것입니다. 이른바 '반려동물 대 가축' 비유에서, 반려동물은 이름이 있고 손수 돌봐야 하며 잃어서는 안 되는 존재인 반면, 가축은 언제든 교체 가능한 존재입니다. 저희의 경우 서버가 바로 그 반려동물이 되었습니다. 컨테이너 하나가 죽으면 세션이 통째로 사라졌고, 컨테이너가 응답하지 않으면 일일이 살려내야 했습니다.
컨테이너를 살리는 일은 곧 응답 없는 세션을 디버깅하는 일이었습니다. 내부를 들여다볼 수 있는 유일한 창구는 WebSocket 이벤트 스트림뿐이었는데, 이것만으로는 어디서 장애가 발생했는지 알 수 없었습니다. 하네스의 버그든, 이벤트 스트림의 패킷 손실이든, 컨테이너 오프라인이든 모두 동일하게 보였습니다. 무엇이 잘못됐는지 파악하려면 엔지니어가 컨테이너 내부에 셸로 직접 접속해야 했는데, 그 컨테이너에는 사용자 데이터도 함께 있었기에 사실상 제대로 된 디버깅이 불가능했습니다.
두 번째 문제는 하네스가 Claude의 작업 대상이 같은 컨테이너 안에 있다고 가정한다는 점이었습니다. 고객사가 Claude를 자사 VPC(가상 사설 클라우드)에 연결하려면, 자신의 네트워크를 저희 네트워크와 피어링하거나 자체 환경에서 하네스를 직접 실행해야 했습니다. 하네스에 깔린 가정이 다른 인프라와의 연결을 가로막는 걸림돌이 된 것입니다.
저희가 내린 해법은 "두뇌"(Claude와 하네스)를 "손"(동작을 수행하는 샌드박스와 도구) 및 "세션"(세션 이벤트 로그)으로부터 분리하는 것이었습니다. 각 요소는 서로에 대한 가정을 최소화하는 인터페이스가 되었고, 각각 독립적으로 장애가 발생하거나 교체될 수 있게 되었습니다.
하네스가 컨테이너를 벗어나다. 두뇌와 손을 분리한다는 것은 하네스가 더 이상 컨테이너 안에 있지 않음을 의미합니다. 하네스는 다른 도구를 호출하듯 컨테이너를 호출합니다. execute(name, input) → string. 컨테이너는 가축이 되었습니다. 컨테이너가 죽으면 하네스는 도구 호출 오류로 이를 감지하고 Claude에게 전달합니다. Claude가 재시도를 결정하면, 표준 레시피로 새 컨테이너를 초기화할 수 있습니다. provision({resources}). 더 이상 장애 난 컨테이너를 손수 살려낼 필요가 없어졌습니다.
하네스 장애 복구. 하네스 역시 가축이 되었습니다. 세션 로그가 하네스 외부에 위치하기 때문에, 하네스 내부에서 크래시가 나더라도 살려야 할 상태가 없습니다. 하네스 하나가 죽으면, 새 하네스를 wake(sessionId)로 재시작하고, getSession(id)로 이벤트 로그를 불러온 뒤 마지막 이벤트부터 재개하면 됩니다. 에이전트 루프가 실행되는 동안 하네스는 emitEvent(id, event)으로 세션에 기록을 남겨 이벤트의 내구성 있는 로그를 유지합니다.

보안 경계. 결합된 설계에서는 Claude가 생성한 신뢰할 수 없는 코드가 자격 증명(credentials)과 동일한 컨테이너에서 실행됩니다. 따라서 프롬프트 인젝션 공격은 Claude를 설득해 자신의 환경 변수를 읽게 만드는 것만으로 충분했습니다. 공격자가 토큰을 탈취하면, 제한 없는 새 세션을 생성해 작업을 위임할 수 있습니다. 토큰 범위를 좁히는 것이 당연한 대응책이지만, 이는 결국 "Claude가 제한된 토큰으로 무엇을 할 수 없는지"에 대한 가정을 인코딩하는 것입니다. 그리고 Claude는 점점 더 똑똑해지고 있습니다. 구조적 해법은 Claude가 생성한 코드가 실행되는 샌드박스에서 토큰에 아예 접근할 수 없도록 만드는 것이었습니다.
이를 위해 두 가지 패턴을 적용했습니다. 인증 정보는 리소스에 묶거나, 샌드박스 외부의 보안 저장소(vault)에 보관합니다. Git의 경우, 각 저장소의 액세스 토큰으로 샌드박스 초기화 시 저장소를 클론하고 로컬 git 리모트에 연결해둡니다. 에이전트가 토큰을 직접 다루지 않아도, 샌드박스 내부에서 push과 pull이 정상 동작합니다. 커스텀 도구의 경우 MCP를 지원하며 OAuth 토큰을 보안 저장소에 보관합니다. Claude는 전용 프록시를 통해 MCP 도구를 호출하고, 이 프록시는 세션에 연결된 토큰을 받습니다. 그러면 프록시가 저장소에서 해당 자격 증명을 가져와 외부 서비스를 호출합니다. 하네스는 어떤 자격 증명도 알지 못합니다.
장기 작업은 Claude의 컨텍스트 윈도우 길이를 초과하는 경우가 많습니다. 이를 해결하는 일반적인 방법들은 모두 무엇을 남길지에 대한 비가역적 결정을 수반합니다. 저희는 이 기법들을 이전 컨텍스트 엔지니어링 연구에서 탐구한 바 있습니다. 예를 들어, 압축(compaction)은 Claude가 컨텍스트 윈도우 요약을 저장하게 하고, 메모리 도구는 Claude가 컨텍스트를 파일로 기록해 세션 간 학습을 가능하게 합니다. 여기에 오래된 도구 결과나 thinking 블록처럼 불필요한 토큰을 선택적으로 제거하는 컨텍스트 트리밍을 결합할 수도 있습니다.
그러나 컨텍스트를 선택적으로 보존하거나 버리는 비가역적 결정은 장애로 이어질 수 있습니다. 이후 처리에 어떤 토큰이 필요할지 사전에 알기 어렵기 때문입니다. 압축 단계에서 메시지가 변환되면 하네스는 압축된 메시지를 Claude의 컨텍스트 윈도우에서 제거하는데, 이 메시지는 별도로 저장해두지 않으면 복구할 수 없습니다. 이전 연구에서는 컨텍스트를 컨텍스트 윈도우 외부에 객체로 저장하는 방식으로 이 문제를 해결하려 했습니다. 예를 들어, 컨텍스트를 REPL의 객체로 두고 LLM이 코드를 작성해 필터링하거나 슬라이싱해 프로그래밍 방식으로 접근하는 식입니다.

관리형 에이전트에서 세션은 동일한 역할을 합니다. Claude의 컨텍스트 윈도우 외부에 존재하는 컨텍스트 객체로 기능합니다. 다만 샌드박스나 REPL에 저장되는 대신, 컨텍스트는 세션 로그에 내구성 있게 보관됩니다. getEvents(), 인터페이스를 통해 두뇌는 이벤트 스트림의 위치 기반 슬라이스를 선택해 컨텍스트를 조회할 수 있습니다. 이 인터페이스는 유연하게 활용할 수 있습니다. 마지막으로 읽은 지점부터 이어서 읽거나, 특정 시점 직전 몇 개의 이벤트를 되감아 경위를 파악하거나, 특정 동작 이전의 컨텍스트를 다시 읽는 식입니다.
가져온 이벤트는 Claude의 컨텍스트 윈도우에 전달되기 전에 하네스에서 변환될 수도 있습니다. 변환 방식은 하네스가 인코딩하는 내용에 따라 자유롭게 정의할 수 있으며, 높은 프롬프트 캐시 적중률을 위한 컨텍스트 구성과 컨텍스트 엔지니어링이 포함됩니다. 저희가 복구 가능한 컨텍스트 저장(세션)과 임의적인 컨텍스트 관리(하네스)를 분리한 이유는, 미래 모델에 어떤 구체적인 컨텍스트 엔지니어링이 필요할지 예측할 수 없기 때문입니다. 인터페이스는 컨텍스트 관리를 하네스에 위임하고, 세션이 내구성 있게 유지되며 언제든 조회 가능하다는 것만 보장합니다.
다수의 두뇌. 두뇌와 손을 분리함으로써 고객들이 가장 먼저 토로했던 불만이 해소되었습니다. 팀이 Claude를 자사 VPC 내 리소스에 연결하려면, 하네스를 담은 컨테이너가 모든 리소스가 옆에 있다고 가정했기 때문에 유일한 방법은 자신의 네트워크를 저희 네트워크와 피어링하는 것뿐이었습니다. 하네스가 컨테이너에서 분리되자 그 가정은 사라졌습니다. 같은 변화가 성능 면에서도 효과를 가져왔습니다. 처음에 두뇌를 컨테이너 안에 두었을 때는, 다수의 두뇌를 운영하려면 그만큼의 컨테이너가 필요했습니다. 각 두뇌마다 컨테이너가 프로비저닝되어야 추론이 시작될 수 있었고, 모든 세션이 컨테이너 셋업 비용을 전부 선납해야 했습니다. 샌드박스를 한 번도 쓰지 않을 세션조차도 저장소를 클론하고, 프로세스를 부팅하고, 서버에서 대기 중인 이벤트를 가져와야 했습니다.
이 대기 시간은 첫 번째 토큰 생성 시간(TTFT, time-to-first-token)으로 측정됩니다. 세션이 작업을 받아들인 시점부터 첫 응답 토큰을 생성할 때까지 걸리는 시간으로, 사용자가 가장 직접적으로 체감하는 지연입니다.
두뇌와 손을 분리하면, 컨테이너는 필요할 때만 두뇌가 (execute(name, input) → string) 도구 호출을 통해 프로비저닝합니다. 따라서 컨테이너가 당장 필요 없는 세션은 기다릴 필요가 없습니다. 오케스트레이션 레이어가 세션 로그에서 대기 중인 이벤트를 가져오는 즉시 추론을 시작할 수 있습니다. 이 아키텍처를 통해 p50 TTFT는 약 60%, p95는 90% 이상 감소했습니다. 다수의 두뇌로 확장하는 것은 단순히 상태가 없는 하네스를 여러 개 시작하고, 필요할 때만 손에 연결하는 것으로 충분해졌습니다.
다수의 손. 저희는 또한 각 두뇌가 여러 손에 연결될 수 있기를 원했습니다. 실제로 이는 Claude가 여러 실행 환경을 파악하고 어디에 작업을 보낼지 결정해야 함을 의미합니다. 단일 셸에서 동작하는 것보다 인지적으로 훨씬 까다로운 과제입니다. 초기에 두뇌를 단일 컨테이너 안에 둔 것은 당시 모델들이 이를 처리할 능력이 없었기 때문입니다. 그런데 모델 지능이 향상되면서, 오히려 단일 컨테이너가 발목을 잡기 시작했습니다. 그 컨테이너가 장애를 일으키면 두뇌가 뻗고 있던 모든 손의 상태가 함께 사라졌습니다.
두뇌와 손을 분리하면 각 손은 하나의 도구 execute(name, input) → string가 됩니다. 이름과 입력을 넣으면 문자열이 반환되는 구조입니다. 이 인터페이스는 모든 커스텀 도구, MCP 서버, 저희 자체 도구를 모두 지원합니다. 하네스는 샌드박스가 컨테이너인지, 휴대폰인지, 포켓몬 에뮬레이터인지 알 필요가 없습니다. 그리고 어떤 손도 특정 두뇌에 종속되지 않기 때문에, 두뇌들끼리 손을 서로 넘겨줄 수도 있습니다.

저희가 마주한 과제는 오래된 것이었습니다. "아직 생각하지도 못한 프로그램들"을 위한 시스템을 어떻게 설계할 것인가. 운영체제는 하드웨어를 아직 존재하지 않는 프로그램들도 쓸 수 있을 만큼 범용적인 추상화로 가상화함으로써 수십 년을 버텨왔습니다. 관리형 에이전트 역시 같은 방향을 목표로 했습니다. 미래에 등장할 하네스, 샌드박스, 또는 Claude 주변의 다른 구성 요소들을 수용할 수 있는 시스템을 설계하는 것입니다.
관리형 에이전트는 같은 정신을 담은 메타 하네스입니다. 미래에 Claude에게 필요할 특정 하네스가 무엇인지에 대해 의견을 갖지 않습니다. 대신 다양한 하네스를 수용할 수 있는 범용 인터페이스를 갖춘 시스템입니다. 예를 들어, Claude Code는 저희가 다양한 작업에 폭넓게 활용하는 훌륭한 하네스입니다. 작업별 특화 에이전트 하네스가 좁은 도메인에서 탁월한 성능을 발휘한다는 것도 확인했습니다. 관리형 에이전트는 이 모두를 수용하며, 시간이 흐를수록 향상되는 Claude의 지능에 맞춰 나갈 수 있습니다.
메타 하네스 설계란 Claude를 둘러싼 인터페이스에 대해 명확한 관점을 갖는 것을 의미합니다. 저희는 Claude가 상태를 다루는 능력(세션)과 연산을 수행하는 능력(샌드박스)을 필요로 할 것이라 봅니다. 또한 다수의 두뇌와 다수의 손으로 확장할 수 있는 능력도 필요할 것입니다. 이러한 기능이 장기간에 걸쳐 안정적으로, 그리고 안전하게 동작할 수 있도록 인터페이스를 설계했습니다. 다만 Claude에게 몇 개의 두뇌와 손이 필요한지, 어디에 위치해야 하는지에 대해서는 어떤 가정도 하지 않습니다.
Lance Martin, Gabe Cemaj, Michael Cohen이 작성했습니다. Agents API 팀과 Jake Eaton의 기여에 특별히 감사드립니다.