그래프 엔지니어링 — 거대 프롬프트가 환각하는 진짜 이유와 그 구조적 처방
프롬프트 하나에 전부 담은 에이전트는 왜 자신 있게 거짓말을 하는가. Google ADK 2.0의 팬아웃·조인·라우터 세 패턴으로 LLM 호출 1회짜리 멀티에이전트 워크플로를 만드는 법.
Google Cloud Tech 채널에 올라온 6분짜리 영상 Graph Engineering with ADK 를 정리했습니다. 발표자는 Annie, 주제는 그래프 엔지니어링(Graph Engineering) 입니다.
한 줄로 요약하면 이렇습니다.
에이전트가 환각하는 건 프롬프트가 나빠서가 아니라 구조가 없어서다. 처방은 더 좋은 프롬프트가 아니라 그래프다.
영상은 마라톤 레이스 코치 에이전트 하나를 끝까지 끌고 가면서 “프롬프트 한 덩어리” → “노드 2개 그래프” → “팬아웃·조인·라우터가 들어간 그래프”로 세 번 리팩터링합니다. 그 과정에서 LLM 호출 횟수는 끝까지 1회로 유지됩니다. 이게 이 영상의 진짜 하이라이트입니다.
1. 문제 — 거대 프롬프트 하나짜리 에이전트
예제 상황은 단순합니다. 마라톤을 앞둔 러너가 에이전트에게 질문 하나를 던집니다.
“이 레이스를 어떻게 뛰어야 하나요?”
첫 번째 시도는 에이전트 하나, 거대 프롬프트 하나입니다. 프롬프트 안에 워크플로 전체가 글로 적혀 있습니다.
- 날씨를 가져와라
- 코스를 분석해라
- 내 체력 상태를 확인해라
- 그걸로 레이스 전략을 세워라
돌려보면 답이 나옵니다. 그것도 아주 자신 있고 아주 구체적으로. 문제는 여기서 시작됩니다.
이 에이전트에는 날씨 API가 연결돼 있지 않고, 코스 데이터도 없습니다. 그러니까 답에 등장한 숫자는 전부 지어낸 것입니다.
영상은 이걸 “질병(disease)”이라고 부릅니다. 증상이 아니라 병입니다.
모든 단계가 단 한 번의 모델 호출 안에서 살면, 무엇도 가져올 수 없고, 무엇도 테스트할 수 없고, 무엇도 신뢰할 수 없다.
여기서 중요한 포인트는 “프롬프트를 더 잘 쓰면 해결된다”가 아니라는 것입니다. 날씨 API가 없는 건 문장력의 문제가 아닙니다. 배선의 문제입니다.
2. 네 번째 층 — 프롬프트, 컨텍스트, 루프, 그리고 그래프
영상은 그래프 엔지니어링을 지금까지 쌓아온 계층의 다음 층으로 위치시킵니다.
정의는 의외로 담백합니다.
시스템을 위한 그래프 워크플로를 만드는 것. 그래프 안의 노드에는 에이전트를 둘 수도 있고, 결정론적인 함수를 둘 수도 있다.
“에이전트 또는 함수”가 같은 급의 노드라는 점이 이 정의의 핵심입니다. 바로 다음 절에서 이게 왜 중요한지 드러납니다.
3. 가장 작은 그래프 — 노드 2개
리팩터링 1단계는 거창하지 않습니다. 문제를 풀 수 있는 가장 작은 그래프를 만듭니다.
먼저 레이스 당일 컨디션을 가져오고, 그다음 전략을 짠다. 노드 두 개면 됩니다.
| 노드 | 정체 | 비용 |
|---|---|---|
fetch | 평범한 파이썬 함수 | 모델 없음, 비용 0 |
advise | 에이전트 | LLM 호출 1회 |
영상에서 “사람들이 놀라는 부분”이라고 콕 집어 말하는 지점이 있습니다.
함수와 에이전트가 같은 목록에, 같은 방식으로 배선된다.
start → fetch → advise. 이 엣지 정의가 오케스트레이션의 전부입니다. “함수를 부르는 코드”와 “에이전트를 부르는 코드”가 따로 있는 게 아닙니다.
그리고 원칙 하나가 나옵니다. 사실 대부분 이미 알고 있는 원칙입니다.
예측 가능한 일은 함수로, 추론은 모델로. (Predictable work goes in functions, reasoning goes in the model.)
알고 있는데 안 지키는 이유는 보통 하나입니다. 프롬프트 하나에 다 때려넣는 게 당장은 빠르니까요.
4. 세 가지 패턴 — 팬아웃, 조인, 라우터
두 번째 리팩터링에서 그래프가 진짜 그래프다워집니다.
전략을 세우려면 입력이 세 개 필요합니다. 날씨, 코스, 체력. 그런데 이 셋은 서로 의존하지 않습니다. 날씨를 알아야 코스를 분석할 수 있는 게 아닙니다.
그럼 순서대로 할 이유가 없습니다.
패턴 1 — 팬아웃(Fan-out)
start에서 엣지를 세 개 뽑습니다. 세 개의 fetch가 동시에 출발해 동시에 처리됩니다.
여기에 ADK 2.0이 하나 더 얹는 게 있습니다. 다이나믹 팬아웃(dynamic fan-out) 입니다.
병렬 처리를 해야 하는데 몇 개가 동시에 돌지 미리 모르는 경우, 다이나믹 워크플로로 런타임에 그래프 모양을 만든다.
정적으로 “3갈래”라고 못 박는 게 아니라, 입력을 보고 갈래 수를 정한다는 뜻입니다.
패턴 2 — 조인(Join)
세 갈래가 다 끝나면 조인 노드에서 만납니다. 영상은 이걸 “거대한 시스템 속 신디사이저”에 비유합니다.
조인 노드가 하는 일은 두 가지입니다.
- 세 갈래를 전부 기다린다 — 가장 느린 branch에 맞춰 기다립니다
- 결과를 노드 이름을 키로 하는 딕셔너리 하나로 묶는다
여기가 실무자 입장에서 제일 반가운 대목입니다.
머저(merger)를 직접 짤 필요가 없다. 애그리게이터 에이전트를 만들 필요도 없다. 조인 노드가 그래프 안에서 그냥 동작한다.
“결과 세 개를 합쳐줘”라고 LLM에게 시키는 순간 비용이 붙고 비결정성이 붙습니다. 그걸 그래프 원시 기능으로 흡수한 겁니다.
패턴 3 — 라우터(Router)
마지막이자, 영상이 “다들 제일 쉽게 틀리는 모양” 이라고 경고한 패턴입니다.
상황은 이렇습니다. 더운 날 / 보통 날 / 추운 날 — 전략이 셋이고, 전문 에이전트도 셋입니다. 하나를 골라야 합니다.
선택지는 두 개입니다.
옵션 1 — LLM 라우터. 모델에게 분류를 시킵니다. 동작은 합니다. 다만,
- 토큰 비용이 든다
- AI는 비결정적이라 잘못 읽을 수 있다
옵션 2 — 결정론적 라우터. 고정된 조건을 가진 라우터 노드를 그래프에 둡니다. hot → 더운 날 전략, normal → 보통 날 전략, cold → 추운 날 전략.
그럼 언제 무엇을 쓰나. 영상이 준 판별 기준은 명확합니다.
| 상황 | 성격 | 맞는 라우터 |
|---|---|---|
| 사용자가 자유 텍스트로 아무거나 입력 — if 문이 읽을 신호가 없다 | 열린 집합 | LLM 라우터 |
| 경우의 수가 정해져 있고 판단 신호가 이미 데이터에 들어 있다 | 닫힌 집합 | 결정론적 라우터 |
마라톤 예제는 기온이라는 신호가 데이터에 있고 경우의 수가 셋으로 닫혀 있으니 결정론적 라우터를 씁니다. 이유도 두 개입니다. 더 신뢰할 수 있고, 더 싸다.
5. 비용을 세어보자 — 여전히 LLM 호출 1회
패턴 셋을 다 넣고 나서 영상은 비용을 셉니다.
- 병렬 fetch 3개
- 조인 1개
- 전략가(strategist) 1개
노드가 다섯입니다. 그런데 LLM 호출은 정확히 1회입니다.
모델 호출이 필요한 노드는 전략가 에이전트 하나뿐이고, 나머지는 전부 모델이 필요 없는 로직이기 때문입니다.
이 지점이 “멀티에이전트 = 비싸다”는 통념을 정면으로 건드립니다. 비싸지는 건 에이전트를 나눠서가 아니라, 모델이 필요 없는 일까지 모델에게 시켜서입니다.
처음의 거대 프롬프트도 LLM 호출은 1회였습니다. 같은 1회를 쓰고도 한쪽은 전부 지어낸 숫자를 내놨고, 다른 쪽은 실제 데이터 위에서 답을 냈습니다.
6. 그런데, 그래프를 쓰면 안 되는 경우
영상이 좋았던 이유 중 하나는 여기서 브레이크를 밟는다는 점입니다.
가끔은 그래프가 아예 필요 없다.
판단을 위한 질문 두 개를 줍니다.
질문 1 — 입력이 도착하기 전에 워크플로를 그릴 수 있는가? 그릴 수 있다면 그리고, 그래프 워크플로를 쓰면 됩니다.
질문 2 — 모양 자체가 입력에 따라 달라진다면? 예로 든 게 딥리서치 시나리오입니다. 그래프가 정확히 어떻게 생겼는지 미리 알 수 없습니다. 이 경우 ADK 2.0의 다이나믹 워크플로로, 코드가 런타임에 그래프 모양을 결정하게 합니다.
즉 “그래프냐 아니냐”가 아니라 “모양을 언제 정하느냐” 의 문제로 바뀝니다.
| 워크플로 모양 | 결정 시점 | 도구 |
|---|---|---|
| 정적 — 미리 그릴 수 있다 | 설계 시점 | 그래프 워크플로 |
| 동적 — 입력에 따라 달라진다 | 런타임 | 다이나믹 워크플로 / 다이나믹 팬아웃 |
7. 정리하며
이 영상에서 건져갈 것을 네 줄로 줄이면 이렇습니다.
- 환각은 프롬프트 품질 문제가 아니라 구조 문제다. 한 번의 모델 호출 안에 모든 단계를 넣으면 아무것도 검증할 수 없다.
- 예측 가능한 일은 함수로, 추론은 모델로. 그리고 그래프에서 둘은 같은 급의 노드다.
- 팬아웃 · 조인 · 라우터. 합치는 일(조인)과 고르는 일(라우터)을 LLM에게 시키지 않는 것만으로 비용과 비결정성이 같이 빠진다.
- 닫힌 집합이면 if 문, 열린 집합이면 LLM. 라우터를 고르는 기준은 이거 하나다.
개인적으로 가장 오래 남은 문장은 조인 노드 설명이었습니다. “머저를 직접 짤 필요도, 애그리게이터 에이전트를 만들 필요도 없다” — 병렬 결과를 합치겠다고 LLM 호출을 하나 더 쓰는 건 실제로 흔히 하는 실수인데, 그게 프레임워크 기능으로 내려가 있다는 건 꽤 큰 차이입니다.
원본 자료
- 영상: Graph Engineering with ADK — Google Cloud Tech, 6분 9초
- 실습: ADK Codelab
- 문서: adk.dev
영상으로 보기
기초부터 짚고 싶다면 이 영상을 먼저 보세요. (AI 에이전트 기초 EP.03 — LLM은 어떻게 말을 할까)
📺 Peanut Radio AI 유튜브 채널 — AI 시대의 IT, 기초부터 실무까지