18장. 그래프 기반 워크플로우
왜 그래프가 필요한가 — if문이 뒤엉키기 시작한다
한 줄 요약
17장까지 하나의 에이전트를 단계별로 만들며 필요한 기능을 갖췄습니다. 이제 이 기능들을 연결해 하나의 상담 흐름으로 구성할 차례입니다. 하지만 조건마다 if문을 추가하다 보면 코드가 복잡해져 전체 흐름을 파악하기 어려워집니다. 이번 장에서는 각 단계와 연결 관계를 그래프로 표현해, 상담 흐름을 명확하게 정의하고 관리하는 방법을 배웁니다.
1. 여기서부터 STEP 7이다
1장에서 이 과정은 17장까지 에이전트 하나를 점점 키워 가고, 왜 나눠야 하는지를 먼저 겪어 본 다음에 나눈다고 했습니다. 지금이 그 자리입니다.
| 지금까지 만든 부품 | 어디서 |
|---|---|
| 문의가 어떤 유형인지 가려내는 분류기 | 4장 |
| 주문·상품·멤버십을 조회하는 도구와, 도구를 골라 쓰는 에이전트 | 5~11장 |
| 정책 문서에서 근거를 찾아 답하는 검색 | 12~15장 |
| 대화를 이어 가는 기억 | 16~17장 |
16장과 17장의 「따라하기」를 떠올려 보세요. 16장의 상담원은 주문 도구만 가졌고, 환불 기간 같은 정책은 "확인 후 안내"로 미뤘습니다. 17장의 상담원은 프롬프트에 직접 적어 준 정책 발췌 두 줄을 바탕으로 답했습니다. 부품은 다 있는데 한 대화에서 함께 쓰이지 않습니다.
STEP 7(18~24장)은 이 부품들을 엮고, 여러 에이전트로 나누고, 서로 일을 넘기게 만드는 단계입니다. 이것을 오케스트레이션(orchestration) 이라고 합니다. 그 첫걸음이 "흐름을 어떻게 적을 것인가"입니다.
2. 가장 먼저 떠오르는 방법 — if문
부품을 엮으려고 하면 자연스럽게 이런 코드가 나옵니다. (설명을 위한 예시입니다. 이 코드는 만들지 않습니다.)
def handle(question, history):
intent = classify(question)
if intent == "환불교환":
docs = search_policy(question)
if not docs:
return make_ticket(question)
answer = generate_with_context(question, docs)
elif intent == "주문배송조회":
if not history:
answer = agent_with_tools(question)
else:
answer = agent_with_tools_and_history(question, history)
elif intent == "상담원연결":
return make_ticket(question)
else:
...
log(question, intent, answer)
return answer
지금은 읽을 만합니다. 문제는 이 함수가 앞으로 겪을 일입니다.
| 새 요구 | 이 함수에서 일어나는 일 |
|---|---|
| 긴급한 문의는 먼저 처리하자 | 분기가 하나 더 늘어난다 |
| 정책 검색에 실패하면 한 번 더 찾자 | if 안에 if가 한 겹 더 들어간다 |
| 답한 뒤에 기록을 남기자 | return이 여러 곳에 있어, 모든 갈래의 끝을 손봐야 한다 |
위 코드를 다시 보세요. 이미 return make_ticket(...) 두 곳은 맨 아래의 log(...)를 거치지 않고 빠져나갑니다. 티켓으로 넘긴 문의는 기록이 남지 않습니다. 열다섯 줄짜리 함수에 벌써 구멍이 있습니다.
3. 근본 원인 — 흐름이 코드 속에 묻혀 있다
if문 방식의 문제는 길이가 아니라 흐름이 보이지 않는다는 데 있습니다.
| 문제 | 뜻 |
|---|---|
| 보이지 않는다 | "분류 다음에 무엇이 오는가"를 알려면 코드를 처음부터 읽어야 합니다. 그림으로 보여 줄 수 없습니다 |
| 떼어 내 시험할 수 없다 | 흐름의 한 단계만 꺼내 따로 돌려 보기 어렵습니다 |
| 고치기 무섭다 | 한 갈래를 고치면 다른 갈래가 망가지지 않았는지 알 수 없습니다 |
필요한 것은 "어떤 단계들이 있고, 어느 단계 다음에 어느 단계가 오는가"를 코드와 따로 적어 두는 방법입니다.
4. 이걸 모르면 무엇을 판단하지 못하는가
- 에이전트가 이상하게 동작할 때 어느 단계에서 틀어졌는지 짚지 못합니다.
- AI가 만들어 준 에이전트 코드가 어떤 순서로 무엇을 하는지 설명하지 못합니다.
- 새 기능을 넣을 때 어디를 건드려야 하고 어디는 건드리면 안 되는지 가려내지 못합니다.
- 여러 에이전트를 묶은 시스템(21~22장)을 무엇 위에 세우는지 알지 못합니다.
5. 이번 장에서 다루는 것
흐름을 구조로 적는 방법이 상태 그래프(state graph) 이고, 이 과정에서는 LangGraph라는 라이브러리로 만듭니다. 11장에서 create_agent로 에이전트를 만들 때 그 밑에서 돌아가던 것이 LangGraph입니다. 이번 장부터는 직접 다룹니다.
| 요소 | 한 줄 설명 | 순서도로 치면 |
|---|---|---|
| 상태(State) | 흐름 전체가 함께 보는 데이터 | 단계마다 넘겨받는 서류철 |
| 노드(Node) | 일하는 단계 하나 | 상자 |
| 엣지(Edge) | 단계에서 단계로 가는 길 | 화살표 |
이번 장의 그래프는 가장 단순한 직선입니다. 분류하고, 답하고, 기록합니다. 갈림길은 19장에서 넣습니다.
핵심 정리
- 17장까지는 에이전트 하나를 키웠고, 18장부터는 부품을 엮고 나누는 오케스트레이션입니다.
- if문으로 엮은 흐름은 보이지 않고, 떼어 시험할 수 없고, 고치기 무섭습니다.
- 해법은 흐름을 코드에서 꺼내 상태·노드·엣지로 선언하는 것입니다.
- 이번 장의 산출물은 분류 → 응답 → 기록으로 이어지는 첫 상태 그래프입니다.