Skip to content

서브에이전트 라우팅

위임된 작업 — Task 서브에이전트, 훅 에이전트, 워크플로 서브에이전트 — 을 고정된 맵이나 판정 모델로 부모 세션과 다른 타깃에 보냅니다.

Updated View as Markdown

[models.subagents] 오버레이는 부모 세션이 자기 목적지를 그대로 유지하는 동안 위임된 작업을 다른 타깃으로 보냅니다. 그 id를 요청하는 Task 서브에이전트, 훅 에이전트, 워크플로 서브에이전트는 오버레이의 타깃으로 우회됩니다. 부모 세션 자신의 턴은 이 테이블을 전혀 보지 않으며, 오버레이가 없을 때와 똑같이 항목을 해석합니다.

오버레이는 어떤 [[models]] 항목에도 붙일 수 있습니다 — [models.upstream_model] 맵을 가진 항목, [models.router] 테이블을 가진 항목, 맵이 없어 [[routes]]로 해석되는 id 모두입니다. 이 테이블이 라우터 테이블 안이 아니라 항목 위에 놓이는 것은 고정 항목에는 라우터 테이블이 없고, Switchyard의 “subagents를 곁들인 passthrough”가 여기서는 바로 그 고정 항목이기 때문입니다.

형태는 둘입니다.

type 자식의 타깃이 정해지는 곳
passthrough 에이전트 타입에 맞는 by_type, 없으면 target — 판정 모델도 상태도 없음
llm_classifier 위임된 작업을 읽는 판정 모델, (세션, 에이전트)마다 한 번

무엇이 위임된 작업인가

업스트림은 자기 claude_lineage 헤더 하나에만 기댑니다. shunt는 x-claude-code-request-class를 먼저 읽고, 그 클래스가 전송되지 않을 때만 에이전트 id로 폴백합니다.

요청 오버레이를 타는가?
x-claude-code-request-class: subagent 또는 workflow 예
x-claude-code-request-class: main, 에이전트 id가 함께 실려 와도 아니오 — 메인 트래픽
x-claude-code-request-class: compaction 또는 auxiliary 아니오 — 하네스 유지보수
클래스 헤더 없음, 비어 있지 않은 x-claude-code-agent-id 예

Claude Code는 힌트 게이트와 무관하게 모든 위임 턴에 x-claude-code-agent-id를 보냅니다. 클래스와 에이전트 타입 헤더는 게이트를 받습니다. shunt 뒤에서는 클라이언트가 CLAUDE_CODE_GATEWAY_HINT_HEADERS=1을 설정했을 때만 전송됩니다 (클라이언트 환경 변수 참고). 따라서 기본 배포에서는 모든 Task 자식이 target으로 가며, by_type을 쓰려면 이 변수가 필요합니다.

passthrough 형태

[[models]]
id = "claude-opus-4-8"

[models.upstream_model]
anthropic = "claude-opus-4-8"

[models.subagents]
type = "passthrough"
target = "claude-haiku-4-5"
by_type = { Explore = "claude-haiku-4-5", fork = "claude-sonnet-4-6", teammate = "claude-sonnet-4-6" }

위임 턴은 자신의 x-claude-code-agent-type과 맞는 by_type 항목으로 가고, by_type이 그 에이전트 타입에 아무것도 지정하지 않았으면 target으로 갑니다. 에이전트 타입 헤더가 전송되지 않으면 모든 위임 턴이 target으로 갑니다.

by_type 키는 헤더의 값 그대로이며 대소문자까지 포함해 정확히 매칭됩니다. 내장 에이전트의 id는 그대로 전달됩니다 — Explore, Plan, general-purpose, claude, 그리고 클라이언트가 CLAUDE_CODE_FORK_SUBAGENT=1에서만 제공하는 fork입니다. .claude/agents/의 프로젝트 에이전트는 custom으로 도착하며 자기 이름은 전송되지 않으므로, custom이 그 에이전트가 매칭할 수 있는 유일한 키입니다. teammate는 Agent Teams 멤버를 가리키는 클라이언트의 리터럴이며 아직 와이어에서 관측된 적은 없습니다.

상태 없음. 타깃은 오직 설정과 요청 헤더만의 함수입니다 — 세션 핀도, 저장소도, 판정 호출도 없습니다. 라우터가 달린 id에서는 라우터가 돌기 전에 자식이 우회되므로, 자식의 턴은 트랜스크립트를 상대로 채점되지도, 고정되지도 않고, 부모의 핀이나 dwell에 닿거나 그것을 진행시키지도 않습니다.

llm_classifier 형태

고정된 타깃 대신, 판정 모델이 위임된 작업을 읽고 그 일을 맡을 그룹의 이름을 댑니다. 여기에는 mode = "custom"만 있고, 키는 LLM 분류기 페이지에서 설명한 custom 모드의 것입니다.

[models.subagents]
type = "llm_classifier"
mode = "custom"
models = { judge = ["claude-haiku-4-5"], capable = ["claude-opus-4-8"], efficient = ["claude-sonnet-4-6"], any = ["claude-sonnet-4-6", "claude-opus-4-8"] }
default_target = "efficient"
classify_trigger = "new_session"
max_output_tokens = 64
prompt = """
위임된 작업에 맞는 타깃을 정확히 하나 고르세요.

- 코드 리뷰, 비평, 감사, 정확성 분석에는 "capable"을 고르세요.
- 구현, 조사, 설명, 그 밖의 위임 작업에는 "efficient"를 고르세요.

응답 스키마에 맞는 JSON만 반환하세요.
"""
response_schema = '''
{"type": "object",
 "properties": {"target": {"type": "string", "enum": ["capable", "efficient"]}},
 "required": ["target"],
 "additionalProperties": false}
'''
policy = { type = "target_selector", selector = "/target" }

[models.router] 형태와 다른 규칙이 셋 있습니다.

  • classify_trigger의 기본값이 new_session이고, user_turn은 거부됩니다. 위임된 자식은 하나의 작업이므로 타깃을 한 번 골라 끝까지 유지합니다. 사용자 턴마다 다시 판정하면 바뀔 수 없는 결정에 판정 호출을 계속 쓰게 됩니다.
  • message_hash_fallback은 false여야 합니다. 분류는 이미 (세션, 에이전트)로 키를 잡고 있으므로, 대신 첫 메시지를 해시하면 한 세션의 서로 다른 자식 둘이 같은 판정에 묶입니다.
  • 부모는 분류되지 않습니다. 무엇이 위임된 작업인지는 passthrough 형태와 정확히 같습니다. 그래서 부모의 턴, 에이전트 id가 붙은 main 턴, compaction과 auxiliary 클래스는 모두 이 테이블이 없는 것처럼 항목을 해석하고 판정 호출도 하지 않습니다.

여섯 개 호출당 한도는 호출을 만드는 주체인 이 테이블에 놓습니다. 판정 모델은 다른 곳과 같은 규칙을 지킵니다 — 한 홉, passthrough 라우트 금지, 그리고 호출자의 자격 증명 슬롯은 하나도 함께 가지 않습니다 (판정 호출 참고). 위임된 턴이 판정 모델을 부를 수 있으므로 그런 턴의 인바운드 인증은 오버레이의 타깃과 판정 모델까지 대상으로 삼습니다 — 인증하지 못한 위임 턴은 판정 호출을 한 번도 만들지 않고 거부됩니다. count_tokens 프로브도 마찬가지로 판정 호출을 하지 않습니다. 그 자식의 (세션, 에이전트) 배정이 있으면 그 배정으로, 없으면 default_target 그룹의 첫 모델로 응답합니다.

클라이언트가 보는 것

우회된 턴은 x-gateway-routed-model(타깃)과 x-gateway-route-source를 실어 보내며 — by_type이 맞으면 subagent_type, target 폴백이면 subagent — algorithm = "subagents"로 shunt.router.decisions에 집계됩니다. llm_classifier 형태는 그 대신 classifier 자신의 소스를 보고합니다. 판정 모델이 분류한 턴은 llm-classifier, 그 자식의 이후 턴이 배정을 재생하면 classifier_retained, 판정이 턴을 결정하지 못했으면 classifier_fail_open입니다(LLM Classifier 참고).

요청이 없는 표면은 아무것도 해석하지 않습니다. /v1/models 디스커버리와 shunt check는 모델별 목적지 정보를 전혀 싣지 않고, GET /routes는 부모 자신의 [[routes]]/[models.router] 항목이 있을 때만 그것을 보여 줍니다(server.default_provider에 맡겨진 id는 어느 배열에도 나오지 않습니다). 셋 중 무엇도 우회된 타깃을 해석하지 않으며, 오버레이 자체가 routers 배열에 실리는 일도 없습니다.

타깃 규칙, 검증 오류, 모든 키는 설정 레퍼런스에 있습니다.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close