로드 중

첫 번째 관리형 에이전트 구축하기

강의 128분
로드 중

첫 번째 관리형 에이전트 구축하기

직접 에이전트 루프를 구축해 본 적이 있다면 그 과정을 잘 아실 것입니다: while 루프, stop reason 분기 처리, 도구 실행. 이 방식은 잘 작동하며, 많은 기능에서는 실제로 적합한 구조입니다. 하지만 때로는 그 루프가 매우 오랫동안 — 몇 분, 심지어 몇 시간 동안 — 여러 도구에 걸쳐 실행되어야 하고, 상태를 유지하고, 파일을 작성하고, 네트워크 문제 이후 작업을 재개해야 할 때가 있습니다. 이런 경우에는 루프를 자신의 서버에서 실행하고 싶지 않을 것입니다. 그 작업을 위임하고 싶을 것입니다. 그것이 바로 관리형 에이전트(managed agents)입니다.

관리형 에이전트란 무엇인가요?

관리형 에이전트는 여러분의 인프라가 아닌 Anthropic의 인프라에서 실행되는 에이전트 루프입니다. 에이전트를 한 번 정의하고, 작업할 환경을 제공한 다음, 세션을 시작합니다. Anthropic이 루프를 실행하고, 여러분은 작업이 진행되는 동안 이벤트를 스트리밍으로 받기만 하면 됩니다.

관리형 에이전트는 모든 API 계정에서 기본적으로 활성화되어 있으며, 별도의 접근 권한이 필요하지 않습니다.

네 가지 기본 요소

네 가지 기본 요소가 있으며, 다음과 같은 순서로 구성됩니다:

  1. Agent — 페르소나: 모델, 시스템 프롬프트, 도구 세트. 여러 실행에서 재사용할 수 있습니다.
  2. Environment — 에이전트가 실행되는 위치: 클라우드 또는 자체 호스팅, 네트워킹 설정 등.
  3. Session — 특정 환경 내에서 이루어지는 에이전트의 단일 실행. 세션은 작업의 단위입니다.
  4. Events — 주고받는 메시지: 에이전트의 행동, 도구 호출, 결과, 응답.

이 요소들이 어떻게 맞물리는지 살펴보겠습니다: 여러분의 앱은 세션과 통신하고, 세션은 환경 내에서 작업을 진행하며, 발생하는 모든 일은 이벤트 스트림을 통해 다시 흘러나옵니다:

Session에 연결된 에이전트가 Environment를 구동하고, 결과가 Event Stream을 통해 앱으로 다시 흘러가는 모습을 보여주는 아키텍처 다이어그램

여기서 일어나는 변화에 주목하세요: 여러분은 while 루프를 실행하는 것이 아닙니다. 이벤트를 보내고 이벤트를 읽는 것입니다.

가능한 가장 작은 관리형 에이전트

유용한 작업을 수행하는 가장 작은 관리형 에이전트를 만들어 보겠습니다: 임시 드라이브에 파일을 생성하고, 줄 수를 세고, 결과를 보고하는 작업입니다.

도구로는 에이전트 도구 세트(agent toolset) — Anthropic이 번들로 제공하는 파일, bash, 웹 도구를 사용하겠습니다. 이 작업에는 이 도구들로 충분하므로 직접 도구를 정의할 필요가 없습니다.

1단계: 에이전트 생성

먼저 에이전트를 생성합니다. tools 배열에 바로 정의된 에이전트 도구 세트에 주목하세요 — 이것이 번들 도구 세트입니다:

python
import anthropic

client = anthropic.Anthropic()

agent = client.beta.agents.create(
    name="Line Counter",
    model="claude-opus-5",
    system="You are a helpful agent that completes small file tasks.",
    tools=[
        {"type": "agent_toolset_20260401", "default_config": {"enabled": True}}
    ],
)

기억하세요: 에이전트는 재사용할 수 있습니다. 한 번 생성하고 여러 세션에서 실행하세요.

2단계: 환경 생성

다음은 환경입니다. 이는 에이전트가 실행되는 샌드박스를 정의합니다: 클라우드, limited 네트워킹 사용. limited를 사용하면 샌드박스와 웹 도구는 allowed_hosts에 나열한 호스트에만 접근할 수 있습니다. 이 작업은 네트워크가 필요 없으므로 아무것도 나열하지 않습니다. unrestricted는 에이전트가 미리 나열할 수 없는 사이트에 접근해야 하는 경우에만 사용하세요. 각 세션은 자체 샌드박스를 가지며, 파일이 작성되는 곳이 바로 여기입니다:

python
environment = client.beta.environments.create(
    name="line-counter-env",
    config={
        "type": "cloud",
        "networking": {"type": "limited"},
    },
)

3단계: 세션 생성

그런 다음 에이전트와 환경, 그리고 선택적인 제목을 사용해 세션을 생성합니다. 세션은 작업의 단위입니다:

python
session = client.beta.sessions.create(
    agent=agent.id,
    environment_id=environment.id,
    title="Count lines demo",
)

4단계: 스트림을 연 다음 시작 메시지 보내기

이제 이벤트 스트림을 엽니다 — 이 작업을 먼저 한다는 점에 주목하세요. 스트림은 열린 이후에 발생하는 이벤트만 전달하므로, 항상 시작 메시지를 보내기 전에 스트림을 열어야 합니다. 그런 다음 실시간 스트림에 사용자 메시지를 보냅니다:

python
with client.beta.sessions.events.stream(session_id=session.id) as stream:
    # 스트림이 열렸습니다 — 이제 킥오프를 전송합니다
    client.beta.sessions.events.send(
        session_id=session.id,
        events=[
            {
                "type": "user.message",
                "content": [
                    {
                        "type": "text",
                        "text": "Create a file in the temp directory, "
                                "count its lines, and report back.",
                    }
                ],
            }
        ],
    )

events가 복수형이라는 점에 주목하세요. 이 API에서는 이벤트를 통해 모든 것이 흘러갑니다.

5단계: 스트림 소비하기

마지막으로 스트림을 소비합니다. 이 데모에서 중요한 이벤트 유형은 세 가지입니다:

  • agent.message — Claude의 텍스트
  • agent.tool_use — Claude가 선택한 도구
  • session.status_idle — 에이전트가 멈추고 입력을 기다리는 상태

이 데모는 첫 번째 idle 이벤트에서 멈춥니다. 여러분의 앱에서는 작업이 완료되었다고 간주하기 전에 event.stop_reason.type을 확인하세요: end_turn은 에이전트가 턴을 마쳤거나 여러분이 중단시켰다는 의미이고, requires_action은 도구 호출에 여러분의 응답이 필요하다는 의미입니다.

python
    for event in stream:
        if event.type == "agent.message":
            for block in event.content:
                if block.type == "text":
                    print(block.text, end="", flush=True)
        elif event.type == "agent.tool_use":
            print(f"\n[tool] {event.name}")
        elif event.type == "session.status_idle":
            print("\n--- Agent done ---")
            break

실행하면, 출력 결과는 에이전트가 소리 내어 추론하는 과정입니다 — 실제 텍스트, 선택한 도구, 그리고 최종 답변까지. 이 모든 것이 여러분의 컨테이너가 아닌 Anthropic의 컨테이너 안에서 실행됩니다:

관리형 에이전트 실행의 터미널 출력: 에이전트, 환경, 세션 ID가 생성된 후, 이벤트 스트림에서 에이전트가 파일을 작성하고 도구를 실행하며 파일에 3줄이 있다고 보고하는 모습

트레이드오프

일반적으로 에이전트를 사용할 때는 모든 것을 직접 제어해야 하는 자체 루프를 갖게 됩니다. 관리형 에이전트를 사용하면 그 루프, 샌드박스, 재개 가능성을 위임하고, 들어오는 이벤트 스트림을 소비하기만 하면 됩니다. 세션의 이벤트 기록은 서버에 저장됩니다. 따라서 연결이 끊어지면 새 스트림을 열고, 과거 이벤트 목록을 조회해 놓친 부분을 확인한 다음, 목록에서 이미 반환된 실시간 이벤트는 건너뛰면 됩니다. 코드는 이벤트 누락 없이 재연결하기를 참조하세요.

프로덕션 앱에서는 이것이 장시간 실행되고, 파일을 다루며, "이걸 정리해줘" 같은 작업에 적합한 구조입니다. 파일 공유 정리 작업을 떠올려 보세요: 관리형 에이전트가 대상 디렉터리 구조 사양을 읽고, 정리되지 않은 수신 폴더를 탐색하며, 파일을 올바른 프로젝트 폴더로 이동시키고, 중복 파일과 0바이트 쓰레기 파일을 보관 처리하며, 확신을 가지고 배치할 수 없는 항목은 표시합니다 — 이 모든 작업이 수천 개의 파일을 대상으로 몇 분 동안 실행되는 하나의 세션 안에서 이루어집니다. 실제 앱에서는 이런 모습입니다 — 파일 공유 정리 대시보드가 에이전트가 파일을 정렬, 보관, 표시하는 활동을 실시간으로 스트리밍합니다:

관리형 에이전트가 구동하는 파일 공유 정리 웹 앱. 폴더 트리가 정리되는 모습과 함께 에이전트가 파일을 이동하고 보관하는 이벤트의 실시간 활동 피드를 보여줌

요약

  • 관리형 에이전트는 여러분을 대신해 실행되는 에이전트 루프입니다 — 여러분의 서버가 아닌 Anthropic의 인프라에서 실행됩니다.
  • 흐름은 다음과 같습니다: 에이전트 생성, 환경 생성, 세션 생성, 이벤트 전송, 이벤트 스트리밍 수신.
  • agent(모델, 시스템 프롬프트, 도구 세트)는 여러 실행에서 재사용할 수 있고, session은 단일 실행이며, events는 모든 것이 흘러가는 방식입니다.
  • 시작 메시지를 보내기 전에 이벤트 스트림을 여세요 — 스트림은 열린 이후에 발생하는 이벤트만 전달합니다.
  • 세 가지 이벤트를 주시하세요: agent.message(텍스트), agent.tool_use(도구 선택), session.status_idle(멈추고 입력을 기다리는 상태이며, stop_reason.type이 그 이유를 알려줍니다).
  • 루프가 너무 오래 실행되거나, 너무 많은 작업을 수행하거나, 문제 발생 시에도 살아남아야 할 때는 관리형 에이전트를 사용하세요. 완전한 제어가 필요할 때는 수동 루프를 사용하세요.