ADK Go 2.0 멀티 에이전트 그래프 운영법: HITL·재시도·상태 설계
요약: Google의 ADK for Go 2.0은 멀티 에이전트 애플리케이션을 그래프로 구성하는 방향을 전면에 내세운다. function node, agent node, tool node, join node, dynamic node, workflow node, parallel worker를 조합하고, human-in-the-loop(HITL), retry, timeout, concurrency 제한, state persistence를 런타임에서 다룬다. Go 백엔드에서 에이전트를 제품 기능으로 넣으려는 팀에게는 ‘프롬프트를 잘 쓰는 법’보다 ‘실패해도 복구되는 실행 그래프를 짜는 법’이 더 중요하다.
문제: 에이전트 코드는 빨리 스파게티가 된다
초기 에이전트 프로토타입은 보통 단순하다. 사용자의 요청을 받고, LLM에게 계획을 만들게 하고, 도구 몇 개를 호출한 뒤 답한다. 하지만 제품에 들어가면 곧 복잡해진다. 요청을 분류해야 하고, 전문 에이전트로 분기해야 하고, 여러 API를 병렬로 호출해야 하고, 실패하면 재시도해야 하고, 일정 금액 이상은 사람이 승인해야 한다. 이 흐름을 if문과 goroutine, channel, 임시 DB 필드로 직접 엮으면 처음에는 빨라 보이지만 디버깅이 어려워진다.
ADK Go 2.0의 그래프 기반 워크플로우는 이 문제를 구조적으로 푼다. 각 작업 단위를 node로 만들고, node 사이의 edge와 route로 실행 흐름을 선언한다. 중요한 점은 그래프가 별도 배치 시스템이 아니라 agent.Agent로 동작한다는 것이다. 기존 runner, launcher, console과 같은 실행 환경을 그대로 사용할 수 있다. 단일 에이전트와 복잡한 그래프가 같은 런타임에서 돈다는 것은 관측성, 배포, 테스트를 단순하게 만든다.
기본 설계: node를 너무 크게 만들지 말 것
실무에서 가장 흔한 실수는 node 하나에 너무 많은 책임을 넣는 것이다. 예를 들어 ‘환불 처리’ node 안에서 주문 조회, 정책 판단, 사용자 확인, 승인 요청, 환불 API 호출, 알림 발송까지 모두 처리하면 그래프를 쓰는 의미가 줄어든다. 실패 지점도 흐려지고, 재시도 범위도 커진다. 권장 방식은 node를 외부 부작용 단위나 판단 단위로 쪼개는 것이다.
예시로 환불 에이전트를 설계하면 다음처럼 나눌 수 있다. classify_request node가 환불·교환·문의로 분류한다. fetch_order node가 주문 정보를 가져온다. evaluate_policy node가 자동 환불 가능 여부를 판단한다. request_approval node가 금액이나 위험 조건에 따라 사람에게 승인 요청을 보낸다. execute_refund node가 실제 환불 API를 호출한다. notify_user node가 결과를 사용자에게 설명한다. 이렇게 나누면 execute_refund에만 강한 idempotency key를 넣고, fetch_order에는 짧은 retry를 넣는 식으로 정책을 다르게 줄 수 있다.
HITL은 예외가 아니라 정상 플로우다
ADK Go 2.0에서 HITL이 built-in primitive로 들어간 점은 중요하다. 실제 업무 에이전트는 완전 자동화보다 ‘대부분 자동화 + 중요한 지점에서 사람 확인’이 먼저 도입된다. 환불, 계약서 발송, 배포 승인, 개인정보 조회, 비용 발생 작업은 사람이 끼어들어야 한다. 이때 사람이 답할 때까지 프로세스가 죽지 않고, 나중에 재개되며, 스키마에 맞게 응답을 검증해야 한다.
HITL을 직접 구현하면 보통 세 가지 문제가 생긴다. 첫째, 승인 요청과 원래 실행 상태를 연결하는 ID가 흔들린다. 둘째, 프로세스 재시작 후 어디서 재개해야 하는지 잃어버린다. 셋째, 사용자가 늦게 답하거나 잘못된 형식으로 답했을 때 예외 처리가 불명확하다. ADK의 interrupt, resumed input, idempotent resume은 이 문제를 런타임 레벨에서 다루려는 접근이다. 개발자는 ‘어떤 질문을 어떤 schema로 물을지’에 집중할 수 있다.
재시도와 타임아웃은 node별로 다르게 둔다
모든 node에 같은 retry를 걸면 장애가 커진다. 읽기 API는 3~5회 재시도해도 괜찮을 수 있지만, 결제·환불·메일 발송 같은 부작용 작업은 idempotency 없이 재시도하면 중복 실행 위험이 있다. ADK Go 2.0은 node config에 retry policy, timeout, graph-wide concurrency 제한을 둘 수 있다. 이것을 제품 정책과 연결해야 한다.
권장 기준은 간단하다. 외부 상태를 바꾸지 않는 조회 node는 짧은 exponential backoff와 jitter를 둔다. 외부 상태를 바꾸는 node는 idempotency key가 있을 때만 재시도한다. LLM 판단 node는 timeout을 너무 길게 두지 말고 fallback route를 둔다. 병렬 fan-out은 API rate limit과 비용 예산을 기준으로 max concurrency를 정한다. join node는 일부 실패를 허용할지, 전체 실패로 볼지 명확히 결정한다.
상태 설계: session state와 업무 DB를 구분한다
에이전트 프레임워크가 session state를 제공한다고 해서 모든 업무 상태를 거기에 넣으면 안 된다. session state는 대화와 실행 흐름을 재개하기 위한 상태에 가깝다. 주문 상태, 결제 상태, 고객 권한, 재고 수량처럼 시스템의 근거 데이터는 기존 업무 DB에 남아야 한다. 에이전트 state에는 ‘현재 workflow run이 어떤 근거 데이터를 읽었고, 어떤 결정을 대기 중인지’를 저장하는 것이 안전하다.
예를 들어 여행 예약 에이전트가 사용자의 날짜 변경을 받았다고 하자. session state에는 최신 사용자 의도와 후보 itinerary ID를 저장할 수 있다. 하지만 실제 예약 확정은 booking service의 transaction으로 처리해야 한다. 그래야 에이전트가 재시작되거나 같은 요청을 다시 실행해도 예약 중복을 막을 수 있다.
Go 팀이 얻는 장점과 주의점
Go 팀에게 ADK Go 2.0이 매력적인 이유는 타입과 런타임 통합이다. function node에서 generics가 input/output schema를 추론하고, plain Go 함수로 dynamic orchestration을 작성할 수 있다. 기존 Go 서비스에 에이전트를 붙일 때 별도 Python 오케스트레이터를 두지 않아도 된다. OpenTelemetry span tree가 node/agent 실행을 일관되게 보여주는 것도 운영 관점에서 크다.
주의할 점도 있다. 그래프가 생기면 모든 것이 깔끔해질 것 같지만, 잘못 짜면 시각적으로만 예쁜 스파게티가 된다. route 조건은 테스트 가능해야 하고, node 이름은 로그에서 바로 의미가 보여야 하며, cycle은 종료 조건이 명확해야 한다. LLM을 router로 쓸 때는 classification confidence와 fallback을 반드시 둬야 한다. 모델이 route를 틀리면 뒤의 그래프가 아무리 좋아도 잘못된 업무가 실행된다.
실행 체크리스트
- 업무 흐름을 node로 쪼갤 때 판단, 조회, 부작용, 사용자 승인 단위를 분리한다.
- 모든 외부 부작용 node에는 idempotency key와 중복 실행 방지 로직을 둔다.
- HITL 질문은 자유 텍스트가 아니라 schema로 받는다. yes/no, 금액, 날짜, 선택지처럼 검증 가능한 형태가 좋다.
- node별 retry, timeout, fallback route를 다르게 설정한다.
- fan-out 병렬 실행은 API rate limit과 비용 예산을 기준으로 제한한다.
- session state에는 실행 재개용 상태를, 업무 DB에는 진짜 업무 상태를 저장한다.
- LLM router는 단위 테스트와 회귀 테스트를 따로 둔다. 대표 요청 50개만 있어도 route 품질이 크게 안정된다.
- 운영 로그에는 workflow run ID, node ID, route, retry count, human interrupt ID, final outcome을 남긴다.
ADK Go 2.0은 에이전트를 더 똑똑하게 만드는 도구라기보다, 에이전트가 실패해도 추적하고 재개할 수 있게 만드는 실행 모델에 가깝다. 프로덕션에서는 이 차이가 크다. 데모는 한 번 성공하면 되지만, 제품은 실패한 다음에도 안전하게 이어져야 한다.