[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 배열에 실리는 일도 없습니다.
타깃 규칙, 검증 오류, 모든 키는 설정 레퍼런스에 있습니다.