LangChain - Agent Middleware
TMTLangChain은 거의 3년 동안 에이전트 추상화를 제공해 왔습니다. 이제 동일한 핵심 추상화를 가진 에이전트 프레임워크가 아마도 수백 개는 있을 것입니다. 이들은 모두 초기 LangChain 에이전트가 겪었던 동일한 단점을 겪습니다: 필요할 때 개발자에게 컨텍스트 엔지니어링에 대한 충분한 제어 권한을 주지 못해, 개발자들이 사소하지 않은 사용 사례에서는 이 추상화를 졸업하게 만듭니다. LangChain 1.0에서는 우리가 이를 해결한다고 생각하는 새로운 에이전트 추상화(Middleware)를 도입합니다.
핵심 에이전트 구성 요소는 매우 간단합니다:
- 모델
- 프롬프트
- 도구 목록
핵심 에이전트 알고리즘도 동일하게 단순합니다. 사용자가 어떤 입력 메시지로 에이전트를 먼저 호출하면, 에이전트는 루프를 실행하며 도구를 호출하고, AI 및 도구 메시지를 상태에 추가하다가, 더 이상 도구를 호출하지 않기로 결정하고 최종적으로 종료합니다.
우리는 2022년 11월에 LangChain에 이 에이전트의 버전을 보유하고 있었고, 지난 3년 동안 수백 개의 프레임워크가 유사한 추상화와 함께 등장했습니다.
동시에, 기본적인 에이전트 추상화를 작동시키는 것은 쉽지만, 이 추상화를 프로덕션에 가져올 만큼 유연하게 만드는 것은 어렵습니다.
이 블로그에서는 다음을 다룹니다:
- 왜 이 추상화를 프로덕션에 가져오기 충분히 신뢰할 수 있게 만드는 것이 어려운지
- 지난 1년여에 걸친 더 신뢰할 수 있도록 만들기 위한 우리의 여정
- 우리가 LangChain 1.0에서 도입하는 새로운 Middleware 추상화, 이것이 가장 유연하고 합성 가능한 에이전트 추상화라고 생각하는 이유
왜 이 추상화를 프로덕션으로 가져오기 어려운가
그렇다면 왜 여전히 이러한 프레임워크로 신뢰할 수 있는 에이전트를 구축하는 것이 어려울까요? 왜 많은 사람들이 일정 수준의 복잡도에 도달하면 프레임워크를 떠나 맞춤 코드를 선호할까요?
답은 컨텍스트 엔지니어링입니다. 모델에 들어가는 컨텍스트가 그 결과를 결정합니다. 모델(따라서 에이전트)을 더 신뢰할 수 있게 만들기 위해서는 모델에 들어가는 것에 대해 완전한 제어를 하고 싶어집니다. 그리고 이 단순한 에이전트 상태와 단순한 에이전트 루프는 시작하기에 훌륭하지만, 에이전트 성능의 한계를 밀어붙이다 보면 그 일부를 수정하고 싶어질 가능성이 큽니다.
복잡성이 증가함에 따라 더 많은 제어를 하고 싶어질 수 있는 것들이 여러 가지 있습니다:
- 에이전트의 “상태”를 메시지 이상으로 조정하고 싶을 수 있습니다
- 모델에 정확히 무엇이 들어가는지에 대해 더 많은 제어를 하고 싶을 수 있습니다
- 실행되는 단계의 순서에 대해 더 많은 제어를 하고 싶을 수 있습니다
더 신뢰할 수 있도록 만들기 위한 우리의 여정
지난 2년 동안 우리는 에이전트 추상화가 컨텍스트 엔지니어링을 더 잘 지원하도록 작업했습니다. 우리가 한 일들(대략 순서대로):
- 사용자가 런타임 구성 지정 가능: 연결 문자열 및 읽기 전용 사용자 정보 같은 것을 전달
- 사용자가 임의의 상태 스키마 지정 가능: 사용자 또는 에이전트가 업데이트할 수 있음
- 사용자가 문자열이 아닌 프롬프트를 반환하는 함수를 지정 가능: 동적 프롬프트 허용
- 사용자가 모델에 전달되는 전체 메시지 목록을 완전히 제어하기 위해 메시지 목록을 반환하는 함수를 지정 가능
- 사용자가 “프리 모델 훅(pre model hook)”을 지정 가능: 모델 호출 전에 단계를 실행하여 상태를 업데이트하거나 다른 노드로 점프. 이는 긴 대화 요약과 같은 것을 가능하게 함.
- 사용자가 “포스트 모델 훅(post model hook)”을 지정 가능: 모델 호출 후에 단계를 실행하여 상태를 업데이트하거나 다른 노드로 점프. 이는 human-in-the-loop 및 가드레일과 같은 것을 가능하게 함.
- 각 호출에서 사용할 모델을 반환하는 함수를 사용자가 지정 가능: 동적 모델 스위칭 및 동적 도구 호출을 가능하게 함.
이는 수행되는 컨텍스트 엔지니어링에 대해 높은 수준의 사용자 정의와 제어를 가능하게 했습니다.
하지만 그 결과 에이전트에 많은 수의 파라미터가 생겼습니다. 게다가 이러한 파라미터는 종종 서로 의존성이 있어 조율하기가 어려웠습니다. 또한 이러한 파라미터의 여러 버전을 결합하거나, 시험해 볼 수 있는 완제품 변형을 제공하기도 어려웠습니다.
LangChain 1.0에서 우리가 하는 일
LangChain 1.0에서는 Middleware라는 개념을 도입하여 이 핵심 에이전트 루프를 수정하는 아이디어로 기울입니다.
핵심 에이전트 루프는 여전히 모델 노드와 도구 노드로 구성됩니다. 하지만 이제 미들웨어는 다음을 지정할 수 있습니다:
- before_model: 모델 호출 전에 실행되며, 상태를 업데이트하거나 다른 노드로 점프할 수 있습니다.
- after_model: 모델 호출 후에 실행되며, 상태를 업데이트하거나 다른 노드로 점프할 수 있습니다.
- modify_model_request: 모델 호출 전에 실행되며, 사용자에게 (해당 모델 요청에 한해) 도구, 프롬프트, 메시지 목록, 모델, 모델 설정, 출력 형식, 도구 선택을 수정할 수 있도록 합니다.
에이전트에 여러 미들웨어를 제공할 수 있습니다. 미들웨어는 웹 서버에서처럼 실행됩니다: 모델 호출로 들어가는 경로에서는 순차적으로(before_model, modify_model_request), 돌아오는 경로에서는 역순으로(after_model) 실행됩니다.
미들웨어는 사용자 정의 상태 스키마와 도구도 지정할 수 있습니다.
우리는 개발자가 시작할 수 있도록 미리 준비된 미들웨어를 제공할 것입니다. 또한 커뮤니티 미들웨어 목록을 유지하여 쉽게 접근할 수 있도록 하겠습니다. 한동안 개발자들은 LangGraph 에이전트에 플러그인할 수 있는 노드 모음(collections)을 요청해 왔습니다. 이것이 바로 그것입니다.
미들웨어는 우리의 다양한 에이전트 추상화를 통합하는 데에도 도움이 될 것입니다. 현재 우리는 supervisor, swarm, bigtool, deepagents, reflection 등용 LangGraph 에이전트를 별도로 보유하고 있습니다. 우리는 이미 미들웨어를 사용하여 이러한 아키텍처를 복제할 수 있음을 검증했습니다.
LangChain 1.0 알파에서 사용해 보기
가장 최신 LangChain 1.0 알파 릴리스(Python 및 JavaScript)에서 미들웨어를 사용해 볼 수 있습니다. 이것은 LangChain 1.0의 가장 큰 새로운 부분이므로 미들웨어에 대한 여러분의 피드백을 매우 원합니다.
이 알파 릴리스의 일부로, 우리는 세 가지 다른 미들웨어 구현(모두 내부 에이전트에서 이미 사용 중)을 출시합니다:
- Human-in-the-loop:
Middleware.after_model을 사용하여 도구 호출에 대한 human-in-the-loop 피드백을 얻기 위한 인터럽트를 손쉽게 추가하는 방법을 제공합니다. - Summarization:
Middleware.before_model을 사용하여 메시지가 특정 임계값을 초과하여 누적될 때 메시지를 요약합니다 - Anthropic Prompt Caching:
Middleware.modify_model_request를 사용하여 특별한 프롬프트 캐싱 태그를 메시지에 추가합니다.
Python에서 사용해 보기: pip install --pre -U langchain
JavaScript에서 사용해 보기: npm install langchain@next