AgentTool, SendMessageTool, 다중 에이전트 스폰 — 하나의 Claude 인스턴스가 다른 인스턴스에 작업을 위임하는 방법.
Claude Code의 에이전트 시스템은 하나의 Claude 인스턴스가 다른 인스턴스에 작업을 위임할 수 있게 합니다. 에이전트는 독립적인 대화 컨텍스트와 도구 세트를 가지며, 부모 에이전트와 메시지를 주고받으면서 복잡한 작업을 분할 정복합니다.
AgentTool.ts → SendMessageTool.ts → agentRegistry.ts → forkAgent.ts → worktree.ts
에이전트 시스템은 세 가지 핵심 축으로 구성됩니다:
SendMessageTool을 통한 메시지 교환과 결과 반환에이전트는 세 가지 타입으로 분류되며, 동일한 이름의 에이전트가 여러 소스에서 정의될 경우 우선순위에 따라 하나만 활성화됩니다.
동일한 agentType의 에이전트가 여러 소스에서 정의되면 다음 순서로 우선순위가 적용됩니다:
// 에이전트 우선순위 (높음 → 낮음)
// 뒤의 그룹이 앞의 그룹을 덮어쓰므로 policySettings가 최고 우선순위
// 1. built-in — 시스템 내장, 가장 낮은 우선순위
// 2. plugin — MCP/플러그인 제공
// 3. userSettings — ~/.claude/settings.json
// 4. projectSettings — .claude/settings.json
// 5. flag — CLI 플래그로 전달
// 6. policySettings — MDM/엔터프라이즈 정책, 최고 우선순위
동일한 agentType에서 두 에이전트가 동일한 이름으로 등록되면 우선순위가 높은 소스의 에이전트가 선택됩니다. policySettings에서 정의한 에이전트가 projectSettings 에이전트보다 우선합니다.
Claude Code에는 여섯 개의 빌트인 에이전트가 내장되어 있으며, 각각 특화된 역할을 담당합니다.
background: true가 하드코딩된 유일한 빌트인 에이전트로, 항상 비동기 실행됩니다.// 빌트인 에이전트 정의 예시
const verificationAgent = {
name: 'Verification',
type: 'built-in',
background: true, // 항상 비동기 — 유일한 빌트인
description: '구현 결과 검증 에이전트',
}
에이전트는 동기 또는 비동기로 실행될 수 있습니다. 비동기 실행 여부는 shouldRunAsync 함수가 6가지 조건을 평가하여 결정합니다.
shouldRunAsync 6개 조건background: true 하드코딩: Verification 에이전트처럼 항상 비동기run_in_background: true 파라미터: 명시적 비동기 요청run_in_background인프로세스 팀메이트가 run_in_background: true로 호출되면 즉시 비동기 실행으로 전환됩니다. 이는 동일 프로세스 내에서 Promise 기반 비동기 실행을 사용하며, AgentTool이 즉시 오류를 발생시키지 않고 백그라운드에서 작업을 수행합니다.
포크 에이전트는 부모 대화의 컨텍스트를 복제하여 독립적인 분기를 생성합니다. 이때 핵심 최적화는 바이트 동일 접두사를 통한 프롬프트 캐시 공유입니다.
포크 자식은 부모와 동일한 placeholder 텍스트를 사용합니다. 이 텍스트가 동일하면 Anthropic API의 서버 측 프롬프트 캐시에서 동일한 캐시 접두사를 공유하게 됩니다. 결과적으로 부모가 이미 캐시한 수만 토큰의 시스템 프롬프트와 도구 정의를 자식이 재활용할 수 있어, API 비용과 지연 시간이 크게 절감됩니다.
// 포크 에이전트 — 바이트 동일 접두사로 캐시 공유
// 부모와 자식이 동일한 도구 목록 + 시스템 프롬프트를 사용
// → API 서버에서 동일 캐시 히트
const forkChild = {
systemPrompt: parentSystemPrompt, // 바이트 동일
tools: parentTools, // 바이트 동일
messages: [parentPrefix, ...forkSpecificMessages],
}
Anthropic API의 프롬프트 캐시는 요청의 접두사를 기준으로 캐시 키를 생성합니다. 시스템 프롬프트, 도구 정의, 초기 메시지가 바이트 단위로 동일하면 서버는 이전 요청의 캐시된 KV 상태를 재사용합니다. 포크 에이전트가 도구 목록을 건너뛰거나 순서를 바꾸면 캐시 미스가 발생하여 수만 토큰을 재처리해야 합니다. 이를 방지하기 위해 포크 에이전트는 canUseTool로 런타임에서 도구 사용을 제한하되, 도구 목록 자체는 부모와 동일하게 유지합니다.
에이전트가 파일시스템 변경을 수반하는 작업을 수행할 때, git worktree를 사용하여 메인 작업 디렉토리와 격리된 환경에서 작업합니다.
에이전트가 완료되면 워크트리의 상태에 따라 정리 전략이 달라집니다:
// 워크트리 정리 로직
async function cleanupWorktree(worktreePath: string) {
const hasChanges = await hasTrackedChanges(worktreePath)
if (!hasChanges) {
await removeWorktree(worktreePath) // 워크트리 + 브랜치 삭제
}
// 변경 있으면 보존 — 부모/사용자가 검사
}
SendMessageTool은 실행 중인 에이전트에 메시지를 보내는 도구입니다. 비동기 에이전트의 상태를 조회하거나, 추가 지시를 전달하거나, 결과를 수집하는 데 사용됩니다.
에이전트 정의 파일에는 다음과 같은 프론트매터 필드가 포함됩니다:
name — 에이전트 식별 이름description — 에이전트 역할 설명 (모델이 선택 시 참조)background — 비동기 실행 여부 (true/false)tools — 사용 가능한 도구 목록 또는 필터model — 사용할 모델 지정 (선택)systemPrompt — 커스텀 시스템 프롬프트 (선택)background: true가 하드코딩되어 항상 비동기로 실행됩니다shouldRunAsync는 6가지 조건(background 하드코딩, run_in_background 파라미터, 코디네이터 모드, 팀 컨텍스트, 깊이 제한, 설정 오버라이드)을 평가합니다canUseTool로 런타임에서 도구 접근을 제한합니다SendMessageTool은 비동기 에이전트와의 통신 채널로, 상태 조회, 지시 전달, 결과 수집에 사용됩니다Q1. 동일한 agentType에서 두 에이전트가 충돌할 때 최고 우선순위를 갖는 소스는?
Q2. 포크 자식이 부모와 동일한 placeholder 텍스트를 사용하는 목적은?
Q3. 인프로세스 팀메이트가 run_in_background: true로 호출되면 어떻게 되나요?
run_in_background: true는 shouldRunAsync의 6가지 조건 중 하나로, 인프로세스 팀메이트라도 이 플래그가 설정되면 Promise 기반 비동기 실행으로 전환됩니다.Q4. 에이전트가 git 추적 변경 없이 워크트리 작업을 완료하면 어떻게 정리되나요?
Q5. background: true가 하드코딩된 유일한 빌트인 에이전트는?
background: true가 하드코딩되어 있어 항상 비동기로 실행됩니다. 다른 빌트인 에이전트는 호출 시 조건에 따라 동기/비동기가 결정됩니다.