Claude Platform Docs
Managed Agents고급 오케스트레이션

세션 스레드

멀티에이전트 세션의 스레드를 나열, 중단, 보관하고, 스레드의 이벤트를 읽고, 스레드 전반에서 도구 권한을 처리합니다.

멀티에이전트 세션에서 각 에이전트는 자체 session thread(세션 스레드)에서 작업합니다. 이 페이지에서는 스레드를 나열, 중단, 보관하는 방법, 스레드가 보내는 이벤트, 그리고 스레드 전반에서 도구 권한이 작동하는 방식을 다룹니다. 워크플로 실행도 세션 스레드를 생성합니다.

기본 스레드와 세션 스레드

세션 수준 이벤트 스트림(/v1/sessions/{session_id}/events/stream)은 primary thread(기본 스레드)로 간주되며, 모든 스레드에 걸친 모든 활동의 요약된 보기를 포함합니다. 서브에이전트의 전체 활동은 볼 수 없지만, 작업의 시작과 끝, 그리고 도구 권한 요청과 같은 차단 이벤트는 볼 수 있습니다.

세션 스레드는 특정 에이전트의 활동을 자세히 살펴보는 곳입니다.

세션 status는 모든 에이전트 활동의 집계입니다. 하나 이상의 스레드가 running이면 전체 세션 상태도 running입니다. 실행 중인 워크플로 실행도 해당 스레드 중 어느 것도 작업하고 있지 않을 때조차 세션을 running 상태로 유지할 수 있습니다. 작업 중인 스레드가 없고 어떤 스레드가 클라이언트를 기다리고 있으면 세션은 idle입니다. 작업이 완료되는 시점 파악하기를 참조하세요.

세션 예산은 세션의 모든 스레드에 걸쳐 공유되는 단일 한도입니다. 한도에 도달하면 스레드는 각각 독립적으로 일시 중지되며, 각 스레드의 비용은 해당 스레드가 실제로 사용한 모델 기준으로 책정됩니다.

스레드 나열

다음과 같이 세션과 연결된 모든 스레드를 나열합니다:

for thread in client.beta.sessions.threads.list(session.id):
    agent = thread.agent
    label = agent.type if agent.type == "advisor" else agent.name
    print(f"[{label}] {thread.status}")

전체 목록에는 기본 스레드가 포함됩니다. 기본 스레드의 parent_thread_id는 null입니다. 그 외의 모든 스레드는 하위 스레드입니다. workflow_run_id는 실행의 스레드를 제외하고는 null입니다.

특정 상태의 스레드만 나열하려면 요청에 statuses[]를 추가하고, 둘 이상의 상태를 지정하려면 ?statuses[]=running&statuses[]=idle처럼 반복하세요. 생략하면 모든 상태의 스레드가 반환됩니다.

세션 스레드 중단

특정 스레드를 중지하려면 session_thread_id와 함께 user.interrupt를 보내세요. session_thread_id를 생략하면 기본 스레드를 포함하여 세션의 보관되지 않은 모든 스레드가 중단됩니다. 동적 워크플로가 있는 세션에서 중단은 어떤 실행도 종료하지 않으며, 실행의 스레드를 지정한 중단은 아무것도 중지하지 않습니다. 중단은 다른 하위 스레드의 대기 중인 도구 호출을 닫지만, 실행 스레드의 대기 중인 도구 호출을 닫는 데에는 의존하지 마세요. 실행이 열려 있는 세션 중단하기를 참조하세요.

client.beta.sessions.events.send(
    session.id,
    events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
)

requires_action에서 차단된 서브에이전트의 스레드에 대해 중단을 보내면, 대기 중인 각 도구 호출이 오류 도구 결과("Tool execution was interrupted before completion. Please retry.")로 닫히고 stop_reason: end_turn과 함께 session.thread_status_idle이 직접 다시 발생합니다. 이때 모델은 샘플링되지 않습니다. end_turn 또는 budget_reached로 유휴 상태인 하위 스레드에 대한 중단은 아무 작업도 하지 않습니다. 종료된 스레드를 지정한 중단은 400 오류를 반환합니다. 중단된 하위 스레드는 턴이 끝날 때 보내는 보고를 기본 스레드의 에이전트에게 보내지 않습니다. 해당 에이전트가 하위 스레드를 기다리는 동안에는 user.message나 다른 스레드의 보고와 같은 다른 무언가가 도달할 때까지 새 턴을 시작하지 않습니다.

세션 스레드 보관

세션 스레드가 작업을 완료하면 선택적으로 보관할 수 있습니다. 스레드를 보관하면 25개 하위 스레드 제한에서 해당 자리가 비워집니다. 서버는 워크플로 실행의 스레드를 직접 보관합니다. 이러한 스레드는 보관할 필요가 없으며, 실행이 열려 있는 동안에는 보관할 수 없습니다.

archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
print(archived.status, archived.archived_at)

보관은 스레드가 idle인 경우에만 성공합니다. requires_action에 머물러 있는 스레드는 유휴 상태로 간주되어 바로 보관할 수 있으며, 실행 중인 스레드만 먼저 중단해야 합니다:

client.beta.sessions.events.send(
    session.id,
    events=[{"type": "user.interrupt", "session_thread_id": thread.id}],
)
archived = client.beta.sessions.threads.archive(thread.id, session_id=session.id)
print(archived.status, archived.archived_at)

기본 스레드 이벤트

이러한 이벤트는 /v1/sessions/{session_id}/events/stream의 기본 스레드에서 멀티에이전트 활동을 보여줍니다. 메시지 방향 이벤트는 해당 이벤트가 나타나는 스트림의 스레드를 기준으로 이름이 지정됩니다. agent.thread_message_received는 다른 스레드로부터 이 스레드에 메시지가 도착했음을 의미하고, agent.thread_message_sent는 이 스레드가 메시지를 보냈음을 의미합니다. 예를 들어 기본 스레드의 에이전트가 위임한 작업은 하위 스레드 자체의 스트림에 agent.thread_message_received 이벤트로 도착합니다.

유형설명
session.thread_created스레드가 생성되었습니다. session_thread_id와 agent_name을 포함합니다.
session.thread_status_running스레드가 활동을 시작했습니다.
session.thread_status_idle스레드와 연결된 에이전트가 입력을 기다리고 있습니다. 에이전트가 중지된 이유를 나타내는 stop_reason을 포함합니다.
session.thread_status_terminated스레드가 종료되어 더 이상 입력을 받지 않습니다. 예를 들어 보관되었거나 복구할 수 없는 오류가 발생한 경우입니다. 어드바이저 스레드도 자문이 끝나면 종료됩니다.
agent.thread_message_received기본 스레드에서, 서브에이전트가 기본 스레드의 에이전트에게 보고나 질문을 보냈습니다. from_session_thread_id, from_agent_name, content를 포함합니다.
agent.thread_message_sent기본 스레드에서, 기본 스레드의 에이전트가 서브에이전트에게 작업이나 후속 메시지를 보냈습니다. to_session_thread_id, to_agent_name, content를 포함합니다.

어드바이저 자문은 예약된 이름 anthropic.advisor로 이와 동일한 스레드 이벤트를 발생시킵니다(스레드 수명 주기 이벤트에서는 agent_name으로, 조언 전달에서는 from_agent_name으로). 순서는 세션에 어드바이저 제공하기를 참조하세요.

워크플로 실행의 스레드는 기본 스트림에 다음과 같이 표시됩니다:

  • 수명 주기 이벤트: 각 실행 스레드는 실행의 workflow_run_id와 함께 session.thread_created를 보내고, session.thread_status_running, session.thread_status_idle, session.thread_status_terminated 이벤트를 보냅니다.
  • 메시지 이벤트: 실행 스레드의 프롬프트인 agent.thread_message_received 이벤트는 해당 스레드 자체의 스트림에만 남습니다.
  • 실행 이벤트: workflow_run.* 이벤트도 이 스트림에 도착합니다. 실행 이벤트를 참조하세요.
  • 사용자를 기다리는 도구 호출: 클라이언트가 필요한 실행 스레드의 도구 호출은 다른 하위 스레드와 마찬가지로 이 스트림에 교차 게시됩니다. 도구 권한 및 사용자 정의 도구를 참조하세요.

세션 스레드 이벤트

중요한 이벤트는 기본 스레드로 전달됩니다. 하지만 특정 에이전트의 추론과 도구 호출을 조사하고 싶을 수도 있습니다. 그렇게 하려면 연결된 세션 스레드의 이벤트를 스트리밍하거나 나열하세요.

각 세션 스레드에는 /v1/sessions/{session_id}/threads/{thread_id}/stream에 자체 이벤트 스트림이 있으며, 세션 수준 스트림과 동일한 event_deltas[] 매개변수를 받으므로 모델이 생성하는 대로 서브에이전트의 텍스트를 미리 볼 수 있습니다. 연결은 읽고 있는 스레드만 미리 봅니다. 하위 스레드의 미리보기는 세션 수준 스트림에 절대 나타나지 않으므로, 서브에이전트를 실시간으로 보려면 해당 스레드 자체의 스트림을 여세요. 옵트인, 누적, 미리보기 조정에 대해서는 세션 스레드 이벤트 미리보기를 참조하세요.

워크플로 실행에서 서버는 워크플로, 즉 기본 스레드의 에이전트가 작성한 프로그램을 실행합니다. 실행의 각 스레드에서 첫 번째 agent.thread_message_received는 워크플로가 작성한 프롬프트입니다. 해당 이벤트의 from_session_thread_id는 기본 스레드의 ID이고, from_agent_name은 없습니다. API는 프롬프트의 텍스트를 보장하지 않으므로 이를 파싱하지 마세요. 기본 스레드의 스트림에 있는 해당 스레드의 session.thread_status_terminated 이벤트는 스레드가 완료되었음을 알려줍니다. 스레드가 워크플로에 반환한 결과를 기록하는 이벤트는 없습니다.

스레드의 스트림은 이전 이벤트를 재생하지 않습니다. session.thread_created 직후에는 서버가 스레드의 첫 번째 이벤트를 그 이후에 기록하기 때문에 실행 스레드의 이벤트 목록이 비어 있을 수 있습니다. 따라서 먼저 스레드의 스트림을 연 다음 스레드의 이벤트를 나열하고, 목록이 반환한 id를 가진 스트리밍된 이벤트는 건너뛰세요.

with client.beta.sessions.threads.events.stream(
    thread.id,
    session_id=session.id,
) as stream:
    for event in stream:
        match event.type:
            case "agent.message":
                for block in event.content:
                    if block.type == "text":
                        print(block.text, end="")
            case "session.thread_status_idle":
                break

도구 권한 및 사용자 정의 도구

서브에이전트가 도구 호출을 실행하기 위한 권한이나 사용자 정의 도구의 결과처럼 클라이언트로부터 무언가가 필요한 경우, 해당 이벤트는 원래 세션 스레드를 식별하는 session_thread_id와 함께 기본 스레드에 교차 게시됩니다. 도구 호출은 always_ask에서, 또는 서버가 판단을 내리지 못한 경우 auto에서 사용자의 권한이 필요합니다.

{
  "type": "session.thread_status_idle",
  "id": "sevt_01ABC...",
  "session_thread_id": "sthr_01DEF...",
  "agent_name": "code-reviewer",
  "stop_reason": {
    "type": "requires_action",
    "event_ids": ["sevt_01XYZ..."]
  }
}

user.tool_confirmation(tool_use_id 포함) 또는 user.custom_tool_result(custom_tool_use_id 포함)를 게시하세요. 서버가 응답을 올바른 스레드로 자동 라우팅합니다. 응답은 기본 스레드와 서브에이전트의 스레드에 서로 다른 id 값으로 나타날 수 있습니다. 두 복사본을 대조하려면 id가 아니라 type과 tool_use_id(또는 custom_tool_use_id)를 비교하세요.

세션은 running 상태인 스레드가 없을 때만 idle이 되므로, session.status_idle은 서브에이전트의 호출보다 훨씬 나중에 도착할 수 있습니다. 이를 기다릴 필요는 없습니다. 교차 게시된 agent.custom_tool_use 이벤트가 도착하는 즉시 user.custom_tool_result를 보내세요.

auto에서는 사용자의 user.message 이벤트로 인해 서버가 원래라면 거부했을 호출을 허용할 수 있습니다. 서브에이전트의 스레드에 있는 어떤 것도 사용자의 의도로 간주되지 않습니다. 클라이언트는 그곳에 메시지를 게시하지 않으며, 기본 스레드의 에이전트가 서브에이전트에게 보내는 메시지도 사용자의 의도로 간주되지 않습니다. 서버가 auto에서 호출을 거부하면 아무것도 교차 게시되지 않습니다. 이벤트와 오류 도구 결과는 서브에이전트 자체의 스레드 스트림에만 나타나며, 서브에이전트는 계속 실행됩니다.

다음 예제는 도구 확인 핸들러의 이벤트 루프 안에 들어갑니다. stop_reason.event_ids의 각 ID에 대해 호출을 허용하는 user.tool_confirmation을 보냅니다. 동일한 패턴이 user.custom_tool_result에도 적용됩니다.

for event_id in stop.event_ids:
    client.beta.sessions.events.send(
        session.id,
        events=[
            {
                "type": "user.tool_confirmation",
                "tool_use_id": event_id,
                "result": "allow",
            }
        ],
    )

앞의 패턴은 유휴 이벤트가 나열하는 호출에 응답합니다. 기본 스트림에서 서브에이전트의 session.thread_status_idle 이벤트는 해당 stop_reason.event_ids가 나열하는 agent.tool_use 또는 agent.mcp_tool_use 이벤트보다 먼저 도착할 수 있습니다. 아직 이벤트가 도착하지 않은 호출에 대한 user.tool_confirmation은 400을 반환할 수 있습니다. 이를 방지하려면 evaluated_permission이 ask인 각 호출에 대해, 해당 호출 자체의 이벤트가 기본 스트림에 도착했을 때 응답하세요.

Was this page helpful?