-
Notifications
You must be signed in to change notification settings - Fork 0
SubAgents
이 페이지는 SubAgent 가 언제 생성되고, 어떻게 식별되고, 어떻게 정리되는지 를 구현 레벨에서 다룬다. README 의 현재 SubAgent 역할 과 SubAgent 는 언제 실제로 뜨나 섹션의 뒷면이다.
모든 역할은 src/coding_agent/async_subagent_manager.py:32-160 의 DEFAULT_ASYNC_SUBAGENTS dict 에 하드코딩되어 있다. 각 역할은 자기 system prompt, 책임 범위, 기본 모델을 가진다.
| 역할 | 책임 |
|---|---|
planner |
PRD 생성, atomic task breakdown, acceptance criteria 정의 |
architect |
System design, 모듈 경계, API/data flow |
frontend |
Web UI/UX 구현 |
mobile |
Mobile UX/flow |
backend |
DB/API/domain logic |
coder |
일반 코드 구현 |
reviewer |
코드/산출물 리뷰, 회귀 탐지, 누락 테스트 지적 |
debugger |
버그 재현, root cause 분석 |
researcher |
외부 정보 탐색, 배경 지식 정리 |
remember |
장기 메모리 후보 파일 선별 + 파일별 계층 추천 + 저장 노트 초안 |
load_async_subagents() (async_subagent_manager.py:235) 가 먼저 ~/.deepagents/config.toml 의 [async_subagents] 섹션을 시도하고, 없으면 위 DEFAULT_ASYNC_SUBAGENTS 를 그대로 사용한다. config 파일은 자동 생성되지 않는다 — 커스터마이즈하고 싶을 때만 사람이 만든다.
LocalAsyncSubagentManager.__init__() (async_subagent_manager.py:337-348) 에서 위 스펙에 host, port, model, transport 를 확장하고, build_async_subagents() 가 DeepAgents 에 넘길 최종 spec 목록을 만든다.
split topology 의 핵심은 앱 시작 시 SubAgent 를 전부 띄우지 않는다는 점이다. 구현은 src/coding_agent/middleware/lazy_async_subagents.py 의 LazyAsyncSubagentsMiddleware 에 있다.
- Main Agent 가 tool call 을 발행
-
LazyAsyncSubagentsMiddleware.wrap_tool_call() / awrap_tool_call()(lazy_async_subagents.py:74-90) 가 모든 tool call 을 가로챔 -
tool_call.get("name") == "start_async_task"이면 (lazy_async_subagents.py:26), 타겟 subagent 이름을 추출 -
self._runtime.ensure_started(subagent_type)호출 (lazy_async_subagents.py:53) -
LocalAsyncSubagentManager가 해당 역할의 SubAgent 서버 프로세스를 spawn → health check 통과까지 대기 - 그 후 원래 tool call 이 실행되어 task 가 그 SubAgent 로 들어감
- 10개 역할을 미리 띄우면 포트 10개 + 프로세스 10개 + 모델 로딩 10번 → 대부분은 안 쓰임
- 질의 하나는 평균 2-3개 역할만 사용
-
MAX_SUBAGENTS(기본 30) 한도 안에서 "지금 이 순간 실제로 쓰이는" 프로세스만 살아있도록 관리
실제 프로세스는 src/coding_agent/async_subagent_server.py:243-253 에서 create_deep_agent(...) 로 조립된다. 차이점:
| 항목 | Main Agent | SubAgent |
|---|---|---|
| 조립 함수 |
finalize_coding_agent() (agent.py:298) |
async_subagent_server.py:243-253 |
| Middleware | 6개 (fallback, LTM, async-only, lazy, lifecycle, completion) | 2개 (fallback, LTM) |
| System prompt | build_system_prompt(...) |
역할별 prompt (DEFAULT_ASYNC_SUBAGENTS) |
| Shell backend | LocalShellBackend(root_dir=<query_workdir>) |
같은 query_workdir 공유 |
| Partial output | 직접 streaming | 파일로 덤프 → WebUI polling |
즉, SubAgent 는 "단독으로 create_deep_agent 로 만든, middleware 가 얇은 에이전트" 이지 Main Agent 의 일부가 아니다. 이 설계가 process isolation 과 crash containment 를 만들어낸다.
SubAgentLifecycleMiddleware 가 다음 상태 전이를 추적한다 (src/coding_agent/middleware/subagent_lifecycle.py):
created → assigned → running → completed
└→ blocked → (alternate role 재시도)
└→ failed
└→ cancelled
→ destroyed
각 전이는 DurableStateStore (state/store.py) 의 subagent_records / subagent_events 테이블에 기록되며, WebUI 는 이 이벤트 스트림을 polling 해서 실시간 timeline 을 그린다.
메타데이터 (WebUI 에 노출):
-
agent_id+ role ordinal (예:architect agent #1) -
task_id,run_id -
endpoint,pid,port,model - lifecycle event summary
Resilience and Models 페이지에서 자세히 다루지만, SubAgent 관련 policy 두 개를 미리 언급한다:
-
subagent_failure(resilience.py:43) — SubAgent 가blocked혹은failed상태가 되면 다른 role 로 alternate 재시도 를 시도한다. 예:architect가 막히면planner로 우회. -
safe_stop(resilience.py:59) — 증거가 부족하거나 위험한 작업 요청이 들어오면 즉시 정지.
-
~/.deepagents/config.toml을 만들고[async_subagents]섹션에 새 역할 정의 - 역할 이름, system prompt, 기본 모델, 필요 시 tools 를 명시
-
LocalAsyncSubagentManager가 재시작 시 이 파일을 읽음 (async_subagent_manager.py:235) - Main Agent 가
start_async_task(subagent_type="<new_role>")를 호출하면 lazy spawn 됨
⚠ 주의: config.toml 에 정의한 역할 이름과 Main Agent prompt 안의 역할 이름이 일치해야 한다. src/coding_agent/async_subagent_manager.py 의 DEFAULT_ASYNC_SUBAGENTS 를 템플릿으로 참고하자.
- 하드 리밋:
max_subagents = 100(agent.py의 loop 설정) - 실사용 리밋: 환경변수
MAX_SUBAGENTS, 기본 30 (.env.example,docker-compose.yml모두30) - 이 한도를 넘기면 Main Agent 의 추가
start_async_task는 대기하거나 거절된다
Danny's Coding AI Agent
- Home
- Architecture
- SubAgents
- Memory and HITL
- Resilience and Models
- Installation
- Testing and Development
- File Reference
External