ADK Go 2.0 그래프 워크플로우: 멀티 에이전트를 Go 서비스에 붙이는 방법
요약: Google의 ADK for Go 2.0은 에이전트 앱을 단일 프롬프트가 아니라 그래프 워크플로우로 다루게 만든다. 조건 분기, fan-out/fan-in, human-in-the-loop, 재시도, 상태 저장, 재개를 Go 런타임 안에서 표현할 수 있다. 검색 의도는 “ADK Go 2.0으로 production agent를 어떻게 설계하나?”에 가깝다. 이 글은 기능 소개보다 실무 설계 순서에 초점을 둔다.
왜 에이전트에 그래프가 필요한가
초기 에이전트는 대부분 단순하다. 사용자 입력을 받고, LLM을 호출하고, 도구를 몇 번 호출한 뒤 답한다. 하지만 실제 서비스에 붙이면 곧 제어 흐름이 복잡해진다.
예를 들어 사내 결제 문의 처리 에이전트를 생각해보자. 입력을 먼저 분류해야 한다. 환불이면 주문 데이터를 조회하고, 정책을 확인하고, 금액이 크면 사람 승인을 받아야 한다. 장애 신고라면 로그를 보고, 최근 배포와 연결하고, 임시 대응 문서를 만들어야 한다. 보안 이슈라면 민감 정보 마스킹과 별도 승인 경로가 필요하다.
이 모든 것을 하나의 system prompt에 넣고 “알아서 해”라고 하면 운영이 불안정해진다. 프롬프트가 길어지고, 분기 기준이 모호해지고, 중간 상태를 추적하기 어렵다. 실패했을 때 어느 단계가 문제였는지도 잘 보이지 않는다.
ADK Go 2.0의 그래프 워크플로우는 이 문제를 코드 구조로 풀려는 접근이다. 노드가 작업 단위가 되고, 엣지가 흐름이 되며, 라우팅 값으로 다음 단계를 고른다. 모델은 판단이 필요한 곳에 들어가고, 실행 흐름은 그래프가 맡는다.
ADK Go 2.0의 핵심 구성요소
ADK Go 2.0에서 먼저 봐야 할 구성요소는 다음과 같다.
- Function node: 일반 Go 함수를 노드로 감싼다.
- Agent node: LLM agent를 그래프 안의 단계로 넣는다.
- Tool node: 도구 실행을 독립 노드로 둔다.
- Join node: 병렬 branch의 결과를 모은다.
- Dynamic node: 실행 순서가 런타임에 정해지는 경우 Go 코드로 오케스트레이션한다.
- Workflow node: 그래프 안에 하위 그래프를 넣는다.
- Parallel worker: 리스트의 각 항목을 병렬 처리하고 결과를 모은다.
- State-bound node: 세션 상태 값을 typed parameter로 가져온다.
중요한 점은 “그래프도 agent.Agent로 실행된다”는 점이다. 별도의 특수 서버나 전용 harness를 붙이는 방식이 아니라, 기존 runner와 launcher에 얹히는 구조다. Go 서비스 안에 에이전트 기능을 넣으려는 팀에게 이 부분이 실용적이다.
또 하나의 변화는 agent.Context로 통합된 실행 컨텍스트다. 이전의 ToolContext, CallbackContext 같은 개념이 하나로 모이면서 도구, 콜백, 노드 작성 방식이 단순해졌다. 마이그레이션 작업은 필요하지만, 장기적으로는 테스트와 telemetry 구조를 정리하기 쉽다.
단일 프롬프트를 그래프로 나누는 기준
그래프 워크플로우를 쓸 때 가장 먼저 할 일은 기존 프롬프트를 노드로 쪼개는 것이다. 아무 기준 없이 잘게 나누면 오히려 복잡해진다. 다음 기준을 쓰면 안전하다.
첫째, 입력과 출력 스키마가 명확한 작업은 노드로 분리한다. 예를 들어 문의 분류 결과는 {category, confidence, reason} 같은 구조로 표현할 수 있다. 이런 작업은 노드화하기 좋다.
둘째, 실패 시 재시도 정책이 다른 작업은 분리한다. 외부 API 조회는 네트워크 오류 재시도가 필요하지만, 정책 판단 LLM 호출은 같은 입력으로 재시도해도 의미가 적을 수 있다.
셋째, 사람이 승인해야 하는 작업은 별도 노드로 둔다. 환불 승인, 외부 발송, 권한 변경, 데이터 삭제 같은 단계는 human-in-the-loop 노드가 되어야 한다.
넷째, 병렬 처리 가능한 작업은 fan-out/fan-in으로 분리한다. 코드 리뷰에서 보안, 성능, 테스트 커버리지, API 호환성을 각각 다른 branch로 보게 한 뒤 Join node에서 합치는 식이다.
다섯째, 같은 흐름이 여러 곳에서 재사용되면 workflow node로 묶는다. 예를 들어 “민감정보 마스킹 -> 정책 검사 -> 승인 요청”은 여러 업무에서 반복될 수 있다.
LLM은 라우터로 쓰고, 그래프는 실행기로 둔다
ADK Go 2.0에서 특히 유용한 패턴은 LLM-as-router다. 모델이 사용자 요청을 읽고 category를 결정한다. 이후 그래프가 해당 route에 맞는 노드로 보낸다.
이 방식의 장점은 책임 분리가 명확하다는 것이다. 모델은 애매한 자연어를 구조화한다. 그래프는 구조화된 route를 실행한다. 모델이 직접 모든 도구를 고르게 하지 않아도 된다.
실무 예시는 다음과 같다.
classify_requestLLM node가 문의를refund,bug,account,security,other로 분류한다.- route node가 confidence가 낮으면
ask_clarifying_question으로 보낸다. refund는 주문 조회와 정책 검사로 간다.- 20만원 이상 환불은
human_approval노드에서 멈춘다. - 승인되면 처리 API를 호출하고, 거절되면 안내 메시지를 만든다.
이 구조는 프롬프트만으로 만든 에이전트보다 운영자가 이해하기 쉽다. 어디서 멈췄는지, 어떤 route가 선택됐는지, 어떤 노드가 실패했는지 telemetry에 남는다.
Human-in-the-loop는 기능이 아니라 안전장치다
ADK Go 2.0은 노드가 중간에 멈추고 사람 입력을 요청할 수 있다. 이 기능은 단순히 “승인 버튼”을 붙이는 수준이 아니다. durable resume, schema validation, idempotent resume 같은 운영 요소가 중요하다.
실무에서 human-in-the-loop를 넣어야 하는 지점은 다음과 같다.
- 고객에게 메시지를 발송하기 전
- 환불, 결제 취소, 권한 변경 전
- 외부 시스템에 쓰기 작업을 하기 전
- 민감 데이터가 포함된 문서를 공유하기 전
- 법무, 보안, 장애 대응처럼 책임 소재가 큰 작업 전
중요한 것은 승인 요청 메시지의 품질이다. “승인할까요?”만 띄우면 사람은 판단하기 어렵다. 요청에는 최소한 작업 요약, 영향 범위, 근거 데이터, 실패 시 복구 방법, 추천 액션이 들어가야 한다.
예를 들어 환불 승인 요청이라면 다음 정보를 포함한다.
- 고객 ID와 주문 ID
- 환불 금액
- 정책상 가능 여부
- 최근 환불 이력
- 에이전트 추천과 근거
- 승인/거절 후 실행될 API
사람을 루프에 넣는 목적은 속도를 늦추는 것이 아니라, 책임 있는 자동화를 만드는 것이다.
재시도와 타임아웃을 노드별로 다르게 잡아야 한다
ADK Go 2.0은 노드별 retry policy, exponential backoff, jitter, timeout, graph-wide concurrency cap을 제공한다. 이 기능을 제대로 쓰려면 작업 특성별로 정책을 다르게 잡아야 한다.
외부 API 조회 노드는 재시도 가치가 있다. 429, 500, 네트워크 timeout은 backoff 후 재시도하면 성공할 수 있다. 반면 LLM이 잘못된 route를 낸 경우 같은 요청을 즉시 반복해도 품질이 크게 좋아지지 않을 수 있다. 이때는 재시도보다 clarification이나 fallback route가 낫다.
병렬 branch는 격리가 중요하다. 한 branch의 중간 대화나 오류 로그가 다른 branch의 프롬프트에 섞이면 예측하기 어렵다. ADK의 isolation scope와 branch history 분리는 이런 문제를 줄이는 데 도움이 된다.
운영 기준은 다음처럼 잡을 수 있다.
| 노드 유형 | 재시도 | 타임아웃 | 실패 처리 |
|---|---|---|---|
| 외부 API 조회 | 3~5회 | 짧게 | fallback 데이터 또는 사용자 안내 |
| LLM 분류 | 1~2회 | 중간 | confidence 낮으면 질문 |
| 결제/권한 쓰기 | 자동 재시도 제한 | 짧게 | idempotency key 필수 |
| 병렬 분석 branch | 1회 | branch별 제한 | Join에서 누락 표시 |
| 승인 대기 | 재시도 없음 | 업무 SLA 기준 | 만료 시 보류 |
도입 순서: 고객-facing보다 내부 운영부터
ADK Go 2.0을 처음 도입한다면 고객-facing 자동화부터 시작하지 않는 편이 좋다. 내부 운영 업무가 더 안전하다. 예를 들어 장애 리포트 초안, PR 리뷰 보조, 고객 문의 분류, 릴리즈 노트 생성 같은 작업이 적합하다.
추천 순서는 다음과 같다.
- 기존 수작업 workflow를 하나 고른다.
- 단계를 노드로 쪼개고 입력·출력 스키마를 정한다.
- LLM이 필요한 단계와 코드로 충분한 단계를 분리한다.
- 외부 쓰기 작업은 모두 human-in-the-loop 뒤에 둔다.
- 노드별 retry, timeout, telemetry를 설정한다.
- 실패 로그를 모아 route와 prompt를 개선한다.
- 같은 패턴을 workflow node로 묶어 재사용한다.
이 접근은 화려하지 않지만 운영 가능성이 높다. 에이전트는 데모보다 장애 대응이 중요하다.
실행 체크리스트
- 단일 프롬프트에 들어 있던 업무를 노드 단위로 분해했다.
- 각 노드의 입력·출력 스키마를 문서화했다.
- LLM 판단 단계와 결정적 코드 실행 단계를 분리했다.
- route 값과 fallback route를 정의했다.
- human-in-the-loop가 필요한 쓰기 작업을 모두 표시했다.
- 승인 요청 메시지에 근거, 영향 범위, 실행 API를 포함했다.
- 외부 API 노드와 LLM 노드의 재시도 정책을 다르게 설정했다.
- branch별 isolation과 graph-wide concurrency cap을 검토했다.
- telemetry에서 node, route, retry, timeout, resume 이벤트를 볼 수 있다.
- 고객-facing 적용 전 내부 업무에서 최소 2주 이상 실패 패턴을 모았다.
ADK Go 2.0의 의미는 Go로 에이전트를 만들 수 있다는 데서 끝나지 않는다. 중요한 변화는 에이전트 workflow를 코드 리뷰 가능한 그래프로 바꾼다는 점이다. 실무 팀에게 필요한 것은 “똑똑한 프롬프트”보다 멈출 수 있고, 재개할 수 있고, 설명할 수 있는 실행 구조다.