동적 서브에이전트(Dynamic subagents)로 워크플로우 자동화하기
동적 서브에이전트(Dynamic subagents)로 워크플로우 자동화하기
동적 서브에이전트는 에이전트가 인터프리터 코드 안에서 서브에이전트를 직접 dispatch 하는 기능이에요. 모델이 한 번에 서브에이전트 호출 하나씩 고르게 하는 대신, JavaScript 루프·분기·병렬 배치로 여러 서브에이전트에 작업을 나누고 결과를 합칠 수 있죠. 작업이 독립된 단위 여럿으로 나뉘거나, 여러 관점이 필요하거나, 재귀적 분석이 유리한 경우에 유용해요. 일반 인터프리터 설정에 대해서는 Interpreters 문서를 확인해 보세요.
출처: 공식문서
시작하기 (Quickstart)
동적 서브에이전트에는 두 가지가 필요해요. 하나는 작업을 dispatch 할 서브에이전트들이고, 다른 하나는 모델이 오케스트레이션 코드를 쓰고 실행할 안전하고 가벼운 런타임인 코드 인터프리터예요. Deep Agents에는 QuickJS 기반의 선택형 코드 인터프리터가 포함되어 있어요. QuickJS 미들웨어 패키지를 설치한 뒤, create_deep_agent의 middleware 인자로 인터프리터 미들웨어를 넘기면 됩니다.
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="openai:gpt-5.5",
middleware=[CodeInterpreterMiddleware()],
)
Deep Agents에는 범용 서브에이전트가 기본 내장되어 있어서, 추가 설정 없이도 기본적인 fan-out은 동작해요. 더 특화된 작업이 필요하면 이름·설명·시스템 프롬프트가 있는 커스텀 서브에이전트를 구성하는데, 이 이름과 설명이 에이전트가 어떤 역할을 골라 쓸지를 판단하는 기준이 돼요. 동적 서브에이전트를 트리거하려면 에이전트 프롬프트에 "workflow"라는 단어를 넣어 주세요.
result = await agent.ainvoke({"messages": [{"role": "user", "content": "Run a workflow that reviews every file in src/routes/ and summarizes the top risks."}]})
코딩 에이전트와 함께 쓰기
동적 서브에이전트를 가장 빨리 써 볼 방법은 Deep Agent 기반의 LangChain 터미널 코딩 에이전트인 dcode를 쓰는 거예요. dcode에는 코드 인터프리터가 기본으로 켜져 있어서, 아무것도 연결하지 않아도 동적 서브에이전트가 바로 동작하죠. dcode를 설치합니다.
curl -LsSf https://langch.in/dcode | bash
실행합니다:
dcode
동적 서브에이전트를 트리거하려면 "workflow"를 요청하세요. 에이전트가 작업을 직접 처리하거나 네이티브 task 도구로 fan-out을 관리하는 대신, 내장 task() 전역 함수를 호출하는 오케스트레이션 스크립트를 작성해 코드 인터프리터에서 실행해요. 예를 들어 "Run a workflow to review every file in src/ for SQL injection"처럼 요청하는 거죠. 서브에이전트가 생성되면 dcode가 dynamic subagents 패널에 dispatch 시점별로 단계를 묶어 실시간으로 보여줍니다.
dcode가 가장 빠른 방법이지만, ACP를 통해 선택한 코딩 에이전트에서도 동적 서브에이전트를 쓸 수 있어요(예: Zed).
동작 원리 (How it works)
에이전트에 서브에이전트와 인터프리터 미들웨어가 있으면, 인터프리터가 코드에서 서브에이전트를 dispatch 하는 내장 task() 전역 함수를 노출해요. 여러 독립 단위로 나뉜 작업(디렉토리의 모든 파일 검토, 티켓 배치 분류 등)은 fan-out 루프가 되어, 모델이 도구 호출을 하나씩 고르는 대신 결정적으로 실행돼요. 서브에이전트 오케스트레이션은 Recursive Language Models(RLM) 논문의 접근 방식인 재귀 언어 모델 워크플로우도 지원해요. 작업 셋을 인터프리터 변수에 유지하고, 슬라이스를 골라 task()로 서브에이전트를 호출하고, 결과를 합치는 방식이에요. task()는 다음 입력을 받습니다.
description: 서브에이전트에게 줄 프롬프트subagentType: 실행할 서브에이전트responseSchema(선택): 구조화된 출력
task()는 전체 에이전트 루프를 실행하고 서브에이전트의 결과로 resolve 돼요.
const review = await task({
description: "Review src/auth/login.ts for auth issues. Cite line numbers.",
subagentType: "reviewer",
responseSchema: {
type: "object",
properties: {
issues: {
type: "array",
items: {
type: "object",
properties: {
file: { type: "string" },
line: { type: "number" },
severity: { type: "string" },
description: { type: "string" },
},
},
},
},
},
});
// With responseSchema, the result is already a typed value, so no JSON.parse is needed.
const critical = review.issues.filter((issue) => issue.severity === "high");
responseSchema를 넘기면 결과가 이미 타입이 있는 JavaScript 객체라서 JSON.parse가 필요 없어요. 서브에이전트가 의도적으로 JSON 문자열을 반환한 경우에만 JSON.parse를 호출하세요.
주요 패턴 (Patterns)
에이전트는 작업의 형태에 따라 전략을 고르게 되는데, 이 패턴들은 설정이 아니라 인터프리터 코드를 어떻게 쓰느냐에서 나와요. 그리고 내가 어떤 서브에이전트를 준비했느냐가 에이전트가 할 수 있는 일을 결정하죠. 모든 패턴은 같은 모델을 공유해요. 작업을 JS 변수에 담고, task()로 서브에이전트를 dispatch 하고, 코드에서 결과를 합치는 거예요.
먼저 분류하고 행동하기 (Classify and act)
항목을 먼저 분류한 뒤, 각 항목을 분류에 해당하는 전문화된 서브에이전트가 처리해요. 서로 다른 유형에 서로 다른 전문성이 필요한 혼합 입력을 처리할 때 좋죠. 사용 예: 지원 티켓, 오류 로그, 사용자 피드백 분류 — 유형에 따라 처리 방식이 달라지는 항목 배치.
Fan-out 후 합성하기 (Fan-out and synthesize)
같은 종류의 작업을 많은 항목에 병렬로 dispatch 한 뒤 결과를 합쳐요. 사용 예: 디렉토리 전체 코드 리뷰, 문서 배치 분석, 로그 파일 처리, 여러 서비스에 같은 검사 실행.
적대적 검증 (Adversarial verification)
두 패스 방식이에요. 첫 패스가 결과를 만들고, 두 번째 패스가 각 결과를 독립 검증자에게 보내 일치하는 결과만 남겨요. 속도보다 신뢰도가 중요할 때 false positive를 줄여 줍니다. 사용 예: false positive 비용이 큰 보안 감사, 컴플라이언스 검사, 높은 신뢰도가 필요한 리뷰.
생성 후 필터링 (Generate and filter)
여러 서브에이전트가 같은 문제에 독립적인 해결책을 만들고, 에이전트가 코드에서 비교·채점·필터링해 최선만 남겨요. 사용 예: 아키텍처 제안, 리팩터링 전략, 콘텐츠 변형 — 결정 전에 여러 옵션을 탐색하는 게 더 나은 결과를 만드는 작업.
토너먼트 (Tournament)
변형들을 심판 서브에이전트가 1:1로 비교하고, 승자가 토너먼트식으로 라운드를 거쳐 올라가요. 사용 예: 주관적 기준에서의 최적화, 스타일 선택, 경쟁 구현 간 선택.
끝날 때까지 루프 (Loop until done)
에이전트가 이미 찾은 것과 중복을 제거하며 발견 루프를 돌려, 새로운 결과가 나오지 않을 때까지 반복해요. 작업 범위를 미리 알 수 없을 때 유용하죠. 사용 예: 완전 탐색, 죽은 코드 탐지, 의존성 감사, 고정 개수보다 완전성을 원하는 스윕.
동적 서브에이전트 끄기
서브에이전트 dispatch는 에이전트에 서브에이전트가 있으면 기본적으로 켜져 있어요. 서브에이전트를 일반 task 도구 경로로만 쓰고 싶다면 이 기능을 끌 수 있습니다.
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware
agent = create_deep_agent(
model="openai:gpt-5.5",
subagents=[{"name": "reviewer", "description": "Reviews code", "system_prompt": "Review code."}],
middleware=[CodeInterpreterMiddleware(subagents=False)],
)
여기서 CodeInterpreterMiddleware(subagents=False)로 넘기면 서브에이전트 dispatch를 끄고 기본 task 도구 경로만 남겨요.
더 알아보기 (Learn more)
- 서브에이전트(Subagents) 설정 방법
- Interpreters 일반 인터프리터 설정
- Recursive Language Models 논문