Spring AI Agentic Patterns (Part 4): Subagent Orchestration — 하나의 범용 에이전트 대신, 전문 하위 에이전트에 위임하기

Spring AI Agentic Patterns (Part 4): Subagent Orchestration — 하나의 범용 에이전트 대신, 전문 하위 에이전트에 위임하기
하나의 범용 에이전트가 모든 일을 처리하도록 만드는 대신, 작업별로 특화된 에이전트에 위임하자. 이렇게 하면 각 작업이 자기 전용 컨텍스트 윈도우에서 실행되므로, 성능 저하를 부르는 문맥 혼잡을 줄일 수 있다.
Task 도구는 spring-ai-agent-utils 툴킷의 일부로 제공되며, Claude Code의 서브에이전트 개념에서 영감을 받은, 이식 가능하고 모델 비종속적인 Spring AI 구현이다. 이 도구를 사용하면 특화된 하위 에이전트가 각자 고립된 컨텍스트 창에서 집중된 작업을 수행하고, 부모 에이전트에게는 꼭 필요한 결과만 돌려주는 계층형 에이전트 아키텍처를 만들 수 있다.
또한 Spring AI의 구조는 Claude의 마크다운 기반 포맷에만 머무르지 않는다. A2A 같은 프로토콜과 다른 에이전트 연동 방식으로 확장할 수 있도록 설계되어 있어, 이기종 에이전트 오케스트레이션에도 열려 있다. 이 부분은 다음 글에서 더 자세히 다뤄진다.
이 글은 Spring AI Agentic Patterns 시리즈의 Part 4다. 앞선 글에서는 Agent Skills, AskUserQuestionTool, TodoWriteTool를 살펴봤고, 이번에는 계층형 서브에이전트 구조를 본격적으로 다룬다.
바로 시작하고 싶다면 Getting Started 섹션으로 건너가도 된다.
How It Works
메인 에이전트는 Task 도구를 통해 특화된 서브에이전트에게 일을 위임한다. 이때 각 서브에이전트는 자기만의 고립된 컨텍스트 윈도우에서 동작한다. 그래서 메인 대화의 긴 문맥을 모두 끌고 가지 않아도 되고, 특정 작업에만 집중할 수 있다.
서브에이전트 아키텍처는 크게 세 가지 요소로 구성된다.
1. Main Agent (Orchestrator)
사용자와 직접 상호작용하는 주 에이전트다. 메인 에이전트의 LLM은 Task 도구에 접근할 수 있고, 시작 시점에 채워지는 Agent Registry를 통해 어떤 서브에이전트들이 उपलब्ध한지 알고 있다. 이 레지스트리에는 각 서브에이전트의 이름과 설명이 담기며, 메인 에이전트는 설명 필드를 바탕으로 언제 어떤 서브에이전트에 위임할지 자동으로 판단한다.
2. Agent Configuration Files
서브에이전트는 agents/ 폴더 아래의 마크다운 파일들(예: agent-x.md, agent-y.md)로 정의된다. 각 파일에는 다음 정보가 들어간다.
- 에이전트 이름
- 설명
- 허용 도구 목록
- 선호 모델
- 시스템 프롬프트
이 설정 파일들은 시작 시 Agent Registry와 Task 도구 양쪽에 함께 반영된다.
3. Subagents
실제 작업을 수행하는 별도의 에이전트 인스턴스들이다. 각 서브에이전트는 메인 에이전트와 분리된 컨텍스트 창에서 실행되며, 필요하면 서로 다른 LLM을 쓸 수도 있다. 즉, LLM-X / LLM-Y / LLM-Z처럼 에이전트별로 서로 다른 모델, 서로 다른 시스템 프롬프트, 서로 다른 도구와 스킬을 배정할 수 있고, 이를 통해 작업 난이도에 맞는 멀티 모델 라우팅이 가능해진다.
아래 다이어그램은 전체 실행 흐름을 잘 보여준다.

실행 흐름을 순서대로 정리하면 다음과 같다.
- 애플리케이션 시작 시 Task 도구가 설정된 서브에이전트 참조들을 로딩한다.
- 각 서브에이전트의 이름과 설명을 해석해 Agent Registry를 채운다.
- 사용자가 메인 에이전트에게 복잡한 질문을 보낸다.
- 메인 에이전트의 LLM이 요청을 분석하고, 레지스트리에 등록된 서브에이전트 후보를 검토한다.
- LLM이 위임이 적절하다고 판단하면, 서브에이전트 이름과 작업 설명을 담아 Task 도구를 호출한다.
- Task 도구가 에이전트 설정에 맞는 서브에이전트를 실행한다.
- 서브에이전트는 자기 전용 컨텍스트에서 자율적으로 작업한다.
- 작업 결과는 메인 에이전트로 다시 돌아오지만, 중간 추론 전체가 아니라 핵심 결과만 전달된다.
- 메인 에이전트가 이를 종합해 최종 답변을 사용자에게 반환한다.
각 서브에이전트는 다음 특성을 가진다.
- 전용 컨텍스트 윈도우: 메인 대화와 분리되어 문맥 혼잡을 막는다.
- 맞춤형 시스템 프롬프트: 특정 도메인에 맞는 전문성을 부여할 수 있다.
- 세밀한 도구 권한 제어: 꼭 필요한 기능만 허용하도록 제한할 수 있다.
- 멀티 모델 라우팅: 단순 작업은 저렴한 모델로, 복잡한 분석은 더 강한 모델로 보낼 수 있다.
- 병렬 실행: 여러 서브에이전트를 동시에 돌릴 수 있다.
- 백그라운드 실행: 오래 걸리는 작업을 비동기로 처리할 수 있다.
Built-in Subagents
Spring AI Agent Utils는 TaskTool을 구성하면 기본으로 등록되는 네 가지 내장 서브에이전트를 제공한다.
Explore
빠르고 읽기 전용인 코드베이스 탐색용 에이전트다. 파일 찾기, 코드 검색, 내용 분석 같은 작업에 적합하다.
- 목적: 빠른 탐색, 구조 파악, 검색
- 도구:
Read,Grep,Glob
General-Purpose
다단계 조사와 실행을 처리하는 범용 에이전트다. 읽기/쓰기 권한까지 포함해 비교적 폭넓은 작업을 맡을 수 있다.
- 목적: 조사 + 실행이 섞인 범용 작업
- 도구: 모든 도구
Plan
구현 전략을 설계하고 트레이드오프를 분석하는 소프트웨어 아키텍트 역할의 에이전트다.
- 목적: 구현 계획 수립, 구조 설계, 선택지 비교
- 도구: 읽기 전용 + 검색 계열
Bash
터미널 명령 실행에 특화된 에이전트다. Git 작업, 빌드, 테스트, 쉘 기반 운영 작업에 적합하다.
- 목적: 명령 실행, 빌드, Git, 터미널 작업
- 도구:
Bash전용
더 자세한 기능 차이는 공식 참고 문서에서 확인할 수 있다.
여기서 중요한 포인트는 여러 서브에이전트를 동시에 실행할 수 있다는 점이다. 예를 들어 코드 리뷰 시나리오라면 스타일 체커, 보안 스캐너, 테스트 커버리지 분석기를 병렬로 돌린 뒤 메인 에이전트가 결과를 취합하는 식으로 확장할 수 있다.
Getting Started
1. 의존성 추가
먼저 spring-ai-agent-utils 의존성을 추가한다.
1<dependency>2 <groupId>org.springaicommunity</groupId>3 <artifactId>spring-ai-agent-utils</artifactId>4 <version>0.4.2</version>5</dependency>6
2. 메인 에이전트에 Task 도구 연결
이제 메인 에이전트가 Task 도구를 사용할 수 있게 구성한다.
1import org.springaicommunity.agent.tools.task.TaskToolCallbackProvider;23@Configuration4public class AgentConfig {56 @Bean7 CommandLineRunner demo(ChatClient.Builder chatClientBuilder) {8 return args -> {9 // Configure Task tools10 var taskTools = TaskToolCallbackProvider.builder()11 .chatClientBuilder("default", chatClientBuilder)12 .subagentReferences(13 ClaudeSubagentReferences.fromRootDirectory("src/main/resources/agents"))14 .build();1516 // Build main chat client with Task tools17 ChatClient chatClient = chatClientBuilder18 .defaultToolCallbacks(taskTools)19 .build();2021 // Use naturally - agent will delegate to subagents22 String response = chatClient23 .prompt("Explore the authentication module and explain how it works")24 .call()25 .content();26 };27 }28}29
핵심은 메인 에이전트가 별도로 위임 규칙을 하드코딩하지 않아도, 서브에이전트의 description 필드를 바탕으로 스스로 위임 시점을 판단한다는 것이다.
3. 멀티 모델 라우팅 적용(선택)
작업 복잡도에 따라 서브에이전트를 서로 다른 모델로 보내고 싶다면 다음처럼 구성할 수 있다.
1var taskTools = TaskToolCallbackProvider.builder()2 .chatClientBuilder("default", sonnetBuilder) // Default model3 .chatClientBuilder("haiku", haikuBuilder) // Fast, cheap4 .chatClientBuilder("opus", opusBuilder) // Complex analysis5 .build();6
이후 각 서브에이전트 정의 파일에 선호 모델을 지정하면, Task 도구가 그 설정을 읽어 적절한 모델로 라우팅한다.
Creating Custom Subagents
사용자 정의 서브에이전트는 보통 .claude/agents/ 아래에 두는 YAML frontmatter + 마크다운 본문 파일로 작성한다.
1project-root/2├── .claude/3│ └── agents/4│ ├── code-reviewer.md5│ └── test-runner.md6
Subagent File Format
아래는 서브에이전트 정의 파일의 예시다.
1---2name: code-reviewer3description: Expert code reviewer. Use proactively after writing code.4tools: Read, Grep, Glob5disallowedTools: Edit, Write6model: sonnet7---89You are a senior code reviewer with expertise in software quality.1011**When Invoked:**121. Run `git diff` to see recent changes132. Focus analysis on modified files143. Check surrounding code context1516**Review Checklist:**17- Code clarity and readability18- Proper naming conventions19- Error handling20- Security vulnerabilities2122**Output:** Clear, actionable feedback with file references.23
이 포맷이 실용적인 이유는 코드 안에 에이전트 역할을 하드코딩하지 않고, 운영 가능한 설정 파일 형태로 분리할 수 있기 때문이다. 역할 수정, 모델 변경, 도구 권한 축소가 모두 설정 파일 수준에서 가능해진다.
Configuration Fields
정의 파일의 핵심 필드는 다음과 같다.
name(필수): 고유 식별자. 보통 소문자와 하이픈을 사용한다.description(필수): 언제 이 서브에이전트를 사용해야 하는지 설명하는 자연어 문장이다.tools(선택): 허용할 도구 이름 목록이다. 생략하면 상위 기본값을 물려받을 수 있다.disallowedTools(선택): 명시적으로 금지할 도구 목록이다.model(선택):haiku,sonnet,opus처럼 선호 모델을 지정한다.
더 많은 필드(skills, permissionMode 등)는 참고 문서에서 확인할 수 있다.
중요한 제약도 하나 있다.
서브에이전트는 다시 자기 하위 서브에이전트를 생성할 수 없다. 따라서 서브에이전트의 tools 목록에는
Task를 넣지 말아야 한다.
이 제약은 불필요한 다단계 재귀 위임과 통제 불가능한 계층 폭증을 막는 데 중요하다.
Loading Custom Subagents
사용자 정의 서브에이전트를 로드하는 구성은 다음과 같다.
1import org.springaicommunity.agent.tools.task.subagent.claude.ClaudeSubagentReferences;23var taskTools = TaskToolCallbackProvider.builder()4 .chatClientBuilder("default", chatClientBuilder)5 .subagentReferences(6 ClaudeSubagentReferences.fromRootDirectory("src/main/resources/agents")7 )8 .build();9
즉, 서브에이전트는 애플리케이션 시작 시점에 디렉터리에서 읽혀 레지스트리와 Task 도구에 함께 등록된다.
Background Execution
오래 걸리는 서브에이전트는 비동기 방식으로 백그라운드 실행할 수 있다. 이 경우 메인 에이전트는 서브에이전트가 끝날 때까지 모든 흐름을 블로킹하지 않고, 자기 일을 계속 진행할 수 있다. 필요할 때는 TaskOutputTool을 사용해 결과를 가져오면 된다.
여러 인스턴스에 걸친 영속 저장이 필요하다면 TaskRepository 문서를 함께 확인하는 편이 좋다.
왜 이 패턴이 실무에서 중요한가
Subagent Orchestration의 핵심 가치는 단순히 “에이전트를 여러 개 띄울 수 있다”가 아니다. 실제로는 다음 세 가지를 동시에 해결한다.
1. 컨텍스트 오염 감소
하나의 메인 에이전트가 조사, 계획, 파일 수정, 테스트 실행, 최종 응답 작성을 전부 떠안으면 문맥이 빠르게 오염된다. 반대로 역할별 서브에이전트로 분리하면 필요한 정보만 각 창에 들어가므로 품질이 안정된다.
2. 권한 최소화
탐색 전용 에이전트에는 읽기 도구만 주고, 터미널 전용 에이전트에는 Bash만 주는 식으로 권한을 세밀하게 제한할 수 있다. 이는 안전성과 운영 통제 면에서 매우 유리하다.
3. 비용 대비 성능 최적화
모든 작업에 최고 성능 모델을 붙일 필요는 없다. 검색이나 코드 탐색은 빠르고 저렴한 모델에 맡기고, 설계 분석 같은 고난도 작업만 더 강한 모델로 보내는 식으로 구성할 수 있다.
즉, 서브에이전트는 단순한 역할 분리 단위이면서 동시에 성능, 비용, 보안, 유지보수성을 함께 조절하는 운영 단위가 된다.
Conclusion
Task 도구는 Spring AI에 계층형 서브에이전트 아키텍처를 가져온다. 이를 통해 컨텍스트를 분리하고, 전문화된 지시를 부여하고, 멀티 모델 라우팅을 효율적으로 적용할 수 있다. 복잡한 일을 집중된 하위 에이전트에게 위임하면, 메인 에이전트는 훨씬 가볍고 반응성 좋은 상태를 유지할 수 있다.
다음 Part 5에서는 A2A Integration을 다룬다. 여기서는 한 애플리케이션 내부의 서브에이전트 구조를 넘어, Agent2Agent 프로토콜을 통해 상호운용 가능한 에이전트를 구축하는 방향으로 논의가 확장된다.
또 후속 글에서는 A2A, MCP, 커스텀 프로토콜을 아우르는 Subagent Extension Framework도 예고되어 있다.
Resources
- GitHub Repository: spring-ai-agent-utils
- TaskTools Documentation: TaskTools.md
- Example Project: subagent-demo
- Claude Code Subagents — 원래 영감을 준 구현
Series Links
- Part 1: Agent Skills 한국어 정리
- Part 2: AskUserQuestionTool 한국어 정리
- Part 3: TodoWriteTool 한국어 정리
- Part 4: Subagent Orchestration (원문)
- Part 5: A2A Integration 한국어 정리
- Dynamic Tool Discovery 한국어 정리
- Tool Argument Augmentation 한국어 정리