LangGraph 서브그래프와 에이전트 아키텍처, 복잡한 AI 시스템을 단순하게 만드는 방법

AI 에이전트를 개발하다 보면 단순한 질문-답변을 넘어 더 복잡한 작업을 처리해야 하는 상황에 부딪힙니다. 여러 단계를 거쳐야 하거나, 다양한 도구를 사용해야 하거나, 심지어 여러 에이전트가 협력해야 하는 경우도 있죠. 이런 복잡한 요구사항을 어떻게 체계적으로 관리할 수 있을까요? 오늘은 LangGraph의 서브그래프와 다양한 에이전트 아키텍처를 통해 이 문제를 해결하는 방법을 알아보겠습니다.

에이전트 아키텍처의 기본 개념 이해하기

먼저 에이전트 아키텍처가 무엇인지 명확히 해봅시다. 기존의 AI 애플리케이션들은 대부분 정해진 순서대로 동작했습니다. RAG 시스템을 예로 들면, 사용자 질문이 들어오면 관련 문서를 검색하고, 그 문서를 LLM에게 전달해서 답변을 생성하는 식이죠. 이는 마치 공장의 컨베이어 벨트처럼 예측 가능한 흐름을 가집니다.

하지만 에이전트는 다릅니다. 에이전트는 LLM이 스스로 애플리케이션의 제어 흐름을 결정하는 시스템입니다. 마치 숙련된 직원이 상황을 보고 “이 경우에는 A 방법을, 저 경우에는 B 방법을 사용해야겠다”고 판단하는 것과 같습니다.

LLM이 애플리케이션을 제어하는 방법은 여러 가지가 있습니다. 두 가지 경로 중 하나를 선택할 수도 있고, 여러 도구 중에서 적절한 것을 골라 사용할 수도 있으며, 생성된 답변이 충분한지 아니면 더 작업이 필요한지 판단할 수도 있습니다.

라우터: 선택의 시작점

가장 단순한 형태의 에이전트 아키텍처는 라우터입니다. 라우터는 LLM이 미리 정의된 여러 옵션 중에서 하나를 선택하게 하는 방식입니다. 마치 고속도로에서 어느 출구로 나갈지 결정하는 것과 비슷합니다.

라우터의 핵심은 구조화된 출력입니다. LLM이 자유롭게 대답하는 대신, 특정 형식이나 스키마를 따라 응답하도록 만드는 것이죠. 이를 위해 몇 가지 방법을 사용할 수 있습니다.

첫 번째는 프롬프트 엔지니어링입니다. 시스템 프롬프트를 통해 LLM에게 특정 형식으로 답변하라고 지시하는 방법입니다. 예를 들어 “다음 중 하나로만 답변하세요: ‘product’, ‘support’, ‘billing'”이라고 명시하는 식입니다.

두 번째는 출력 파서를 사용하는 것입니다. LLM의 응답을 후처리해서 구조화된 데이터를 추출하는 방법입니다.

세 번째는 도구 호출 기능을 활용하는 것입니다. 일부 LLM들이 가진 내장 도구 호출 기능을 이용해 구조화된 출력을 생성하는 방식입니다.

이런 구조화된 출력이 중요한 이유는 LLM의 결정을 시스템이 안정적으로 해석하고 행동할 수 있게 하기 때문입니다. “아마도 제품 관련 문의인 것 같습니다”라는 모호한 답변보다는 “product”라는 명확한 라벨이 훨씬 처리하기 쉽습니다.

도구 호출 에이전트: 진짜 에이전트의 시작

라우터가 단일 결정만 내린다면, 더 복잡한 에이전트 아키텍처는 LLM의 제어권을 두 가지 핵심 방식으로 확장합니다.

첫 번째는 다단계 의사결정입니다. LLM이 한 번에 하나의 결정만 내리는 것이 아니라, 연속적으로 여러 결정을 내릴 수 있게 하는 것입니다. 마치 체스에서 한 수만 두는 것이 아니라 여러 수를 연속으로 계획하고 실행하는 것과 같습니다.

두 번째는 도구 접근입니다. LLM이 다양한 도구 중에서 선택해서 사용할 수 있게 하는 것입니다. 계산기, 웹 검색, 데이터베이스 조회 등 필요에 따라 적절한 도구를 골라 쓸 수 있습니다.

ReAct는 이 두 가지 확장을 결합한 대표적인 에이전트 아키텍처입니다. ReAct는 세 가지 핵심 개념을 통합합니다.

도구 호출의 실제

도구 호출은 에이전트가 외부 시스템과 상호작용할 때 필수적입니다. 외부 API들은 자연어가 아닌 특정한 입력 스키마나 페이로드를 요구하는 경우가 많습니다. API를 도구로 바인딩할 때, 모델에게 필요한 입력 스키마를 알려주는 것입니다.

사용자가 자연어로 입력하면, 모델은 그것을 기반으로 적절한 도구를 선택하고, 해당 도구가 요구하는 스키마에 맞는 출력을 반환합니다. LangChain에서는 이것이 매우 간단합니다. 파이썬 함수를 ChatModel.bind_tools(function)에 전달하기만 하면 됩니다.

메모리: 에이전트의 기억 저장소

메모리는 에이전트가 여러 단계의 문제 해결 과정에서 정보를 유지하고 활용할 수 있게 해주는 핵심 요소입니다. 메모리는 다양한 규모로 작동합니다.

단기 메모리는 에이전트가 현재 작업 과정에서 앞선 단계들에서 얻은 정보에 접근할 수 있게 해줍니다. 예를 들어 웹에서 정보를 검색한 후, 그 정보를 바탕으로 다음 질문을 생성할 때 사용됩니다.

장기 메모리는 이전 상호작용에서의 정보를 기억할 수 있게 해줍니다. 과거 대화 내용이나 사용자의 선호도 같은 것들을 기억하는 것이죠.

LangGraph는 메모리 구현에 대한 완전한 제어권을 제공합니다. State는 유지할 메모리의 정확한 구조를 지정하는 사용자 정의 스키마입니다. Checkpointer는 세션 내에서 여러 상호작용에 걸쳐 각 단계의 상태를 저장하는 메커니즘입니다. Store는 세션 간에 사용자별 또는 애플리케이션 수준의 데이터를 저장하는 메커니즘입니다.

계획: 에이전트의 사고 과정

도구 호출 에이전트에서 LLM은 while 루프에서 반복적으로 호출됩니다. 각 단계에서 에이전트는 어떤 도구를 호출할지, 그리고 그 도구에 어떤 입력을 제공할지 결정합니다. 도구들이 실행되고, 그 출력이 관찰로서 LLM에게 다시 피드백됩니다. 에이전트가 사용자 요청을 해결하기에 충분한 정보를 얻었다고 판단하고 더 이상 도구를 호출할 가치가 없다고 결정하면 while 루프가 종료됩니다.

이는 마치 탐정이 단서를 찾기 위해 여러 방법을 시도하고, 각 단서를 바탕으로 다음 행동을 결정하며, 충분한 증거를 모았다고 생각하면 수사를 마무리하는 것과 같습니다.

커스텀 에이전트 아키텍처: 특화된 솔루션

라우터와 도구 호출 에이전트가 일반적이긴 하지만, 특정 작업에 맞게 에이전트 아키텍처를 커스터마이징하면 종종 더 나은 성능을 얻을 수 있습니다. LangGraph는 맞춤형 에이전트 시스템을 구축하기 위한 여러 강력한 기능을 제공합니다.

인간 개입: 신중함이 필요한 순간

인간의 개입은 특히 민감한 작업에서 에이전트의 신뢰성을 크게 향상시킬 수 있습니다. 이는 여러 방식으로 이루어질 수 있습니다.

특정 행동을 승인하는 것부터 시작할 수 있습니다. 예를 들어 중요한 이메일을 보내기 전에 사람의 검토를 받는 것입니다. 에이전트의 상태를 업데이트하기 위한 피드백을 제공할 수도 있습니다. 복잡한 의사결정 과정에서 가이던스를 제공하는 것도 중요한 역할입니다.

완전한 자동화가 불가능하거나 바람직하지 않을 때 인간 개입 패턴은 매우 중요합니다. 특히 금융, 의료, 법률 등의 분야에서는 최종 결정에 반드시 인간의 판단이 필요한 경우가 많습니다.

병렬화: 효율성의 극대화

병렬 처리는 효율적인 멀티 에이전트 시스템과 복잡한 작업에 필수적입니다. LangGraph는 Send API를 통해 병렬화를 지원합니다.

이를 통해 여러 상태의 동시 처리가 가능합니다. 여러 데이터 소스에서 동시에 정보를 수집하거나, 여러 에이전트가 각각 다른 부분을 담당해서 작업할 수 있습니다.

맵-리듀스와 같은 연산의 구현도 가능합니다. 큰 문제를 작은 부분들로 나누어 각각 처리한 후, 결과를 합치는 방식입니다.

독립적인 하위 작업들의 효율적인 처리도 병렬화의 중요한 용도입니다. 서로 의존하지 않는 작업들은 동시에 실행해서 전체 처리 시간을 단축할 수 있습니다.

서브그래프: 복잡성 관리의 핵심

서브그래프는 복잡한 에이전트 아키텍처, 특히 멀티 에이전트 시스템을 관리하는 데 필수적입니다. 서브그래프를 사용하면 여러 가지 중요한 이점을 얻을 수 있습니다.

독립적인 상태 관리

각 에이전트마다 독립적인 상태 관리가 가능합니다. 이는 마치 회사에서 부서별로 독립적인 업무 관리 시스템을 가지는 것과 같습니다. 각 부서는 자신만의 정보와 프로세스를 관리하지만, 필요할 때는 다른 부서와 소통할 수 있습니다.

예를 들어 고객 서비스 시스템에서 주문 처리 에이전트는 주문 상태와 재고 정보를 관리하고, 결제 처리 에이전트는 결제 정보와 보안 검증을 담당할 수 있습니다. 각각은 독립적으로 작동하지만 필요한 정보는 공유합니다.

계층적 에이전트 팀 구성

서브그래프를 통해 에이전트 팀을 계층적으로 구성할 수 있습니다. 상위 관리자 에이전트가 여러 전문가 에이전트들을 관리하는 구조를 만들 수 있는 것입니다.

예를 들어 연구 보고서 작성 시스템에서 메인 에이전트가 전체 프로젝트를 관리하고, 데이터 수집 전문 에이전트, 분석 전문 에이전트, 글쓰기 전문 에이전트들이 각각의 전문 분야를 담당하는 구조를 만들 수 있습니다.

제어된 에이전트 간 소통

서브그래프는 상태 스키마의 겹치는 키를 통해 부모 그래프와 소통합니다. 이는 에이전트들 간의 소통을 체계적이고 안전하게 만듭니다.

마치 API처럼 작동합니다. 각 서브그래프는 정해진 인터페이스를 통해서만 외부와 소통하므로, 전체 시스템의 안정성과 예측 가능성이 향상됩니다.

반성: 자기 개선의 메커니즘

반성 메커니즘은 에이전트의 신뢰성을 크게 향상시킬 수 있습니다. 이는 여러 방식으로 작동합니다.

작업 완료도와 정확성을 평가하는 것부터 시작합니다. 에이전트가 자신의 작업 결과를 스스로 검토하고 개선점을 찾는 것입니다.

반복적인 개선을 위한 피드백을 제공하는 것도 중요합니다. 첫 번째 시도에서 완벽하지 않았다면, 무엇이 부족했는지 분석하고 다시 시도할 수 있습니다.

자기 교정과 학습을 가능하게 하는 것이 반성의 궁극적인 목표입니다. 에이전트가 경험을 통해 점점 더 나아질 수 있게 하는 것입니다.

반성은 주로 LLM 기반이지만, 결정론적 방법도 사용할 수 있습니다. 예를 들어 코딩 작업에서는 컴파일 오류가 피드백 역할을 할 수 있습니다. 에이전트가 코드를 작성하고, 컴파일해보고, 오류가 있으면 수정하는 방식으로 자기 교정이 가능합니다.

실제 구현에서의 고려사항

이론적인 개념들을 실제로 구현할 때는 몇 가지 중요한 고려사항이 있습니다.

복잡성과 성능의 균형

더 복잡한 아키텍처가 항상 더 좋은 것은 아닙니다. 문제의 복잡성에 맞는 적절한 수준의 아키텍처를 선택해야 합니다. 간단한 분류 작업에는 라우터로 충분하고, 복잡한 멀티스텝 문제 해결에는 서브그래프를 활용한 멀티 에이전트 시스템이 필요할 수 있습니다.

디버깅과 모니터링

복잡한 에이전트 시스템에서는 디버깅이 어려울 수 있습니다. 각 단계에서 무엇이 일어나고 있는지 추적할 수 있는 로깅과 모니터링 시스템이 필요합니다. LangGraph는 이를 위한 도구들을 제공하지만, 실제 프로덕션 환경에서는 추가적인 관찰 가능성 도구들이 필요할 수 있습니다.

비용과 지연시간 관리

복잡한 에이전트 시스템은 많은 LLM 호출을 수반하므로 비용이 높아질 수 있습니다. 또한 여러 단계를 거치면서 지연시간도 늘어납니다. 이를 관리하기 위해 캐싱, 배치 처리, 그리고 적절한 모델 선택이 중요합니다.

시작하는 방법

이런 복잡한 개념들을 처음 접하는 분들을 위한 학습 로드맵을 제안해드립니다.

먼저 간단한 라우터부터 시작하세요. 사용자 입력을 몇 개의 카테고리로 분류하는 간단한 시스템을 만들어보세요.

다음으로 기본적인 도구 호출 에이전트를 구현해보세요. 계산기나 웹 검색 같은 간단한 도구를 사용하는 에이전트부터 시작하는 것이 좋습니다.

메모리 기능을 추가해보세요. 에이전트가 이전 대화를 기억하고 활용할 수 있게 만들어보세요.

병렬 처리를 실험해보세요. 여러 작업을 동시에 실행하고 결과를 합치는 시스템을 만들어보세요.

마지막으로 서브그래프를 활용한 멀티 에이전트 시스템을 구축해보세요. 각각 다른 전문성을 가진 에이전트들이 협력하는 시스템을 만들어보는 것입니다.

마무리

LangGraph의 서브그래프와 다양한 에이전트 아키텍처는 복잡한 AI 시스템을 체계적으로 관리할 수 있는 강력한 도구들입니다. 단순한 라우터부터 복잡한 멀티 에이전트 시스템까지, 문제의 복잡성에 맞는 적절한 아키텍처를 선택하는 것이 중요합니다.

핵심은 단계적으로 접근하는 것입니다. 처음부터 복잡한 시스템을 만들려고 하지 말고, 간단한 것부터 시작해서 점진적으로 기능을 추가해나가세요. 각 단계에서 충분히 테스트하고 이해한 후에 다음 단계로 넘어가는 것이 성공의 비결입니다.

 

Leave a Comment