Post

05. LangGraph StateGraph로 Agent 실행 흐름을 분리한 이유

05. LangGraph StateGraph로 Agent 실행 흐름을 분리한 이유

Lumi_agent에서 LangGraph를 쓴 이유는 “Agent를 만들 수 있어서”가 아니라 “실행 흐름을 보이게 만들 수 있어서”다.

외부 도구를 호출하는 Agent는 단순히 답변을 생성하는 챗봇보다 실패 지점이 많다. 도구를 호출할지, 어떤 도구가 안전한지, 실행 후 다시 생각할지, 마지막에 기억을 저장할지 결정해야 한다.

wrapper 방식의 한계

Agent wrapper를 쓰면 빠르게 만들 수 있다. 하지만 실행 흐름이 라이브러리 안쪽에 숨으면 다음 질문에 답하기 어려워진다.

질문필요한 제어
도구 호출 전 무엇을 prompt에 넣었나Context Builder 분리
민감 도구인지 어떻게 판단했나route 함수와 tool set 분리
도구 실행 후 어디로 돌아가나graph edge 명시
대화가 끝난 뒤 기억을 어디서 관리하나Memory Manager node

Lumi_agent는 이 흐름을 StateGraph로 분리했다.

실행 흐름

flowchart TB
    START((START)) --> ANALYZER[analyzer]
    ANALYZER --> CONTEXT[context_builder]
    CONTEXT --> AGENT[agent]
    AGENT --> ROUTE{route_after_agent}
    ROUTE -->|safe_tools| SAFE[safe_tools]
    ROUTE -->|sensitive_tools| SENSITIVE[sensitive_tools]
    ROUTE -->|memory_manager| MEMORY[memory_manager]
    SAFE --> AGENT
    SENSITIVE --> AGENT
    MEMORY --> END((END))

Agent Node는 LLM 호출과 tool binding을 담당한다. Analyzer는 사용자 메시지에서 감정/호감도 상태를 읽고, Context Builder는 기억과 페르소나/도구 지침을 조립한다.

route_after_agent

Agent 응답에 tool call이 없으면 Memory Manager로 이동한다. tool call이 있으면 호출된 도구 이름을 보고 safe 또는 sensitive 경로로 보낸다.

조건이동
tool call 없음memory_manager
민감 도구 포함sensitive_tools
그 외 도구safe_tools

이 분기 덕분에 검색과 메시지 전송을 같은 방식으로 다루지 않는다.

단일 Agent와 분리된 노드

Lumi_agent의 Agent Node는 Reasoning, Tool Calling, Final Answer의 중심이다. 하지만 모든 기능을 Agent Node 안에 넣지는 않는다.

노드분리 이유
Analyzer감정/호감도 분석을 응답 생성과 분리
Context Builder기억 검색과 prompt 조립을 LLM 호출 전 단계로 분리
Tool Nodes안전 도구와 민감 도구 실행 경계를 분리
Memory Manager응답 이후 기록 정리와 장기 저장을 분리

이 방식은 디버깅에도 유리하다. 어느 단계에서 실패했는지, 도구 호출 전 prompt가 어떤 상태였는지, memory update가 어디서 일어났는지 추적할 수 있다.

실패 대응

코드에는 LLM 호출 실패 시 기본 응답을 반환하는 fallback과 rate limit 계열 오류의 1회 재시도 흐름이 있다. 이것은 운영 안정성을 보장한다는 뜻이 아니라, Agent workflow가 실패를 완전히 무시하지 않도록 최소한의 응답 경로를 둔 것이다.

다음 글

다음 글에서는 MCP 도구와 HITL 승인 경계를 어떻게 나눴는지 정리한다.

06. MCP Tool Calling과 HITL 승인 경계 설계

This post is licensed under CC BY 4.0 by the author.