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

워크플로 실행

에이전트의 워크플로 실행을 추적합니다: 실행의 상태와 이벤트, 작업이 완료되는 시점, 실행이 차단하는 요청, 예산 및 제한을 다룹니다.

Workflow(워크플로)는 에이전트가 여러 에이전트를 실행하고 그 에이전트들이 반환한 결과를 결합하기 위해 작성하는 프로그램입니다. Workflow run(워크플로 실행)은 하나의 워크플로를 실행한 것입니다. Dynamic workflows(동적 워크플로)는 에이전트가 워크플로를 작성하고 실행을 시작할 수 있게 하는 기능입니다. 에이전트의 multiagent 블록에 있는 workflows 설정으로 이 기능을 켜거나 끕니다.

서버는 워크플로를 백그라운드에서 실행합니다. 워크플로의 에이전트는 워크플로가 필요로 할 때 서버가 생성하는 세션 스레드에서 작업합니다. 실행은 세션의 이벤트 스트림에서 추적합니다. 실행은 에이전트만 시작할 수 있습니다. 사용자가 보내는 어떤 이벤트도 실행을 종료하지 않지만, 세션을 보관하면 실행이 종료될 수 있습니다.

동적 워크플로의 작동 방식

세션이 실행하는 에이전트는 사용자가 설명한 작업에 맞춰 각 워크플로를 작성합니다. 워크플로는 프로그램입니다. 다른 에이전트를 실행하고, 각 에이전트가 반환한 결과를 수집하고, 그 결과를 결합합니다. 이를 통해 에이전트는 수백 개의 문서를 검토하는 것처럼 하나의 대화로 처리하기에는 너무 큰 작업을 맡을 수 있습니다. 실행 중에 에이전트는 계속 작업하거나 턴을 종료할 수 있으며, 실행 상태를 확인할 수도 있습니다.

Agent on the primary threadWorkflow runThe server runs the workflow in the backgroundPhase: Read the contractsAgent threadAgent threadAgent threadAgents work at the same timePhase: Reconcile the findingsAgent threadAn agent works with the resultsThe program chooses what runs hereand can repeat a stepThe agent writes a workflow (a program)and starts a runThe program passesthe results onWhen the run ends, the agentreads what the run did

다이어그램은 하나의 예시를 보여 줍니다. 에이전트가 작성하는 각 워크플로에는 고유한 단계와 에이전트가 있습니다. 실행에는 다음과 같은 계층이 있습니다:

  • 워크플로 실행: 서버는 워크플로를 하나의 워크플로 실행으로 백그라운드에서 실행합니다. 하나의 세션에서 여러 실행이 동시에 열려 있을 수 있습니다.
  • 단계: 워크플로는 작업을 "phase"(단계)로 나눌 수 있습니다. 단계는 "Read the contracts"와 같이 이름이 붙은 실행의 한 구간입니다. 실행의 진행 상황은 단계 이벤트로 추적합니다.
  • 에이전트 스레드: 단계 안에서 프로그램은 에이전트를 실행합니다. 각 에이전트는 프로그램이 작성한 프롬프트를 가지고 자체 세션 스레드에서 작업합니다. 실행 안의 에이전트는 프로그램이 직접 정의하는 인라인 에이전트일 수도 있고, 사용자가 workflows.predefined_agents에 나열하는 사전 정의 에이전트일 수도 있습니다. 각 스레드가 보여 주는 내용은 실행의 스레드를 참조하세요.

프로그램은 다음을 수행할 수 있습니다:

  • 에이전트를 동시에 실행: 프로그램은 여러 에이전트를 동시에 실행할 수 있으며, 이를 "fanning out"(팬아웃)이라고 합니다. 다이어그램에서는 첫 번째 단계에서 세 에이전트가 계약서를 읽습니다.
  • 한 에이전트의 결과를 다른 에이전트에 전달: 각 에이전트는 결과를 프로그램에 반환합니다. 프로그램은 그 결과를 다른 에이전트에 전달할 수 있습니다. 다이어그램에서는 두 번째 단계의 에이전트가 처음 세 에이전트가 반환한 결과를 가지고 작업합니다. 실행의 에이전트들은 또한 세션의 샌드박스에서 동일한 파일을 가지고 작업합니다.
  • 다음 작업을 스스로 진행: 에이전트의 결과는 세션이 실행하는 에이전트가 아니라 프로그램으로 전달됩니다. 프로그램이 다음에 실행할 에이전트를 결정하고, 그 에이전트들의 프롬프트를 작성합니다.
  • 반복 및 선택: 단계 안에서 프로그램은 작업을 반복하고, 에이전트가 반환한 결과를 바탕으로 다음에 할 일을 선택할 수 있습니다. 예를 들어 검토를 통과하거나 정해진 횟수를 모두 사용할 때까지 초안을 수정하게 할 수 있습니다. 다이어그램에서는 프로그램이 두 번째 단계 안에서 한 작업을 반복할 수 있습니다.
  • 실패한 에이전트 처리: 에이전트 중 하나가 실패하면 프로그램은 그 실패를 처리하거나, 실패로 인해 실행이 종료되도록 둘 수 있습니다.

실행이 종료되면 세션이 실행하는 에이전트는 실행이 수행한 내용을 읽을 턴을 받습니다. 그런 다음 사용자에게 답변하거나 다른 실행을 시작할 수 있습니다. 그 턴이 나중에 오거나 오지 않는 경우는 실행 이벤트에 나와 있습니다.

실행이 작업을 수행하는 방식, 예를 들어 작업을 나누는 방식이나 에이전트가 실패했을 때 수행할 작업을 안내할 수 있습니다. 실행을 사용할 시점을 에이전트에 알려주기를 참조하세요.

실행의 상태 전환 방식

실행은 실행 중 상태 또는 유휴 상태로 시작합니다. 예를 들어 예산에 도달하면 실행 중인 실행이 일시 중지되어 유휴 상태가 되며, 이후 예산을 늘리거나 제거하면 중단으로도 일시 중지된 경우가 아닌 한 다시 실행됩니다. 실행 중인 실행은 워크플로가 완료되거나, 에이전트가 실행을 중지하거나, 실행이 실패하거나, 수명이 지나거나, 세션이 보관되면 종료됩니다. 유휴 상태의 실행도 종료될 수 있습니다. 예를 들어 에이전트가 실행을 중지하거나 세션이 보관되는 경우입니다.

실행은 실행 중이든 유휴 상태이든 workflow_run.created 이벤트부터 workflow_run.status_ended 이벤트까지 열려 있는 상태입니다. 실행은 예를 들어 세션의 예산에 도달하여 일시 중지된 동안 유휴 상태입니다. 실행의 수명은 기본적으로 24시간입니다. 에이전트는 실행을 시작할 때 더 짧은 수명을 설정할 수 있습니다. 실행이 클라이언트를 기다리는 데 보낸 시간도 그 수명에 포함됩니다. 일시 중지가 실행의 수명 경과를 멈추지 않으므로, 일시 중지된 상태로 남아 있는 실행은 timeout_error로 종료될 수 있습니다. 다음 이벤트는 실행의 시작, 단계, 종료를 보고합니다. 예산에 도달하여 일시 중지될 때도 이벤트가 하나 전송됩니다. 중단 후의 일시 중지는 이벤트를 전송하지 않을 수 있습니다. 모든 workflow_run.* 이벤트에는 workflow_run_id가 포함되며, 이 값은 실행이 생성되지 않은 경우의 workflow_run.error에서만 null입니다.

실행 이벤트

실행 이벤트는 기본 스레드의 스트림인 세션의 이벤트 스트림으로 도착하며, 세션의 이벤트를 나열할 때도 반환됩니다. 실행 이벤트는 웹훅을 트리거하지 않습니다. 실행의 스레드에서 발생하는 상태 이벤트도 같은 스트림으로 도착합니다. 각 이벤트는 session_thread_id에 해당 스레드를 명시하며, 실행의 스레드는 session.thread_created 이벤트에 실행의 workflow_run_id가 있었던 스레드입니다.

이벤트도착 시점수행할 작업
workflow_run.created에이전트가 실행을 시작했습니다. workflow_run_id(wrun_…), 실행의 name과 description, 그리고 워크플로가 선언한 단계인 phases가 포함되며, 각 단계에는 id, name, description이 있습니다. 워크플로가 설명을 제공하지 않으면 description은 null입니다. phases는 항상 존재하며 비어 있을 수 있습니다. 실행과 단계의 name 및 description은 모델이 작성한 텍스트이므로 사용자 요청의 단어를 반복할 수 있습니다. 실행의 name은 서버가 할당한 이름일 수도 있습니다.실행을 열린 상태로 추적합니다. 실행의 name과 phases 대비 진행 상황을 표시합니다.
workflow_run.status_running실행이 실제로 실행되기 시작할 때(created 이후 한참 뒤일 수 있음), 그리고 예산으로 인한 일시 중지 후 재개될 때마다 도착합니다. 중단 후의 재개는 이 이벤트를 전송하지 않을 수 있습니다. 유휴 상태로 시작하는 실행은 workflow_run.status_idle을 먼저 받을 수 있습니다.실행을 실행 중으로 표시합니다.
workflow_run.status_idle예를 들어 세션의 예산에 도달하여 실행이 일시 중지되었습니다. 이벤트는 그 이유를 알려 주지 않습니다. 중단 후의 일시 중지는 이 이벤트를 전송하지 않을 수 있습니다.계속하려면 예산 및 제한 또는 실행이 열려 있는 세션 중단하기를 참조하세요.
workflow_run.phase_started, workflow_run.phase_ended워크플로가 단계에 진입하거나 단계를 벗어났거나, 실행의 종료로 인해 아직 열려 있던 단계가 닫혔습니다. 종료 이벤트는 단계의 작업이 완료되었는지 여부를 알려 주지 않습니다. 두 이벤트 모두 workflow_run_phase_id를 포함합니다. 종료 이벤트에는 자신이 닫는 시작 이벤트의 id인 phase_started_id도 있습니다. 두 이벤트 모두 단계의 이름은 포함하지 않으므로, workflow_run.created의 phases에서 workflow_run_phase_id로 찾아보세요.진행 상황을 업데이트합니다. 단계는 phases의 순서대로 한 번에 하나씩, 각각 최대 한 번 실행되지만 API가 이를 보장하지는 않습니다. 단계의 종료를 phase_started_id로 시작과 매칭하세요. 완료되는 실행에서도 둘 이상의 열린 단계, phases에 없는 단계, 나열되었지만 시작되지 않는 단계를 처리하세요. 시작되는 모든 단계는 실행의 workflow_run.status_ended 이전에 종료됩니다.
workflow_run.status_ended실행이 종료되었습니다. 항상 실행의 workflow_run.* 이벤트 중 마지막입니다. result를 포함합니다.result를 읽습니다(다음 표 참조). 그런 다음 에이전트는 실행이 어떻게 종료되었는지 읽을 턴을 받습니다. 예산에 도달했거나 기본 스레드가 클라이언트를 기다리는 동안에는 그 턴이 나중에 옵니다. 중단 후에는 그 턴이 오지 않을 수 있으므로 user.message를 보내거나 result를 직접 읽으세요. 보관 또는 종료 후에는 그 턴이 오지 않습니다.
workflow_run.error서버가 실행의 오류 또는 거부한 시작을 보고합니다. error로 종료되는 실행은 workflow_run.status_ended 이전에 동일한 오류와 함께 이 이벤트를 받습니다. type과 로그에 기록해도 안전한 message로 구성된 error를 포함합니다. 실행이 생성되지 않은 경우 workflow_run_id는 null입니다.로그에 기록하되, 실행의 종료로 간주하지 마세요. workflow_run_id가 null이면 실행이 시작되지 않은 것입니다. 그렇지 않으면 workflow_run.status_ended가 올 때까지 실행을 계속 추적하세요.
result의미
{"type": "completed"}워크플로가 실행을 마쳤습니다. 결과는 작업이 통과했는지 여부를 알려 주지 않습니다. 스레드의 작업이 실패했거나 스레드를 생성할 수 없었더라도 실행은 completed로 종료될 수 있습니다. 실패한 작업을 찾으려면 각 실행의 스레드의 이벤트를 읽으세요.
{"type": "stopped"}에이전트가 실행을 중지했거나 세션이 보관되었습니다. 이벤트는 둘 중 어느 쪽인지 알려 주지 않으며, 이후 릴리스에서 다른 원인이 추가될 수 있습니다.
timeout_error가 포함된 error실행이 수명에 도달했습니다. 수명은 기본적으로 24시간이거나 에이전트가 설정한 값입니다.
program_error가 포함된 error워크플로가 실패했습니다. 워크플로의 코드가 실패했거나, 제한 이외의 워크플로 규칙을 위반했습니다. 또는 실행의 스레드 중 하나가 실패했거나 생성될 수 없었고, 워크플로가 그로 인해 실행이 종료되도록 두었습니다.
thread_limit_error가 포함된 error실행이 워크플로가 시작하는 에이전트 수 제한을 초과했습니다.
unknown_error가 포함된 error서버가 실행을 계속할 수 없었거나, 실행이 워크플로에 대한 서버의 다른 제한 중 하나를 초과했습니다.

오류 결과는 {"type": "error", "error": {"type": "timeout_error", "message": "..."}}와 같은 형태이며, 여기서 message는 로그에 기록해도 안전합니다. 인식할 수 없는 result.type은 다른 방식으로 종료된 실행으로, 인식할 수 없는 error.type은 오류로 처리하세요. 모델, MCP 서버, 자격 증명, 결제 등 세션이 의존하는 요소가 실패하면 실패한 스레드의 스트림이 session.error를 받습니다. 그것만으로는 실행이 종료되지 않습니다. 하지만 그로 인해 실행의 스레드 중 하나가 실패하고 워크플로가 그로 인해 실행이 종료되도록 두면, 실행은 program_error로 종료됩니다.

예를 들어 계약 검토 에이전트에게 300개의 계약서 중 어느 계약서에 지배권 변경 조항이 있는지 물으면, 에이전트가 실행을 시작합니다:

  1. workflow_run.created가 실행의 이름을 "Find change-of-control clauses"로 지정하고 phases에 "Read the contracts"와 "Reconcile the findings" 단계를 나열합니다. 그다음 workflow_run.status_running이 이어집니다.
  2. 단계 이벤트가 각 단계를 표시하고, 실행이 생성하는 각 스레드는 실행의 workflow_run_id와 함께 session.thread_created를 전송합니다.
  3. workflow_run.status_ended가 result: {"type": "completed"}와 함께 도착합니다.
  4. 에이전트가 "300개의 계약서 중 41개에 해당 조항이 있습니다"라고 답변하고, session.status_idle이 end_turn과 함께 도착합니다.

실행의 첫 번째 이벤트는 단계를 나열합니다:

{
  "type": "workflow_run.created",
  "id": "sevt_01abc...",
  "workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
  "name": "Find change-of-control clauses",
  "description": "Reads each contract and lists those that have the clause.",
  "phases": [
    {
      "id": "wrph_01Kd3a1f3",
      "name": "Read the contracts",
      "description": "Reads each contract for the clause."
    },
    { "id": "wrph_01Kd3b7c9", "name": "Reconcile the findings", "description": null }
  ],
  "processed_at": "2026-10-09T14:01:45Z"
}

각 단계 이벤트는 workflow_run_phase_id로 단계를 명시합니다. 이 값은 phases에 있는 id이지만, API가 이를 보장하지는 않습니다:

{
  "type": "workflow_run.phase_started",
  "id": "sevt_01def...",
  "workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
  "workflow_run_phase_id": "wrph_01Kd3a1f3",
  "processed_at": "2026-10-09T14:01:46Z"
}

실행의 마지막 이벤트는 실행이 어떻게 종료되었는지 보고합니다:

{
  "type": "workflow_run.status_ended",
  "id": "sevt_01ghi...",
  "workflow_run_id": "wrun_01J8XkN5uT3vHpLqRfWdY2",
  "result": { "type": "completed" },
  "processed_at": "2026-10-09T14:09:12Z"
}

실행의 스레드

실행의 각 에이전트는 워크플로가 필요로 할 때 서버가 생성하는 자체 세션 스레드에서 작업합니다. 실행의 스레드는 다른 하위 스레드와 마찬가지로 나열하고, 읽고, 스트리밍할 수 있으며, 기본 스트림에서 해당 스레드의 도구 호출에 응답할 수 있습니다. 스레드를 중지하려면 에이전트에게 실행을 중지하도록 요청하세요(실행이 열려 있는 세션 중단하기 참조). ID로 스레드를 중지하거나, 실행이 열려 있는 동안 스레드를 보관할 수는 없습니다.

  • 그룹화: 실행의 스레드에는 실행의 workflow_run_id가 있으며, 해당 스레드를 알리는 session.thread_created 이벤트에도 있습니다. 다른 스레드와 그 스레드를 알리는 session.thread_created 이벤트에서는 workflow_run_id가 null로 설정됩니다.
  • 에이전트: agent는 스레드가 실행하는 에이전트를 보여 줍니다. multiagent.workflows.predefined_agents에 나열한 에이전트의 경우, 나열한 서브에이전트의 스레드와 마찬가지로 agent에 해당 에이전트의 id와 version이 있습니다. 워크플로가 정의하는 에이전트(인라인 에이전트)의 경우, agent의 type은 inline이며 id나 version이 없습니다. 세션 에이전트의 시스템 프롬프트가 아니라 워크플로가 작성한 시스템 프롬프트를 가집니다. 또한 워크플로가 부여한 이름과 설명을 가지며, 워크플로가 이름을 부여하지 않으면 서버가 이름을 할당합니다. 세션이 실행하는 에이전트인 세션 에이전트의 모델을 사용합니다. 도구, MCP 서버, 스킬은 세션 에이전트가 가진 것의 부분 집합입니다. 모두를 받지만 API가 이를 보장하지는 않습니다. 도구는 권한 정책을 그대로 유지합니다.
  • 스레드가 공유하는 것: 실행의 스레드는 세션의 샌드박스에서 작업하므로 모든 스레드가 동일한 파일을 가지고 작업합니다. 여기에는 세션이 마운트하는 메모리 스토어의 파일도 포함됩니다. 워크플로가 정의하는 에이전트는 세션이 MCP 서버에 대해 확인한 자격 증명으로 MCP 서버를 사용합니다. 각 스레드에는 자체 대화 기록이 있습니다.
  • 이벤트: 실행 스레드의 session.thread_created, session.thread_status_running, session.thread_status_idle, session.thread_status_terminated 이벤트는 기본 스트림으로도 도착합니다(실행 이벤트 참조). 메시지 이벤트는 스레드 자체 스트림에만 남습니다. 스레드 웹훅은 다른 하위 스레드와 마찬가지로 전송됩니다. 스레드 자체 스트림이 기록하는 내용은 세션 스레드 이벤트를 참조하세요.
  • 단계: 스레드가 어느 단계에서 작업하는지 알려 주는 이벤트나 필드는 없으며, 한 실행의 스레드들이 동일한 agent_name을 가질 수 있습니다. 실행의 진행 상황은 단계 이벤트로 추적하고, 스레드는 session_thread_id로 구분하세요.
  • 스레드 제한: 실행의 스레드는 세션의 하위 스레드 제한에서 제외됩니다.
  • 실행 시작: 세션의 기본 스레드에 있는 에이전트만 실행을 시작합니다. 실행의 스레드에서 작업하는 에이전트는 자체 실행을 시작할 수 없으므로 실행은 중첩되지 않습니다.
  • 보관: 서버는 늦어도 실행이 종료될 때까지 각 스레드를 보관합니다. 스레드가 결과를 반환하거나 실행이 해당 스레드를 더 이상 사용하지 않으면 더 일찍 보관할 수도 있습니다. 그 시점에 스레드가 아직 실행 중이거나 클라이언트를 기다리고 있으면 서버가 먼저 스레드를 중지합니다. 보관된 스레드는 terminated 상태로 스레드 목록에 남습니다. 실행의 스레드를 직접 보관할 필요는 없습니다. 실행이 열려 있는 동안 서버가 아직 보관하지 않은 스레드를 보관하도록 요청하면 error.details.error_code: "workflow_run_open"과 함께 400이 반환됩니다.
  • 가시성: 워크플로의 코드는 볼 수 없지만, 이 목록 다음의 팁에서 설명하는 것처럼 에이전트에게 워크플로를 요청할 수 있습니다. 또한 에이전트가 실행을 시작하고 관리하기 위해 수행하는 도구 호출이나, 각 스레드가 워크플로에 반환하는 결과도 볼 수 없습니다.

작업이 완료되는 시점 파악하기

실행이 실행 중인 동안에는 스레드가 하나도 작업하지 않더라도 세션이 running 상태로 유지된다고 예상하세요. 작업 중인 스레드가 없고 스레드가 클라이언트를 기다리면 세션은 requires_action과 함께 idle 상태가 됩니다. 유휴 상태 자체가 작업이 완료되었음을 의미하지는 않습니다. 작업은 다음 두 조건이 모두 참일 때 완료됩니다:

  1. 생성된 것을 확인한 모든 실행에 workflow_run.status_ended가 있습니다.
  2. 그 후 stop_reason이 end_turn인 session.status_idle이 도착하며, 중단과 같은 사용자 자신의 요청으로 인해 발생한 것이 아닙니다. 중단한 후에는 다음 user.message 또는 user.define_outcome 이후에 오는 유휴 상태만 고려하세요.
  • 일시 중지된 실행: 일시 중지된 실행은 세션을 running 상태로 유지하지 않으므로, 실행이 아직 열려 있는 동안 세션이 유휴 상태가 될 수 있습니다. 예를 들어 예산에 도달하면 세션은 budget_reached와 함께 유휴 상태가 됩니다. 실행이 종료될 때까지 작업은 완료되지 않습니다.
  • 다른 실행: 에이전트는 결과를 읽을 때 새 실행을 시작할 수 있으므로 다시 확인하세요.
  • 결과(outcome): 결과(outcome)를 정의한 경우, 실행이 열려 있는 동안에는 실행 중이든 유휴 상태이든 평가가 시작되지 않습니다. 에이전트가 실행의 결과를 읽는 턴에서 평가가 시작될 수 있습니다.
  • retries_exhausted: 에이전트의 턴이 오류로 실패했습니다. 재시도 횟수가 소진되었거나, 결제 실패처럼 재시도할 수 없는 오류입니다. 이 유휴 상태가 올 때 실행이 아직 실행 중일 수 있습니다. 실행이 종료되었고 에이전트가 아직 그 결과를 읽지 않았다면, 서버는 사용자의 입력 없이 새 턴을 시작합니다. 세션이 다시 running 상태가 되므로 다음 유휴 상태를 기다리세요. 세션이 유휴 상태로 유지되면 그 전에 온 session.error를 읽고 원인을 해결하세요. 그런 다음 user.message를 보내거나 각 실행의 result를 직접 읽으세요.

실행 추적하기

이 샘플은 사용자의 메시지부터 에이전트의 답변까지 세션을 추적합니다. 스트림을 열고 메시지를 보냅니다. 이어서 다음을 수행합니다:

  • 각 실행을 추적합니다. workflow_run.created부터 workflow_run.status_ended까지 추적하며, 각 단계가 시작될 때 출력합니다.
  • 사용자 정의 도구 호출에 응답합니다. 각 agent.custom_tool_use가 도착할 때 응답하는데, 세션이 running 상태로 유지되는 동안 실행의 스레드가 클라이언트를 기다릴 수 있기 때문입니다. 에이전트의 도구가 확인을 요청하는 경우, evaluated_permission이 ask인 각 agent.tool_use 또는 agent.mcp_tool_use에 응답하는 분기를 추가하세요. 모든 호출을 허용하는 분기는 always_ask를 항상 허용으로 바꿔 버리므로 샘플에는 이러한 분기가 없습니다.
  • 작업이 완료되면 중지합니다. 즉, 열린 실행이 없고 세션이 end_turn과 함께 유휴 상태가 될 때입니다. 세션이 종료되는 경우에도 중지합니다. requires_action을 제외한 다른 중지 사유(예: budget_reached, retries_exhausted, refusal)로 유휴 상태가 되면 이유를 출력하고 중지하므로, 이러한 경우는 자체 코드에서 처리하세요. 서버가 곧 스스로 새 턴을 시작하려는 경우에도 retries_exhausted에서 중지합니다. requires_action에서, 그리고 실행이 열려 있는 동안의 end_turn에서는 계속 기다립니다.
open_runs: dict[str, str] = {}  # workflow_run_id -> run name
phase_names: dict[tuple[str, str], str] = {}  # (run ID, phase ID) -> phase name

# 먼저 스트림을 연 다음 사용자 메시지를 보냅니다
with client.beta.sessions.events.stream(session_id) as stream:
    client.beta.sessions.events.send(
        session_id,
        events=[
            {
                "type": "user.message",
                "content": [
                    {
                        "type": "text",
                        "text": "Which contracts in /contracts have a change-of-control clause?",
                    },
                ],
            },
        ],
    )

    for event in stream:
        match event.type:
            case "workflow_run.created":
                open_runs[event.workflow_run_id] = event.name
                for phase in event.phases:
                    phase_names[event.workflow_run_id, phase.id] = phase.name
                print(f"Run started: {event.name}")
            case "workflow_run.phase_started":
                phase_id = event.workflow_run_phase_id
                key = (event.workflow_run_id, phase_id)
                print(f"  Phase: {phase_names.get(key, phase_id)}")
            case "workflow_run.status_ended":
                name = open_runs.pop(event.workflow_run_id, event.workflow_run_id)
                print(f"Run ended: {name} ({event.result.type})")
            case "agent.custom_tool_use":
                # 이벤트가 도착하면 응답합니다. 실행의 스레드는 세션이 실행 중인 동안
                # 클라이언트를 기다릴 수 있습니다.
                result = call_tool(event.name, event.input)
                try:
                    client.beta.sessions.events.send(
                        session_id,
                        events=[
                            {
                                "type": "user.custom_tool_result",
                                "custom_tool_use_id": event.id,
                                "content": [{"type": "text", "text": result}],
                            },
                        ],
                    )
                except anthropic.BadRequestError as error:
                    # 서버가 호출의 스레드를 보관한 후 너무 늦게 도착한 결과는
                    # 서버가 거부합니다. 실행을 계속 추적합니다.
                    print(f"  Answer to {event.name} refused: {error.message}")
            case "session.status_idle":
                # 모든 실행이 종료되고 에이전트가 턴을 마치면 완료됩니다
                if not open_runs and event.stop_reason.type == "end_turn":
                    break
                # requires_action이 있는 idle은 클라이언트를 기다리므로 계속 읽습니다.
                # 그 외의 중지 사유인 경우 이를 출력하고 중지합니다.
                if event.stop_reason.type not in ("end_turn", "requires_action"):
                    print(f"Session idle: {event.stop_reason.type}")
                    break
            case "session.status_terminated":
                break

실행이 열려 있는 세션 중단하기

session_thread_id 없이, 또는 기본 스레드의 ID와 함께 user.interrupt를 보내세요. 이는 에이전트의 턴을 중지합니다. 실행은 종료하지 않습니다. 세션의 실행은 일시 중지되거나 계속 실행될 수 있으며, 이벤트가 어느 쪽인지 보여 주지 않을 수 있습니다. 일시 중지된 실행의 수명은 계속 경과하므로 실행이 일시 중지된 동안 timeout_error로 종료될 수 있습니다.

  • 대기 중인 도구 호출: 중단 후에도 실행 스레드의 도구 호출이 여전히 클라이언트를 기다릴 수 있습니다. 각 호출에 응답하세요. 확인을 요청하는 호출을 취소하려면 거부하세요. 사용자 정의 도구 호출을 취소하려면 is_error를 true로 설정하고 content에 이유를 설명하는 텍스트를 넣은 결과를 보내세요. 세션이 requires_action과 함께 idle 상태인 동안에는 user.message가 400을 반환하므로 먼저 호출에 응답하세요.
  • 실행을 중지하려면: 에이전트에게 실행을 중지하도록 요청하는 user.message를 보내세요. 중지된 실행은 result {"type": "stopped"}로 종료됩니다. 세션이 budget_reached와 함께 idle 상태인 동안에는 예산을 늘리거나 제거할 때까지 user.message가 400을 반환합니다. 예산을 늘리거나 제거하면 중단으로도 일시 중지된 경우가 아닌 한 예산으로 인해 일시 중지된 실행도 재개됩니다.
  • 계속하려면: 에이전트에게 실행을 계속하도록 요청하는 user.message를 보내세요. 중단 후에는 실행이 이 메시지를 기다릴 수 있습니다. 세션이 budget_reached와 함께 idle 상태이면 먼저 예산을 늘리거나 제거하세요.
  • 실행 결과: 중단 후에 종료되는 실행도 workflow_run.status_ended를 전송합니다.

실행이 열려 있는 동안

요청실행이 열려 있는 동안수행할 작업
세션 보관 또는 삭제세션의 상태와 관계없이 실행이 열려 있는 동안 400을 반환할 수 있습니다. 오류의 error.details.error_code는 "workflow_run_open"일 수 있습니다. 성공할 수도 있습니다.에이전트에게 실행을 중지하도록 요청하거나, 각 실행이 종료될 때까지 기다리세요. 일시 중지된 실행은 수명이 지나야만 스스로 종료됩니다. 그런 다음 세션이 idle 상태가 되면 요청을 보내세요. 성공한 보관은 열려 있는 각 실행을 {"type": "stopped"}로 종료합니다. 보관 후에는 실행의 workflow_run.status_ended와 아직 열려 있던 단계의 workflow_run.phase_ended가 스트림으로 도착하지 않습니다. 이를 읽으려면 세션의 이벤트를 나열하세요. 삭제가 성공한 후에는 어떤 workflow_run 이벤트도 세션 실행의 종료를 보고하지 않습니다.
실행의 스레드 중 하나 보관실행이 실행 중이든 유휴 상태이든 열려 있는 동안에는, 서버가 이미 스레드를 보관한 경우가 아니면 error.details.error_code: "workflow_run_open"과 함께 400을 반환합니다.아무것도 하지 않아도 됩니다. 서버가 실행의 스레드를 보관합니다.
세션의 agent 업데이트일시 중지된 실행을 포함하여 실행이 하나라도 열려 있는 동안에는 error.details.error_code: "workflow_run_open"과 함께 400을 반환합니다. 기반 에이전트를 업데이트하는 것은 여전히 허용되며, 세션은 자체 사본을 유지합니다. budget과 같은 다른 필드도 함께 보내는 요청은 전체가 거부됩니다.모든 실행에 workflow_run.status_ended가 올 때까지 기다리거나, 에이전트에게 실행을 중지하도록 요청하세요.
실행의 스레드에서 온 도구 호출 또는 도구 확인에 응답허용됩니다. 기본 스트림으로 도착하며, session_thread_id가 해당 스레드를 명시합니다.이벤트가 도착하는 즉시 이벤트의 id를 tool_use_id 또는 custom_tool_use_id로 전달하여 응답하세요. session.status_idle을 기다리지 마세요. 실행의 다른 스레드가 작업하는 동안 세션은 running 상태로 유지될 수 있습니다. 서버가 스레드를 보관한 후에는 해당 스레드의 호출에 대한 도구 결과가 아무런 효과가 없으며 400을 반환할 수 있습니다. 도구 결과가 400을 반환하면 스레드 목록에서 호출의 스레드를 찾으세요. 상태가 terminated이면 결과가 너무 늦게 온 것이므로 버리세요. 서버는 요청의 이벤트 중 하나를 거부하면 요청 전체를 거부하므로, 각 도구 결과는 별도의 요청으로 보내세요. 너무 늦게 온 도구 확인은 200을 반환하지만, 이것이 도구가 실행되었다는 의미는 아닙니다.

재연결 후 실행 상태 재구성하기

세션의 이벤트로부터 각 실행의 상태를 재구성하세요. 스트림은 놓친 이벤트를 다시 재생하지 않습니다. 새 연결은 연결이 열린 후에 발생한 이벤트만 전달합니다. 따라서 과거 이벤트 조회하기에서처럼 이벤트 유형마다 types[] 항목 하나씩을 지정하는 types 필터로 이벤트를 나열하세요. next_page가 null이거나 없을 때까지 각 응답의 next_page를 page로 전달하세요. workflow_run.created, workflow_run.status_running, workflow_run.status_idle, workflow_run.status_ended는 각 실행의 상태를 알려 주지만, 중단 후 일시 중지된 실행은 여전히 실행 중으로 표시될 수 있습니다. workflow_run.phase_started와 workflow_run.phase_ended로 진행 상황을 재구성합니다. 아직 상태 이벤트가 없는 실행은 실제로 실행되기 시작하지 않은 것입니다. 실행을 나열하는 엔드포인트는 없습니다.

예산 및 제한

실행의 모델 요청은 세션의 예산에 포함됩니다. 실행 자체에는 별도의 가격이 없습니다. 실행의 에이전트가 사용하는 토큰은 세션의 다른 토큰과 마찬가지로 각 모델의 요금으로 청구됩니다. 세션의 모든 요금은 Claude Managed Agents 가격을 참조하세요.

  • 한 실행의 사용량: 세션의 스레드를 나열하고, 실행의 workflow_run_id가 있는 스레드의 usage에 있는 토큰 수를 합산하세요. 목록에는 상태가 terminated인 보관된 스레드도 포함되므로 완료된 실행의 스레드도 집계됩니다. next_page가 null이거나 없을 때까지 각 응답의 next_page를 page로 전달하고, usage가 null인 스레드는 건너뛰세요. 대신 스레드의 list_cost를 합산하면 합계에서 세션 런타임이 빠지고, 각 수치가 개별적으로 반올림됩니다.
  • 예산에 도달했을 때: 열려 있는 모든 실행이 일시 중지되고, 세션은 budget_reached와 함께 idle을 보고하거나, 도구 호출도 대기 중이면 requires_action을 보고합니다. 각 스레드는 이미 시작한 모델 요청을 완료하므로, 실행은 작업 중인 스레드마다 요청 하나만큼 예산을 초과할 수 있습니다. 예산을 늘리거나 제거하면 중단으로도 일시 중지된 경우가 아닌 한 예산으로 인해 일시 중지된 실행이 재개됩니다. 세션의 사용량에 정가가 없는 모델이 포함된 경우에는 예산을 제거해야만 재개됩니다. 정가가 없는 모델을 참조하세요.
제한값제한에 도달했을 때
한 실행에서 동시에 작업하는 스레드64하나가 완료될 때까지 실행이 더 이상 스레드를 생성하지 않습니다. API가 이 수치를 보장하지 않으므로 변경될 수 있습니다.
실행의 전체 수명 동안 워크플로가 시작하는 에이전트1,000워크플로가 더 많이 요청하면 서버는 다른 에이전트를 시작하지 않으며, 실행은 thread_limit_error로 종료됩니다. 서버는 실패한 에이전트를 새 스레드에서 다시 실행할 수 있으므로, 실행의 스레드가 1,000개를 넘을 수 있습니다.
실행 수명기본적으로 24시간, 또는 에이전트가 설정한 수명실행이 timeout_error로 종료됩니다. 에이전트가 설정한 수명을 알려 주는 이벤트는 없습니다.
한 세션에서 동시에 열려 있는 실행기본적으로 10서버가 다른 실행의 시작을 거부합니다. 에이전트의 도구 호출은 오류를 받고, 사용자는 error.type이 max_workflow_runs_error인 workflow_run.error를 받습니다. 유휴 상태의 실행도 제한에 포함됩니다.

서버는 실행 또는 단계의 name을 64자로, description을 256자로 줄입니다. 서버에는 여기에 나열되지 않은 워크플로에 대한 다른 제한과 규칙이 있습니다. 표시되는 내용은 서버가 문제를 발견하는 시점에 따라 다릅니다:

발생하는 상황표시되는 내용
에이전트가 실행을 시작할 때 워크플로가 다른 제한 중 하나를 초과한 경우시작이 거부됩니다. workflow_run.error를 받으며, 실행은 생성되지 않습니다.
실행이 나중에 다른 제한 중 하나를 초과한 경우workflow_run.error를 받은 다음, 실행이 unknown_error로 종료될 수 있습니다.
시작 후 서버가 워크플로가 제한 이외의 워크플로 규칙을 위반한다는 것을 발견한 경우workflow_run.error를 받은 다음, 실행이 program_error로 종료될 수 있습니다.

세션은 수명 동안 실행을 몇 개든 시작할 수 있습니다.

속도 제한

실행의 작업은 조직에 이미 적용된 "rate limit"(속도 제한)에 포함됩니다.

항목포함되는 제한수행할 작업
세션, 세션의 스레드, 그리고 그 이벤트를 조회하거나 나열하는 클라이언트의 요청Managed Agents 엔드포인트의 읽기 제한폴링하는 대신 세션의 이벤트 스트림에서 실행을 추적하세요.
실행의 스레드에서 보내는 모델 요청각 스레드가 사용하는 모델에 대한 Messages API 속도 제한(다른 트래픽과 합산)해당 제한에 실행을 위한 여유를 남겨 두거나, 더 높은 제한을 요청하세요.

실행의 스레드 중 하나에서 보낸 모델 요청이 속도 제한에 걸리거나 모델이 과부하 상태이면, 해당 스레드 자체 스트림이 model_rate_limited_error 또는 model_overloaded_error 유형의 session.error를 받을 수 있습니다:

  • retry_status.type이 retrying이면 서버가 요청을 재시도하고 있으며, 스레드는 여전히 작업 중입니다.
  • exhausted이면 스레드가 실패한 것입니다. 워크플로가 그 실패로 인해 실행이 종료되도록 두면, 실행은 원인을 명시하지 않는 program_error로 종료됩니다. 원인을 찾으려면 실패한 스레드의 이벤트를 읽으세요.

서버는 조직의 모든 세션이 분당 수행하는 작업량도 제한합니다. 이 제한에 걸린 스레드는 중지되며, 자체 스트림에 속도 제한을 명시하는 메시지가 담긴 session.error가 전송됩니다. 에이전트에게 계속하도록 요청하기 전에 1분 동안 기다리세요.

실행은 동일한 작업에 대해 둘 이상의 스레드를 생성할 수 있으므로, 에이전트가 호출하는 도구는 두 번 호출해도 안전하도록 만드세요.

Was this page helpful?