에이전트를 여러 개로 나누는 결정은 성능을 얻는 대신 토큰과 조율 비용을 크게 지불하는 교환입니다. 이 수업에서는 그 교환이 정당화되는 조건, 오케스트레이터와 워커 사이의 컨텍스트 격리 설계, 핸드오프에서 무엇을 넘기고 무엇을 버릴지 정하는 기준, 그리고 멀티 에이전트 고유의 실패 양상을 다룹니다.
언제 나누는 것이 정당한가
멀티 에이전트는 기본 선택지가 아닙니다. Anthropic이 공개한 멀티 에이전트 리서치 시스템 사례는 이 구조가 일반 대화 대비 훨씬 많은 토큰을 소비한다고 밝히고 있습니다. 성능이 그만큼 개선되지 않으면 순손실입니다.
나누는 것이 값을 하는 조건은 두 가지로 정리됩니다.
첫째, 하위 작업이 서로 독립적이고 병렬 탐색이 이득일 때. 여러 출처를 동시에 조사하는 작업처럼, 하위 조사들이 서로의 결과를 필요로 하지 않으면 병렬로 돌린 만큼 시간이 줄어듭니다. 반대로 앞 결과가 있어야 뒤를 정할 수 있는 순차 의존 작업은 나눠도 시간이 줄지 않고 조율 비용만 붙습니다.
둘째, 컨텍스트 격리가 필요할 때. 한 에이전트가 대량의 중간 자료를 읽어야 하는데 그 자료가 최종 판단에는 필요 없는 경우, 그 읽기를 별도 워커에 격리하면 오케스트레이터의 컨텍스트가 깨끗하게 유지됩니다. 워커는 원문을 읽고 요약만 돌려줍니다.
두 조건 중 어느 것도 해당하지 않는데 "역할을 나누면 각자 잘할 것"이라는 이유로 쪼개는 것은 흔한 오해입니다. 역할 분담은 프롬프트로도 됩니다. 프로세스를 나누는 것은 컨텍스트와 병렬성을 위한 결정입니다.
시험에서는 순차 의존이 명백한 작업에 멀티 에이전트를 제안하는 선택지가 오답으로 자주 등장합니다.
오케스트레이터-워커와 컨텍스트 격리
표준 구조는 오케스트레이터-워커입니다. 중앙 에이전트가 작업을 쪼개 워커에게 위임하고 결과를 종합합니다. 이 구조의 설계 핵심은 조율 로직이 아니라 컨텍스트 경계입니다.
각 워커는 자기 작업에 필요한 최소한만 받습니다. 오케스트레이터의 전체 대화 이력을 그대로 넘기면 격리의 이점이 사라지고 토큰만 배로 듭니다.
async def orchestrate(goal):
plan = await call_model(
f"다음 목표를 독립적으로 수행 가능한 하위 조사로 나눠라: {goal}",
model=MODEL,
)
# 워커는 각자 자기 지시문만 받는다. 오케스트레이터 이력은 넘기지 않는다
results = await gather(*[
run_worker(task.instruction, tools=task.tools)
for task in plan.tasks
])
return await call_model(
synthesis_prompt(goal, [r.summary for r in results]), # 요약만 종합
model=MODEL,
)
두 지점을 눈여겨보십시오. 워커에 넘기는 것은 task.instruction 하나이고, 돌려받는 것은 r.summary입니다. 워커가 읽은 원문 전체가 오케스트레이터로 올라오면 격리한 의미가 없습니다.
워커 지시문의 품질이 전체 성능을 좌우합니다. 모호한 지시를 받은 워커들은 서로 겹치는 작업을 하거나 아무도 다루지 않는 구멍을 남깁니다. 지시문에는 범위, 산출물 형식, 하지 말아야 할 것을 명시합니다. 특히 "다른 워커가 담당하는 영역은 다루지 마라"는 경계 표시가 중복 작업을 크게 줄입니다.
핸드오프에서 무엇을 넘기고 무엇을 버리는가
에이전트 사이의 인계는 정보 선택의 문제입니다. 전부 넘기면 컨텍스트가 부풀고, 너무 줄이면 다음 에이전트가 판단 근거를 잃습니다.
실무에서 쓰는 구분은 세 갈래입니다.
- 결론과 근거 요약 — 반드시 넘깁니다. 다음 판단의 입력입니다
- 원문과 중간 산출물 — 넘기지 않고 참조 가능한 저장소에 두고 식별자만 전달합니다. 다음 에이전트가 필요하면 도구로 가져옵니다
- 시행착오 기록 — 대개 버립니다. 다만 같은 실패를 반복할 위험이 있다면 "이미 시도했고 실패한 것" 목록만 짧게 넘깁니다
세 번째가 실무에서 가장 자주 잘못 처리됩니다. 실패한 시도까지 전부 넘기면 컨텍스트의 상당 부분이 쓸모없는 기록으로 차고, 아무것도 넘기지 않으면 다음 에이전트가 같은 벽에 다시 부딪칩니다.
interface Handoff {
conclusion: string; // 결론
evidence: string[]; // 근거 요약
artifactIds: string[]; // 원문은 식별자만
attempted: string[]; // 실패한 접근의 짧은 목록
openQuestions: string[]; // 해결하지 못한 것
}
openQuestions 항목을 두는 이유는 미해결 사항이 조용히 사라지는 것을 막기 위해서입니다. 에이전트가 답을 못 찾았는데 결론만 넘기면, 종합 단계에서 그 공백이 확신에 찬 문장으로 메워질 수 있습니다.
멀티 에이전트 고유의 실패 양상
단일 에이전트에 없던 실패가 새로 생깁니다. 설계 단계에서 대비해야 하는 것들입니다.
중복 작업. 워커들의 범위가 겹치면 같은 자료를 여러 번 읽습니다. 비용이 늘 뿐 아니라 종합 단계에서 같은 근거가 여러 번 등장해 실제보다 강한 합의로 보입니다. 대응은 지시문의 경계 명시와, 종합 단계에서 출처 기준 중복 제거입니다.
부분 실패. 워커 다섯 중 하나가 실패했을 때 무엇을 반환할지 정해야 합니다. 조용히 넷의 결과만 종합하면 사용자는 완전한 조사로 오해합니다. 실패한 범위를 결과에 명시하거나, 필수 워커의 실패는 전체 실패로 올리는 정책이 필요합니다.
비용 폭주. 오케스트레이터가 워커를 동적으로 만들면 그 수가 예측되지 않습니다. 워커 수 상한, 총 토큰 예산, 깊이 제한을 코드 수준에서 겁니다. 프롬프트에 "너무 많이 만들지 마라"고 쓰는 것은 상한이 아닙니다.
책임 소재 불명. 결과가 틀렸을 때 어느 워커의 어느 판단에서 비롯되었는지 추적할 수 없으면 개선이 불가능합니다. 각 워커 실행에 식별자를 부여하고 입력·출력·소비 토큰을 함께 기록해, 종합 결과에서 근거를 역추적할 수 있게 만듭니다.
MAX_WORKERS = 8
if len(plan.tasks) > MAX_WORKERS:
plan.tasks = plan.tasks[:MAX_WORKERS]
log.warning("worker_cap_applied", requested=len(plan.tasks), cap=MAX_WORKERS)
상한을 적용했다는 사실을 로그로 남기는 것이 요점입니다. 조용히 잘라내면 부분 조사 결과가 완전한 것처럼 보고됩니다.
시험 함정
- 순차 의존 작업에 멀티 에이전트를 제안하는 선택지 — 병렬 이득이 없고 조율 비용만 붙습니다.
- 역할 분담을 이유로 프로세스를 나누는 선택지 — 역할은 프롬프트로 되고, 분리는 컨텍스트와 병렬성을 위한 결정입니다.
- 워커에 오케스트레이터의 전체 이력을 넘기는 구성 — 컨텍스트 격리의 이점이 사라집니다.
- 워커가 읽은 원문 전체를 그대로 올려 종합하는 설계 — 요약만 올려야 격리가 성립합니다.
- 부분 실패를 조용히 제외하고 종합하는 정책 — 사용자가 불완전한 결과를 완전한 것으로 읽습니다.
- 워커 수 상한을 프롬프트 문구로 두는 선택지 — 상한은 코드 수준에서 걸어야 보장됩니다.
실습 시나리오
실제 시험과 같은 형식의 시나리오 문제입니다.
시장 조사 에이전트가 단일 구조로 평균 40분이 걸립니다. 조사는 경쟁사별로 나눌 수 있고 각 경쟁사 조사는 서로의 결과를 필요로 하지 않습니다. 팀이 멀티 에이전트 전환을 제안했고 토큰 비용은 약 3배로 추정됩니다. 아키텍트가 전환 여부를 판단하기 위해 가장 먼저 확인해야 할 것은 무엇입니까?
빌드 연습 · 오케스트레이터-워커 경계 설계
약 55분1.분리 타당성 검토
대상 작업이 병렬 이득과 컨텍스트 격리 중 어느 조건에 해당하는지 판정하고, 해당하지 않으면 단일 구조로 남기는 결론을 적습니다.
기대 결과 · 판정 근거가 순차 의존성 유무와 읽어야 할 자료량으로 서술되어 있습니다.
2.워커 지시문 작성
워커 하나의 지시문에 범위, 산출물 형식, 다루지 말아야 할 영역을 명시해 작성합니다.
기대 결과 · 다른 워커와의 경계가 문장으로 표시되어 있습니다.
3.핸드오프 구조 정의
결론, 근거 요약, 산출물 식별자, 실패한 시도, 미해결 질문 다섯 항목을 갖는 인계 형식을 정의합니다.
기대 결과 · 원문이 값이 아니라 식별자로 전달되도록 되어 있습니다.
4.실패 정책 결정
워커 일부가 실패했을 때 부분 결과를 반환할지, 전체 실패로 올릴지 기준을 정합니다.
기대 결과 · 부분 반환을 택한 경우 실패 범위가 결과에 명시되는 방법이 적혀 있습니다.
5.비용 상한 구현
워커 수 상한과 총 토큰 예산을 코드로 걸고, 상한 적용 사실을 로그로 남깁니다.
기대 결과 · 상한 적용이 조용히 일어나지 않고 경고 로그가 기록됩니다.