Claude Platform Docs
Managed Agents에이전트에 작업 위임

세션 이벤트 스트림

이벤트를 전송하고, 응답을 스트리밍하며, 실행 중인 세션을 중단하거나 방향을 전환합니다.

Claude Managed Agents와의 통신은 이벤트 기반입니다. 사용자 이벤트를 에이전트에 전송하고, 상태를 추적하기 위해 에이전트 이벤트와 세션 이벤트를 다시 수신합니다.

이벤트 유형

이벤트는 두 방향으로 흐릅니다.

  • 사용자 이벤트와 시스템 이벤트는 에이전트에 전송하는 이벤트입니다. user.* 이벤트는 세션을 시작하고 진행되는 동안 세션을 조종합니다. system.message는 함께 전송되는 턴과 이후 모든 턴에 적용되는 시스템 수준 컨텍스트를 추가합니다.
  • 세션 이벤트, 스팬 이벤트, 에이전트 이벤트는 세션 상태와 에이전트 진행 상황에 대한 관찰 가능성(observability)을 위해 사용자에게 전송됩니다. 옵트인한 스트림 연결은 이벤트 델타도 수신합니다.

세션, 스팬, 에이전트, 사용자, 시스템 이벤트 유형 문자열은 {domain}.{action} 명명 규칙을 따릅니다. 스트림 전용 델타 미리보기 이벤트(event_start, event_delta)는 예외입니다. 전체 카탈로그는 레퍼런스의 이벤트 유형을 참조하세요. 웹훅 이벤트 유형은 별개이며, 일부 이름은 스트림의 이름과 다릅니다(예: session.status_idle이 아닌 session.status_idled).

저장되는 모든 이벤트에는 이벤트 처리가 완료될 때 설정되는 processed_at 타임스탬프가 포함됩니다. 사용자가 전송하는 이벤트의 경우, 이벤트가 이전 이벤트 뒤에서 아직 대기 중인 동안에는 processed_at이 null입니다. 예외는 user.define_outcome, user.custom_tool_result, user.tool_result로, 이들은 수신 즉시 처리되어 processed_at이 이미 채워진 상태로 에코됩니다.

이벤트 통합

에이전트의 작업을 시작하거나 계속하려면 user.message 이벤트를 전송하세요:

client.beta.sessions.events.send(
    session.id,
    events=[
        {
            "type": "user.message",
            "content": [
                {
                    "type": "text",
                    "text": "Analyze the performance of the sort function in utils.py",
                },
            ],
        },
    ],
)

실행 중인 에이전트를 중지하려면 user.interrupt 이벤트를 전송한 다음, user.message 이벤트를 이어서 전송하여 방향을 전환하세요:

# Agent is currently analyzing a file...
# Interrupt with a new direction:
client.beta.sessions.events.send(
    session.id,
    events=[
        {"type": "user.interrupt"},
        {
            "type": "user.message",
            "content": [
                {
                    "type": "text",
                    "text": "Instead, focus on fixing the bug in line 42.",
                },
            ],
        },
    ],
)

호출은 이벤트가 대기열에 추가되는 즉시 반환되며, 인터럽트의 processed_at은 에이전트가 이를 적용할 때까지 null로 유지됩니다. 진행 중인 모델 응답은 즉시 중지됩니다. 도구 호출이 실행 중인 동안에는 인터럽트 적용에 더 오래 걸릴 수 있으며, 적용될 때까지 세션은 running 상태로 유지됩니다. 그런 다음 user.interrupt 이벤트가 스트림에 나타나고, 중단된 턴은 session.status_idle 이벤트로 종료됩니다. 이 이벤트의 stop_reason은 end_turn으로, 스스로 완료된 턴과 동일한 값입니다. 중단에 특화된 stop reason은 없습니다. 에이전트는 인터럽트 이후에 전송한 user.message로 다음 턴을 시작합니다.

이벤트 델타

기본적으로 에이전트의 응답 텍스트는 버퍼링된 agent.message 이벤트로 스트림에 도달하며, 각 이벤트는 이를 생성한 모델 요청이 완료된 후에만 발생합니다. "Event deltas"(이벤트 델타)를 사용하면 모델이 아직 텍스트를 생성하는 동안 해당 텍스트를 라이브 미리보기로 점진적으로 렌더링할 수 있습니다. 미리보기는 응답이 아닙니다. 미리보기는 최선 노력(best-effort) 방식의 표시 보조 수단이며, 버퍼링된 agent.message가 항상 권위 있는 기록입니다. 미리보기를 무시하는 클라이언트도 여전히 완전하고 정확한 스트림을 수신합니다.

미리보기 옵트인

미리보기는 스트림 연결별로 옵트인합니다. 읽고 있는 스트림에 event_deltas[] 쿼리 매개변수를 추가하고, 미리보기를 원하는 각 이벤트 유형마다 한 번씩 반복하세요. []는 셸 glob 패턴이므로, 셸에서 요청을 구성할 때는 항상 URL을 따옴표로 감싸세요. 예제에서는 대괄호를 %5B%5D로 퍼센트 인코딩하며, 이 방법도 동작합니다. 두 스트림 엔드포인트 모두 이 매개변수를 받습니다. 세션 수준 스트림인 GET /v1/sessions/{session_id}/events/stream과 각 세션 스레드의 자체 스트림인 GET /v1/sessions/{session_id}/threads/{thread_id}/stream입니다. 허용되는 값은 agent.message와 agent.thinking이며, 다른 값은 400 오류를 반환하고, 100개를 초과하는 값을 가진 요청도 마찬가지입니다. 서브에이전트의 미리보기는 해당 서브에이전트의 자체 스레드 스트림에 나타납니다.

미리보기 대상 이벤트가 시작되면, 스트림은 곧 발생할 이벤트의 유형과 id를 담은 event_start를 발생시킵니다:

{
  "type": "event_start",
  "event": {
    "type": "agent.message",
    "id": "sevt_01abc..."
  }
}

agent.message의 경우, 시작 이벤트 뒤에 점진적 텍스트를 담은 event_delta 이벤트가 이어집니다. 각 델타는 자신이 확장하는 이벤트를 event_id에, 확장하는 콘텐츠 블록을 delta.index에 명시합니다:

{
  "type": "event_delta",
  "event_id": "sevt_01abc...",
  "delta": {
    "type": "content_delta",
    "index": 0,
    "content": {
      "type": "text",
      "text": "Here is the summary"
    }
  }
}

agent.thinking 이벤트가 미리보기될 때는 event_start만 발생합니다. event_delta 이벤트는 뒤따르지 않으며, 미리보기를 마무리하는 버퍼링된 agent.thinking 이벤트에는 사고 콘텐츠가 담기지 않습니다. 이는 콘텐츠 전달자가 아니라 진행 신호입니다.

저장되는 이벤트와 달리, event_start와 event_delta에는 자체 id나 processed_at이 없습니다. 이들이 담는 유일한 식별자는 미리보기 대상 이벤트의 id입니다.

누적 및 조정

이벤트 델타를 지원하는 모든 SDK에는 index 관리를 대신 처리해 주는 누적기 헬퍼가 포함되어 있습니다. Go, Java, Ruby, C# 헬퍼는 누적 중인 미리보기를 이벤트의 id로도 키 지정합니다. Python, TypeScript, PHP 헬퍼에서는 해당 맵을 직접 유지하고 각 델타를 해당 id의 항목에 합칩니다. 사용자 정의 관리가 필요한 경우 수동 패턴도 모든 언어에서 동작합니다. 생성된 이벤트 유형에 이를 적용하세요.

수동 패턴에서는 미리보기를 임시 버퍼로, 버퍼링된 이벤트를 기록으로 취급하세요. 버퍼는 (event_id, index)로 키 지정합니다. 모델 요청별로 조정하세요. 턴은 단일 session.status_running 이벤트로 시작되며, 정상적으로 완료되는 턴에서는 각 모델 요청이 순서대로 span.model_request_start, event_start, event_delta 이벤트들, 버퍼링된 agent.message, 그리고 마지막으로 span.model_request_end(스팬 이벤트 탭에 있음)를 생성합니다. 와이어상에서 이는 해당 시퀀스의 미리보기 부분이며, 연결의 다른 버퍼링된 이벤트와 섞여 나타납니다:

event_start     {"event": {"type": "agent.message", "id": "sevt_01abc..."}}
event_delta     {"event_id": "sevt_01abc...", "delta": {"type": "content_delta", "index": 0, "content": {"type": "text", "text": "..."}}}
...
agent.message   {"id": "sevt_01abc...", "content": [...]}

event_delta 줄은 텍스트 조각마다 한 번씩 반복됩니다. 각 이벤트가 도착하는 대로 처리하세요:

  1. event_start에서 공지된 id를 기록합니다. 식별자는 항상 일치합니다. event_start.event.id, 모든 event_delta.event_id, 버퍼링된 agent.message의 id는 동일한 값입니다.
  2. 각 event_delta에서 delta.content.text를 (event_id, delta.index) 항목에 추가하고 누적 텍스트를 렌더링합니다. 특정 index에 대한 첫 번째 델타가 해당 항목을 생성합니다.
  3. 버퍼링된 agent.message가 도착하면 id로 매칭하고, 누적된 미리보기를 버린 뒤 메시지의 콘텐츠를 대신 렌더링합니다.
  4. span.model_request_end에서 버퍼링된 이벤트로 조정되지 않은 미리보기를 모두 닫습니다. 더 이상 해당 미리보기에 대한 델타는 오지 않습니다. 턴에 오류가 발생하거나 중단되면 버퍼링된 이벤트가 도착하지 않을 수도 있지만, span.model_request_end는 여전히 도착합니다.

이 패턴이 의존하는 보장 사항:

  • 미리보기의 델타를 (event_id, index)로 키 지정하여 도착 순서대로 이어 붙이면 버퍼링된 이벤트의 content[index].text의 접두사(prefix)가 됩니다(부하 상황에서 델타가 버려질 수 있으므로 반드시 전체 텍스트가 아니라 접두사입니다).
  • 하나의 연결은 event_id당 최대 하나의 event_start를 발생시키며, 버퍼링된 이벤트는 해당 연결이 그 id에 대해 전달하는 마지막 항목입니다.
# Preview snapshots, keyed by event id. accumulate_managed_agents_event folds each
# event_start / event_delta into an agent.message snapshot; the buffered
# agent.message replaces it.
previews: dict[str, BetaManagedAgentsAgentMessageEvent] = {}

# Opt in to agent.message previews on this connection
with client.beta.sessions.events.stream(
    session.id, event_deltas=["agent.message"]
) as stream:
    client.beta.sessions.events.send(
        session.id,
        events=[
            {
                "type": "user.message",
                "content": [{"type": "text", "text": "Describe the repo in one sentence."}],
            },
        ],
    )

    for event in stream:
        match event.type:
            case "event_start":
                snapshot = accumulate_managed_agents_event(None, event)
                if snapshot is not None:
                    previews[event.event.id] = snapshot
                print(f"event_start             {event.event.type} {event.event.id}")
            case "event_delta":
                preview = accumulate_managed_agents_event(previews.get(event.event_id), event)
                if preview is not None:
                    previews[event.event_id] = preview
                    text = "".join(block.text for block in preview.content)
                    print(f"event_delta             preview: {text!r}")
            case "agent.message":
                # The buffered event is the record: it replaces and closes the preview
                preview = accumulate_managed_agents_event(previews.pop(event.id, None), event)
                text = "".join(block.text for block in preview.content)
                print(f"agent.message           {event.id} {text!r}")
            case "span.model_request_end":
                # No more deltas are coming. Close any preview whose
                # buffered event never arrived.
                for event_id in previews:
                    print(f"span.model_request_end  closing preview for {event_id}")
                previews.clear()
            case "session.status_idle":
                break

세션 스레드 이벤트 미리보기

멀티에이전트 세션에서는 모든 세션 스레드가 GET /v1/sessions/{session_id}/threads/{thread_id}/stream에 자체 이벤트 스트림을 가지며, 동일한 값을 가진 동일한 event_deltas[] 매개변수를 받습니다. 미리보기는 설계상 스레드 범위입니다. 연결은 자신이 읽고 있는 스레드만 미리보기합니다. 자식 스레드의 미리보기는 해당 자식의 자체 스트림으로 전달되며 세션 수준 스트림에 교차 게시되지 않습니다. 세션 수준 스트림의 미리보기는 기본(primary) 스레드 범위로 유지됩니다. 모델이 생성하는 동안 서브에이전트의 텍스트를 보려면 해당 서브에이전트의 스레드 스트림을 여세요.

스레드 스트림의 경로는 틀리기 쉽습니다. /events/stream(세션 수준에만 존재)이 아니라 /threads/{thread_id}/stream이며, /threads/{thread_id}/events/stream 엔드포인트는 없습니다.

미리보기 이벤트 자체는 변하지 않습니다. event_start와 event_delta는 스레드 스트림에서도 세션 수준 스트림과 동일한 형태를 가지며, 누적 및 조정 패턴이 그대로 적용됩니다. 한 가지 조정 사항은 관리 방식입니다. 스트림 연결당 하나의 누적기 인스턴스를 실행하세요.

# List the session's threads and pick a child: child threads carry a non-null
# parent_thread_id, and the primary thread's parent_thread_id is null.
child_thread = next(
    thread
    for thread in client.beta.sessions.threads.list(session.id)
    if thread.parent_thread_id is not None
)

# The child thread's stream takes the same event_deltas parameter as the
# session stream.
with client.beta.sessions.threads.events.stream(
    child_thread.id,
    session_id=session.id,
    event_deltas=["agent.message"],
) as stream:
    for event in stream:
        match event.type:
            case "event_delta":
                print(event.delta.content.text, end="")
            case "agent.message":
                # The buffered event is the authoritative record; render its content
                print()
                for block in event.content:
                    if block.type == "text":
                        print(block.text, end="")
                print()
            case "session.thread_status_idle":
                break

읽기 루프는 session.thread_status_idle에서 종료됩니다. 이는 세션 스레드의 턴이 완료되고 스레드가 유휴 상태가 될 때 발생하는 이벤트입니다.

제한 사항

미리보기는 응답성에 맞춰 조정되어 있습니다. 다음 제약 조건을 고려하여 구축하세요:

  • 최선 노력: 부하 상황에서 서버는 특정 이벤트의 델타를 버릴 수 있습니다. 그럴 경우 텍스트의 연속된 접두사를 수신한 뒤 해당 이벤트에 대한 추가 델타는 수신하지 않습니다. 버퍼링된 agent.message는 여전히 완전하게 도착합니다. 누적된 미리보기를 절대 최종본으로 취급하지 마세요.
  • 재연결 시 재생 없음: 델타는 옵트인한 연결이 열려 있는 동안 해당 연결에만 전달됩니다. 이는 세션 수준 스트림과 각 세션 스레드 스트림에 동일하게 적용되며, 모델 요청이 시작된 후에 열린 연결은 진행 중인 해당 이벤트에 대한 델타를 수신하지 않습니다. 스트림이 끊기면 이벤트 스트리밍 탭의 재연결 절차를 따르세요. 스트림을 다시 열고 이벤트 기록을 나열합니다. 기록에는 연결이 끊긴 동안 발생한 모든 버퍼링된 이벤트가 포함되며, 미리보기가 기다리던 agent.message도 포함됩니다. 놓친 델타를 다시 요청할 방법은 없습니다.
  • 단일 스레드, 텍스트만: 미리보기는 연결이 읽고 있는 스레드의 어시스턴트 텍스트만 다룹니다. 도구 사용, 도구 결과, MCP 결과, 그리고 다른 세션 스레드의 활동은 해당 연결에서 절대 미리보기되지 않습니다.
  • 시작 전용 agent.thinking: agent.thinking 미리보기는 사고 블록이 시작되었다는 신호로 event_start만 발생시키며, event_delta 이벤트는 뒤따르지 않습니다.
  • 저장되지 않음: event_start와 event_delta는 라이브 스트림에만 존재합니다. 세션의 이벤트 기록(GET /v1/sessions/{session_id}/events)이나 어떤 세션 스레드의 이벤트 기록에도 나타나지 않습니다.

미리보기 문제 해결

스트림이 예상대로 동작하지 않는 경우:

증상의미
버퍼링된 이벤트는 있지만 event_start나 event_delta가 없는 스트림읽고 있는 연결이 옵트인하지 않았거나(event_deltas[]는 세션별이 아니라 연결별로 적용됨), 턴이 스트리밍 중인 스레드를 전혀 건드리지 않았습니다. 미리보기는 스레드 범위이므로, 세션의 스레드를 나열하여(GET /v1/sessions/{session_id}/threads) 어느 스레드가 실행되었는지 찾으세요.
스트림 URL에서 404경로나 ID가 잘못되었거나, 요청에 managed-agents 베타 헤더가 전혀 없습니다. 스레드 엔드포인트는 베타 게이트가 적용되어 있으므로 헤더가 없으면 존재하지 않습니다.
event_deltas를 명시하는 400agent.message와 agent.thinking만 허용됩니다.

추가 시나리오

사용자 정의 도구 호출 처리

에이전트가 사용자 정의 도구를 호출하면:

  1. 세션은 도구 이름과 입력을 담은 agent.custom_tool_use 이벤트를 발생시킵니다.
  2. 세션은 stop_reason: requires_action을 담은 session.status_idle 이벤트와 함께 일시 중지됩니다. 차단 중인 이벤트 ID는 stop_reason.event_ids 배열에 있습니다.
  3. 시스템에서 도구를 실행하고 각각에 대해 user.custom_tool_result 이벤트를 전송하세요. 이때 custom_tool_use_id 매개변수에 이벤트 ID를 결과 콘텐츠와 함께 전달합니다.
  4. 차단 중인 모든 이벤트가 해결되면 세션은 다시 running으로 전환됩니다.
with client.beta.sessions.events.stream(session.id) as stream:
    for event in stream:
        if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
            match stop_reason.type:
                case "requires_action":
                    for event_id in stop_reason.event_ids:
                        # Look up the custom tool use event and execute it
                        tool_event = events_by_id[event_id]
                        result = call_tool(tool_event.name, tool_event.input)

                        # Send the result back
                        client.beta.sessions.events.send(
                            session.id,
                            events=[
                                {
                                    "type": "user.custom_tool_result",
                                    "custom_tool_use_id": event_id,
                                    "content": [{"type": "text", "text": result}],
                                },
                            ],
                        )
                case "end_turn":
                    break

도구 확인

도구 호출은 always_ask 권한 정책에서, 또는 서버가 판단을 내리지 못한 경우의 auto 정책에서 사용자의 확인을 기다립니다. 이 경우:

  1. 세션은 agent.tool_use 또는 agent.mcp_tool_use 이벤트를 발생시킵니다.
  2. 세션은 stop_reason.type이 requires_action인 session.status_idle 이벤트와 함께 일시 중지됩니다. 차단 중인 이벤트 ID는 stop_reason.event_ids 배열에 있습니다.
  3. 각각에 대해 user.tool_confirmation 이벤트를 보내며, tool_use_id 매개변수에 이벤트 ID를 전달합니다. result를 "allow" 또는 "deny"로 설정하세요. 거부 사유를 설명하려면 deny_message를 사용하세요.
  4. 모든 차단 이벤트가 해결되면 세션은 다시 running 상태로 전환됩니다.

각 agent.tool_use 및 agent.mcp_tool_use 이벤트에는 evaluated_permission(allow, ask 또는 deny)이 포함되며, evaluated_permission이 "ask"인 이벤트만 확인을 기다립니다. 대부분의 이벤트에는 어떤 정책이 해당 결과를 만들었는지 기록하는 evaluation 객체도 포함되며, 이는 각 호출이 어떻게 평가되었는지 확인하기에 설명되어 있습니다. 예를 들어, always_ask 정책에서 일시 중지된 bash 호출은 스트림에 다음과 같이 나타납니다:

{
  "type": "agent.tool_use",
  "id": "sevt_01def...",
  "name": "bash",
  "input": {
    "command": "pip install -r requirements.txt"
  },
  "evaluated_permission": "ask",
  "evaluation": {
    "type": "always_ask"
  },
  "processed_at": "2026-03-25T14:01:45Z"
}
with client.beta.sessions.events.stream(session.id) as stream:
    for event in stream:
        if event.type == "session.status_idle" and (stop_reason := event.stop_reason):
            match stop_reason.type:
                case "requires_action":
                    for event_id in stop_reason.event_ids:
                        # Approve the pending tool call
                        client.beta.sessions.events.send(
                            session.id,
                            events=[
                                {
                                    "type": "user.tool_confirmation",
                                    "tool_use_id": event_id,
                                    "result": "allow",
                                },
                            ],
                        )
                case "end_turn":
                    break

유휴 세션 재개

세션은 상호작용 사이에 유지됩니다. 세션이 명시적으로 삭제되지 않는 한 대화 기록은 보존됩니다. 세션이 유휴 상태가 되면 샌드박스가 체크포인트되어 파일 시스템, 설치된 패키지, 에이전트가 생성한 모든 파일을 포함한 전체 샌드박스 상태가 보존됩니다. 이를 통해 비활성 상태에서 깔끔하게 재개할 수 있습니다.

세션을 재개하려면 평소처럼 user.message 이벤트를 전송하세요:

# Resume a previously created session by sending it a new user.message event.
# In production, pass the stored ID of the session you want to resume.
client.beta.sessions.events.send(
    session.id,
    events=[
        {
            "type": "user.message",
            "content": [
                {
                    "type": "text",
                    "text": "Now run the tests against the changes you made earlier.",
                },
            ],
        },
    ],
)

세션 예산 도달

예산과 함께 생성된 세션은 초과 지출하는 대신 일시 중지됩니다. 세션의 추적된 정가 비용(list cost)이 한도에 도달하면, 플랫폼은 각 스레드를 다음 모델 요청 전에 일시 중지하고, 세션은 종료되는 대신 stop_reason이 budget_reached인 유휴 상태가 됩니다. 총액을 한도 너머로 넘긴 요청은 끝까지 실행되므로, session.usage 스냅샷이 보고하는 list_cost는 한도와 같거나 한도를 약간 초과한 값으로 표시될 수 있습니다. 스트림에서 일시 중지는 다음 세 이벤트로 순서대로 도착합니다:

  1. 각 스레드가 일시 중지될 때마다 stop_reason: budget_reached를 담은 session.thread_status_idle.
  2. 세션의 누적 사용량과 추적된 정가 비용의 스냅샷인 session.usage.
  3. stop_reason: budget_reached를 담은 session.status_idle. session.usage 이벤트는 항상 이 유휴 이벤트 바로 앞에 옵니다.

마지막 요청이 한도를 넘는 동시에 턴을 완료한 스레드는 자체 session.thread_status_idle 이벤트에서 end_turn을 보고하지만, 세션은 여전히 budget_reached를 보고합니다. 일시 중지를 감지하려면 세션 수준 stop_reason을 기준으로 삼으세요.

세션이 한도에 도달한 동안에는 이미 진행 중인 작업을 정리하는 이벤트만 받습니다. user.tool_confirmation, user.tool_result, user.custom_tool_result, user.interrupt입니다. user.message를 포함하여 새 작업을 시작하는 모든 이벤트는 해당 목록을 명시하는 400 오류로 거부됩니다. 세션에 도구 요청을 기다리는 스레드와 한도에서 일시 중지된 스레드가 모두 있는 경우, 세션 수준 stop_reason은 budget_reached가 아니라 requires_action입니다. 요청을 정리하는 것은 모델 요청을 트리거하지 않으므로 평소처럼 응답하세요.

한도에서 일시 중지된 세션을 재개하는 이벤트는 없습니다. 대신 세션의 예산을 업데이트하세요. 한도를 소비된 정가 비용보다 높은 값으로 변경하거나, "budget": null로 세션을 업데이트하여 예산을 제거하면 일시 중지된 작업이 자동으로 재개됩니다. 정가 비용 추적 방식과 전체 예산 업데이트 의미론은 세션 예산을 참조하세요.

시스템 메시지 전송

함께 전송되는 턴과 이후 모든 턴에 적용되는 특권 시스템 수준 컨텍스트를 에이전트에 제공하려면 system.message 이벤트를 전송하세요. 에이전트 정의의 system 필드(최상위 시스템 프롬프트를 설정)와 달리, system.message 콘텐츠는 해당 프롬프트를 대체하는 것이 아니라 role: "system" 턴으로 세션의 시스템 컨텍스트에 추가됩니다. 에이전트가 세션 중간에 업데이트된 시스템 수준 지침이 필요할 때 사용하세요. 다른 페르소나, 수정된 제약 조건, 또는 앞으로 모델의 동작을 형성해야 하는 런타임에 가져온 컨텍스트 등입니다.

client.beta.sessions.events.send(
    session.id,
    events=[
        {
            "type": "system.message",
            "content": [
                {
                    "type": "text",
                    "text": "The user's current timezone is America/New_York.",
                },
            ],
        },
    ],
)

세션이 stop_reason: requires_action으로 유휴 상태인 동안, system.message는 동일한 요청에서 도구 결과 이벤트 뒤에 따라올 때만 허용됩니다. 단독으로 또는 user.message와 함께 전송하면 대기 중인 도구 이벤트가 해결될 때까지 거부됩니다. content는 1~1000개의 텍스트 항목을 받습니다.

사용량 추적

세션 객체에는 세션의 누적 사용량을 담은 usage 필드가 포함되어 있습니다. 여기에는 토큰 수, 서버 도구 사용, 활성 시간, 추적된 정가 비용이 포함됩니다. 세션이 유휴 상태가 된 후 세션을 가져오면 최신 합계를 읽을 수 있습니다.

{
  "id": "sesn_01...",
  "status": "idle",
  "usage": {
    "input_tokens": 5000,
    "output_tokens": 3200,
    "cache_read_input_tokens": 20000,
    "cache_creation": {
      "ephemeral_5m_input_tokens": 2000,
      "ephemeral_1h_input_tokens": 0
    },
    "list_cost": {
      "amount": "187",
      "currency": "USD"
    },
    "active_seconds": 342.5,
    "server_tool_use": {
      "web_search_requests": 3,
      "web_fetch_requests": 0
    }
  }
}

input_tokens는 캐시되지 않은 입력 토큰을 보고하고, output_tokens는 세션 내 모든 모델 호출에 걸친 총 출력 토큰을 보고합니다. cache_read_input_tokens 필드는 프롬프트 캐시에서 읽은 토큰을 보고하며, cache_creation 객체는 캐시 생성 토큰을 캐시 수명별로(ephemeral_5m_input_tokens 및 ephemeral_1h_input_tokens) 세분화합니다. 캐시 항목은 기본적으로 5분 TTL을 사용하므로, 해당 시간 내에 연속으로 이어지는 턴은 캐시 읽기의 혜택을 받아 토큰당 비용이 줄어듭니다.

list_cost는 공개 정가 요율로 책정된 세션의 누적 소비량으로, 문자열 형태의 정수 센트 값과 통화 코드로 표시됩니다. active_seconds는 세션에서 최소 하나의 스레드가 실행 중이었던 누적 시간입니다. 동시 스레드의 겹치는 활동은 한 번만 계산되며, 이는 각 스레드 자체의 활성 시간을 합산하는 세션 stats 객체의 active_seconds와 다릅니다. 이 중복 제거된 수치가 세션의 런타임 비용이 책정되는 기준 시간입니다. server_tool_use는 가격 책정을 위해 서버에서 실행된 도구 요청을 계산합니다. 웹 검색 요청은 요청당 정가 비용에 반영되며, 웹 가져오기(web fetch) 요청은 요청당 요금이 없고 계량되지 않으므로 web_fetch_requests는 0으로 표시됩니다. 각 세션 스레드의 자체 usage에도 list_cost와 active_seconds가 포함됩니다. 스레드별 수치는 독립적으로 반올림되며 세션의 실행 시간 비용을 제외하므로, 합산해도 세션의 list_cost와 정확히 일치하지 않습니다. 세션 수치가 권위 있는 기준 값입니다.

이러한 합계를 확인하기 위해 세션을 폴링할 필요는 없습니다. session.usage 이벤트는 동일한 누적 스냅샷(usage 객체와 세션의 budget, 세션에 예산이 없으면 null)을 세션 스트림과 이벤트 기록에 담아 전달합니다. 이 이벤트는 타이머가 아니라 유휴 전환 시점에 발생합니다. 세션은 중지 사유와 관계없이 유휴 상태가 되기 직전에 한 번, 그리고 스레드가 세션 예산에서 일시 중지될 때 한 번 이벤트를 발생시킵니다. 따라서 스트림 리더는 추가로 가져오기를 하지 않고도 턴의 최종 비용 또는 예산에 도달한 작업의 최종 비용을 확인할 수 있습니다.

지출 한도를 적용하려면 사용량을 폴링하여 직접 세션을 중지하는 대신 세션 예산을 설정하세요. 플랫폼은 세션의 소비량을 지속적으로 책정하며, 세션의 정가 비용이 한도에 도달하면 각 스레드를 다음 모델 요청 전에 일시 중지합니다. 스트림에서 이것이 어떻게 보이는지는 세션 예산 도달을 참조하세요.

Console 관측성

Claude Console에는 코드를 작성하지 않고도 에이전트가 수행한 작업을 검사할 수 있는 세션 뷰어가 포함되어 있습니다. Console 사이드바의 Managed Agents 아래에서 Sessions를 선택하면 워크스페이스의 모든 세션을 상태, 에이전트, 토큰 사용량, 비용, 생성 시간과 함께 볼 수 있으며, 세션을 선택하여 열 수 있습니다. 세션 뷰어는 Developer 및 Admin만 접근할 수 있습니다. 다음 내용을 보여줍니다:

  • 타임라인 미니맵: 시간에 따른 세션 활동을 확대/축소할 수 있는 개요로, 멀티에이전트 세션에서는 스레드당 하나의 레인이 표시됩니다. 레인을 선택하면 해당 스레드를 볼 수 있고, 마크를 선택하면 해당 이벤트로 이동합니다.
  • 트랜스크립트: 모델 요청별로 그룹화된 대화로, 사고(thinking), 입력 및 결과가 포함된 도구 호출, 스트리밍되는 메시지 텍스트를 포함합니다. 이벤트를 필터링하고 JSON으로 복사하거나 다운로드할 수 있습니다.
  • 인스펙터: 세션에 대한 세부 정보를 다섯 개의 탭으로 보여주는 크기 조절 가능한 사이드 패널입니다:
    • Session은 세션의 세부 정보와 메타데이터, 시간에 따른 누적 비용, 그리고 예산이 설정된 경우 세션 예산 대비 지출을 보여줍니다.
    • Events는 현재 스레드의 모든 원시 이벤트를 서버가 보낸 순서대로 나열합니다. 이벤트를 선택하면 해당 JSON을 볼 수 있습니다. 페이지가 열려 있는 동안 스트리밍된 메시지에는 해당 이벤트 델타를 보여주는 Deltas 뷰도 있습니다.
    • Tools는 세션의 에이전트에 구성된 도구를 호출 횟수, 실패, 중앙값 소요 시간과 함께 나열합니다. 도구를 선택하면 해당 호출을 보고 트랜스크립트의 특정 호출로 이동할 수 있습니다.
    • Resources는 마운트된 파일, 리포지토리, 메모리 저장소를 컨테이너 경로와 함께 나열하며, 각 저장소의 메모리와 이 세션이 해당 메모리에 가한 변경 사항, 에이전트가 /mnt/session/outputs에 작성한 파일, 그리고 세션의 에이전트에 연결된 스킬을 포함합니다.
    • Threads는 모든 스레드를 상태, 컨텍스트 크기, 비용과 함께 나열합니다. 스레드를 선택하면 에이전트, 모델, 컨텍스트 사용량, 비용 등의 세부 정보를 볼 수 있습니다.

세션 URL에 ?event={event_id}를 추가하면 특정 이벤트에서 세션을 열 수 있습니다.

ant beta:sessions connect를 사용하면 ant CLI에서 동일한 뷰어를 열거나 터미널에서 세션을 실시간으로 확인할 수 있습니다. 자세한 내용은 터미널에서 Managed Agents 세션에 연결하기를 참조하세요.

디버깅 팁

  • 세션 이벤트 확인: 세션 오류는 session.error 이벤트를 통해 전달됩니다
  • 도구 결과 검토: 도구 실행 실패는 종종 예상치 못한 에이전트 동작을 설명해 줍니다
  • 토큰 사용량 추적: 토큰 소비를 모니터링하여 프롬프트를 최적화하고 비용을 줄이세요
  • 시스템 프롬프트 사용: 시스템 프롬프트에 로깅 지침을 추가하여 에이전트가 자신의 추론을 설명하도록 하세요
  • 프리뷰 문제 해결: 이벤트 델타를 옵트인한 스트림이 예상대로 동작하지 않으면 프리뷰 문제 해결을 참조하세요

Was this page helpful?