통신과 핸드오프
메시지 전달 방식, 공유 상태 vs 메시지 패싱, 핸드오프 프로토콜, 컨텍스트 압축
멀티에이전트 시스템의 품질은 개별 agent의 지능보다 handoff 품질에 더 크게 좌우됩니다. 좋은 handoff는 대화를 길게 넘기는 것이 아니라, 다음 담당자가 바로 일할 수 있는 작업 패킷을 만드는 일입니다.
통신 방식 선택
| 방식 | 적합한 상황 | 장점 | 주의점 |
|---|---|---|---|
| 공유 상태(shared state) | 한 workflow 안에서 단계가 명확할 때 | 구현이 단순하고 추적 쉬움 | 결합도가 높아짐 |
| 메시지 패싱(message passing) | agent 경계가 명확할 때 | 책임 분리와 비동기 처리에 강함 | payload 설계가 중요 |
| 이벤트 기반(event-driven) | 장기 실행, 외부 시스템 연동 | 확장성과 재개에 유리 | 순서 보장과 idempotency 필요 |
실전에서는 "workflow 내부는 shared state, agent 간은 message/event"처럼 혼합하는 경우가 많습니다.
이벤트 드리븐 에이전트 패턴: MCP 서버 푸시
2026년 3월 기준으로 MCP 서버가 에이전트에 이벤트를 푸시하는 패턴이 등장하고 있습니다.
대표적으로 Claude Code의 --channels 플래그는 MCP 서버가 발행하는 이벤트를 에이전트가 구독하여 반응하는 구조를 지원합니다.
이 패턴의 핵심은 agent가 polling하지 않고, MCP 서버의 상태 변경을 구독 기반으로 수신한다는 점입니다. 파일 변경 감지, CI 파이프라인 완료 알림, 외부 서비스 웹훅 등을 agent workflow에 자연스럽게 연결할 수 있습니다.
| 장점 | 주의점 |
|---|---|
| 불필요한 polling 제거 | 이벤트 순서 보장과 중복 처리 설계 필요 |
| 외부 시스템 변경에 즉시 반응 | 채널 구독 범위와 권한 관리 |
| 멀티에이전트 간 실시간 조율 | MCP Streamable HTTP 전송 의존 |
대규모 이벤트 스트리밍 아키텍처
위의 이벤트 드리븐 패턴은 MCP 서버 푸시처럼 단일 채널 수준의 통신에 적합합니다. 하지만 수십~수백 개의 worker agent가 분산 환경에서 비동기로 협력해야 하는 대규모 시스템에서는 더 견고한 이벤트 스트리밍 인프라가 필요합니다.
Kafka/Pub-Sub 기반 에이전트 오케스트레이션
메시지 브로커(Kafka, Pub/Sub, NATS 등)를 에이전트 간 통신 백본으로 사용하면 다음 이점을 얻습니다.
| 이점 | 설명 |
|---|---|
| 시간 디커플링 | producer와 consumer가 동시에 online일 필요 없음 |
| 부하 분산 | consumer group으로 worker agent를 수평 확장 |
| 재처리 | 오프셋 기반으로 실패 구간을 되감아 재실행 |
| 감사 추적 | 이벤트 로그 자체가 불변 감사 기록 |
Saga 패턴: 멀티에이전트 트랜잭션
여러 worker agent가 순차적으로 side effect를 일으키는 작업에서는 중간 실패 시 이전 단계를 **보상(compensation)**해야 합니다. Saga 패턴은 각 단계에 보상 액션을 정의하여, 분산 트랜잭션 없이도 일관성을 유지합니다.
| 단계 | 정방향 액션 | 보상 액션 |
|---|---|---|
| 1. 주문 생성 | create_order | cancel_order |
| 2. 결제 처리 | charge_payment | refund_payment |
| 3. 재고 차감 | reserve_inventory | release_inventory |
| 4. 배송 요청 | request_shipping | cancel_shipping |
type SagaStep = {
name: string
execute: () => Promise<void>
compensate: () => Promise<void>
}
async function executeSaga(steps: SagaStep[]) {
const completed: SagaStep[] = []
for (const step of steps) {
try {
await step.execute()
completed.push(step)
} catch (error) {
// 실패 시 완료된 단계를 역순으로 보상
for (const done of completed.reverse()) {
await done.compensate()
}
throw new SagaFailedError(step.name, error)
}
}
}메시지 순서 보장
에이전트 간 이벤트 순서가 뒤바뀌면 상태 불일치가 발생합니다.
| 전략 | 동작 방식 | 적합한 상황 |
|---|---|---|
| Partition key | 같은 key의 메시지는 같은 파티션에 순서대로 저장 | 동일 작업(run_id)의 이벤트 순서 보장 |
| Sequence number | 메시지에 순번을 붙여 consumer가 순서 검증 | 엄격한 순서가 필요한 파이프라인 |
| Causal ordering | 인과 관계가 있는 이벤트만 순서 보장 | 독립 이벤트는 병렬, 의존 이벤트만 직렬 |
partition key 전략 예시: run_id를 partition key로 사용하면 같은 실행의 모든 이벤트가 순서대로 처리됩니다. 서로 다른 run은 독립적으로 병렬 처리되어 처리량이 확보됩니다.
Dead Letter Queue (DLQ)
worker agent가 처리에 실패한 이벤트를 무한 재시도하면 전체 파이프라인이 막힙니다. DLQ는 실패한 이벤트를 별도 큐로 격리하여 정상 흐름을 보호합니다.
| DLQ 설계 항목 | 권장 |
|---|---|
| 격리 조건 | 재시도 횟수 초과(보통 3~5회) 또는 poison message 감지 |
| 메타데이터 보존 | 원본 이벤트, 실패 사유, 재시도 횟수, 마지막 실패 시각 |
| 재처리 방식 | 수동 검토 후 원래 토픽에 재발행, 또는 보상 액션 실행 |
| 알림 | DLQ 적재 시 운영팀에 즉시 알림 |
| 보존 기간 | 최소 7일 이상 보존하여 사후 분석 가능하게 유지 |
type DeadLetterRecord = {
originalTopic: string
originalEvent: AgentEvent
failureReason: string
retryCount: number
lastFailedAt: string // ISO 8601
agentId: string
runId: string
}이벤트 스트리밍 도입 판단 기준
| 질문 | Yes면 도입 검토 |
|---|---|
| worker agent가 10개 이상이고 독립적으로 확장해야 하는가 | 예 |
| 실패한 이벤트를 나중에 재처리해야 하는가 | 예 |
| 에이전트 간 통신 순서가 중요한가 | 예 |
| 감사 추적을 위해 모든 이벤트를 불변 로그로 남겨야 하는가 | 예 |
| 단순 MCP 서버 푸시나 HTTP 콜백으로 충분한가 | 아니오면 검토 |
복잡도 주의
이벤트 스트리밍 인프라는 강력하지만 운영 부담도 큽니다. 브로커 운영, 스키마 관리, 컨슈머 그룹 모니터링, DLQ 처리 프로세스가 모두 필요합니다. worker agent가 5개 미만이고 실패 빈도가 낮다면 직접 호출이나 MCP 서버 푸시로 시작하는 편이 낫습니다.
handoff 패킷의 최소 구성
handoff에는 최소한 아래 정보가 들어가야 합니다.
| 필드 | 설명 |
|---|---|
| goal | 다음 agent가 달성해야 할 목표 |
| context summary | 꼭 필요한 배경 정보 요약 |
| constraints | 정책, 금지 사항, SLA |
| evidence | 근거가 되는 문서나 데이터 |
| output contract | 기대하는 응답 형식 |
| return channel | 결과를 어디로 돌려줄지 |
예시: handoff envelope
{
"goal": "환불 가능 여부를 판단하고 초안 응답을 작성한다",
"contextSummary": "고객은 결제 후 3일 이내 환불 요청, 사용량은 0건",
"constraints": ["환불 확정 금지", "정책 링크 필수"],
"evidence": [
{ "source": "refund-policy-v3", "excerpt": "미사용 건은 7일 내 환불 가능" }
],
"outputContract": {
"fields": ["classification", "answerDraft", "confidence", "needsHumanReview"]
}
}컨텍스트 압축 전략
1. 전체 대화 전달을 기본값으로 두지 않는다
길어진 대화를 그대로 넘기면 token 비용만 늘고, downstream agent가 핵심 신호를 잃기 쉽습니다.
2. 요약은 "사실, 가설, 미해결 질문"으로 나눈다
| 구분 | 예시 |
|---|---|
| 사실 | 주문 상태, 정책 버전, 외부 API 응답 |
| 가설 | 고객이 환불 대신 크레딧을 원할 가능성 |
| 미해결 질문 | 실제 사용량 집계가 최신인지 |
이 세 칸으로 요약하면 downstream agent가 무엇을 믿어도 되는지 빠르게 파악할 수 있습니다.
3. 근거는 별도 배열로 유지한다
자연어 요약 안에 근거가 섞여 있으면 재검증이 어려워집니다. 요약과 evidence를 분리하면 evaluator나 human reviewer가 빠르게 판정할 수 있습니다.
통신 구조 예시
이 구조에서 핵심은 각 agent가 전체 대화를 아는 것이 아니라, 다음 결정을 위해 필요한 정보만 받는 것입니다.
shared state와 message passing의 경계
| shared state에 두기 좋은 것 | message로 넘기기 좋은 것 |
|---|---|
| run id, 현재 단계, checkpoint | 특정 agent가 수행해야 할 작업 |
| 승인 상태, retry count | 목표, 제약, 근거 요약 |
| audit log 참조 키 | 결과 payload |
state에는 재실행과 관측성에 필요한 최소 정보만 두고, 실제 작업 의미는 message 안에 담는 편이 명확합니다.
Human Handoff 설계
agent가 사람에게 작업을 넘기는 것도 handoff의 한 형태입니다. 자동화 경계를 넘을 때 언제, 어떤 정보를 담아 사람에게 넘길지 미리 설계해야 합니다.
Human Gate 조건
다음 조건 중 하나라도 해당하면 human gate를 두는 편이 좋습니다.
- 외부 발송, 결제, 권한 변경처럼 되돌리기 어려운 작업
- 품질보다 책임 소재가 중요한 작업
- 모델 confidence만으로는 정당화할 수 없는 작업
- 법률, HR, 재무처럼 규제 리스크가 큰 작업
- 복구 전략(retry, fallback)이 모두 소진된 고위험 상황
Human Approval Queue 구성
사람에게 넘긴다고 문제가 해결되는 것은 아닙니다. handoff payload가 빈약하면 운영자는 더 느리게 판단합니다.
사람 승인 큐에는 최소한 다음이 필요합니다.
| 항목 | 설명 |
|---|---|
| 요약된 현재 상황 | 사람이 전체 맥락을 다시 읽지 않아도 되도록 |
| 자동화 중단 이유 | 왜 agent가 직접 처리하지 못했는지 |
| 추천 액션 | agent가 판단한 최선의 다음 행동 |
| 근거 링크 | 판단에 사용된 정책, 데이터, 외부 응답 |
| 상태 전이 정의 | 승인/거절 시 workflow가 어디로 이동하는지 |
early handoff를 설계하라
human handoff를 마지막 수단으로만 생각하면 고위험 작업에서 이미 늦습니다. 위험도 기반으로 early handoff 지점을 미리 정의하고, 해당 지점에서는 자동화를 시도하기 전에 사람 승인을 먼저 받는 구조가 더 안전합니다.
실패 모드
| 실패 모드 | 원인 | 대응 |
|---|---|---|
| handoff 후 품질 하락 | 목표와 출력 형식이 모호함 | output contract 강화 |
| 같은 작업 중복 실행 | return channel과 dedupe key 부재 | request id, task id 도입 |
| 대화 길이 폭증 | 원문 전체 전달 | context compression step 추가 |
| 근거 추적 불가 | 요약에 출처가 섞임 | evidence 배열 분리 |
ADR 스타일 결론
Decision
agent 간 통신은 대화 전달이 아니라 작업 패킷 전달로 설계합니다. workflow 내부에는 최소한의 공유 상태를 두고, agent 간에는 goal, constraints, evidence, output contract를 포함한 handoff envelope를 사용합니다.
실무 체크리스트
- handoff payload에 목표와 금지 사항이 모두 포함되는가
- 전체 대화를 그대로 넘기지 않고 요약 단계가 있는가
- 근거와 요약이 분리되어 있는가
- 메시지마다 request id 또는 task id가 있는가
- shared state와 message payload의 책임이 분리되어 있는가