효과적인 AI 에이전트 구축하기

원문: Building Effective Agents by Erik Schluntz and Barry Zhang

개요

지난 1년간 우리는 다양한 산업 분야에서 대규모 언어 모델(LLM) 에이전트를 구축하는 수십 개 팀과 협업했습니다. 일관되게, 가장 성공적인 구현은 복잡한 프레임워크나 특수 라이브러리를 사용하지 않았습니다. 대신 단순하고 조합 가능한 패턴으로 구축했습니다.

이 포스트에서는 고객과 협업하고 직접 에이전트를 구축하면서 배운 것을 공유하며, 개발자가 효과적인 에이전트를 구축하는 데 도움이 되는 실용적인 조언을 제공합니다.

에이전트란 무엇인가?

에이전트”는 여러 방식으로 정의될 수 있습니다. 일부 고객은 에이전트를 다양한 도구를 사용하여 복잡한 작업을 수행하고, 장기간 독립적으로 작동하는 완전 자율 시스템으로 정의합니다. 다른 고객은 미리 정의된 워크플로우를 따르는 보다 규범적인 구현을 설명하는 데 이 용어를 사용합니다.

Anthropic에서는 이러한 모든 변형을 **에이전트 시스템(agentic systems)**으로 분류하지만, **워크플로우(workflows)**와 에이전트(agents) 사이에 중요한 아키텍처 구분을 둡니다:

  • 워크플로우: LLM과 도구가 미리 정의된 코드 경로를 통해 조율되는 시스템
  • 에이전트: LLM이 자체 프로세스와 도구 사용을 동적으로 지시하고, 작업 수행 방법에 대한 제어권을 유지하는 시스템

아래에서는 두 가지 유형의 에이전트 시스템을 자세히 살펴보겠습니다. 부록 1(“실무에서의 에이전트”)에서는 고객이 이러한 시스템 사용에서 특히 가치를 발견한 두 가지 영역을 설명합니다.

관련 문서

LangGraph 관점의 워크플로우와 에이전트 구분에 대해서는 워크플로우와 에이전트 패턴을 참조하세요.

에이전트를 사용할 때와 사용하지 말아야 할 때

LLM으로 애플리케이션을 구축할 때는 가능한 한 가장 단순한 솔루션을 찾고, 필요할 때만 복잡성을 높이는 것을 권장합니다. 이는 에이전트 시스템을 전혀 구축하지 않는 것을 의미할 수도 있습니다. 에이전트 시스템은 종종 지연 시간과 비용을 희생하여 더 나은 작업 성능을 얻습니다. 이러한 트레이드오프가 의미 있는 경우를 고려해야 합니다.

더 많은 복잡성이 필요한 경우, 워크플로우는 잘 정의된 작업에 대한 예측 가능성과 일관성을 제공하는 반면, 에이전트는 유연성과 모델 기반 의사 결정이 대규모로 필요할 때 더 나은 옵션입니다. 그러나 많은 애플리케이션의 경우 검색 및 컨텍스트 내 예제를 사용한 단일 LLM 호출 최적화만으로도 충분합니다.

프레임워크 사용 시기와 방법

에이전트 시스템 구현을 쉽게 만드는 많은 프레임워크가 있습니다:

  • LangChain의 LangGraph
  • Amazon Bedrock의 AI Agent 프레임워크
  • Rivet (드래그 앤 드롭 GUI LLM 워크플로우 빌더)
  • Vellum (복잡한 워크플로우 구축 및 테스트를 위한 GUI 도구)

이러한 프레임워크는 LLM 호출, 도구 정의 및 파싱, 호출 체이닝과 같은 표준 저수준 작업을 단순화하여 쉽게 시작할 수 있게 합니다. 그러나 이들은 종종 기본 프롬프트와 응답을 가리는 추가 추상화 계층을 만들어 디버깅을 어렵게 만듭니다. 또한 더 간단한 설정으로 충분할 때 복잡성을 추가하도록 유혹할 수 있습니다.

개발자는 LLM API를 직접 사용하여 시작하는 것을 권장합니다: 많은 패턴을 몇 줄의 코드로 구현할 수 있습니다. 프레임워크를 사용하는 경우 기본 코드를 이해했는지 확인하세요. 내부 작동에 대한 잘못된 가정은 일반적인 고객 오류의 원인입니다.

샘플 구현은 쿡북을 참조하세요.

구성 요소, 워크플로우, 에이전트

이 섹션에서는 프로덕션에서 본 에이전트 시스템의 일반적인 패턴을 살펴봅니다. 기본 구성 요소인 증강된 LLM부터 시작하여 단순한 조합 워크플로우에서 자율 에이전트까지 점진적으로 복잡성을 높여갑니다.

구성 요소: 증강된 LLM

에이전트 시스템의 기본 구성 요소는 검색, 도구, 메모리와 같은 증강 기능으로 향상된 LLM입니다. 현재 모델은 이러한 기능을 능동적으로 사용할 수 있습니다—자체 검색 쿼리를 생성하고, 적절한 도구를 선택하며, 유지할 정보를 결정합니다.

구현의 두 가지 핵심 측면에 집중하는 것을 권장합니다:

  1. 특정 사용 사례에 맞게 이러한 기능을 조정하기
  2. LLM을 위한 쉽고 잘 문서화된 인터페이스 제공하기

이러한 증강을 구현하는 방법은 많지만, 한 가지 접근 방식은 최근 출시된 Model Context Protocol을 통하는 것입니다. 이를 통해 개발자는 간단한 클라이언트 구현으로 증가하는 타사 도구 생태계와 통합할 수 있습니다.

이 포스트의 나머지 부분에서는 각 LLM 호출이 이러한 증강 기능에 액세스할 수 있다고 가정합니다.

워크플로우: 프롬프트 체이닝

프롬프트 체이닝은 작업을 일련의 단계로 분해하며, 각 LLM 호출은 이전 호출의 출력을 처리합니다. 중간 단계에서 프로그래밍 방식의 검사(“게이트”)를 추가하여 프로세스가 제대로 진행되고 있는지 확인할 수 있습니다.

적용 시기: 작업을 고정된 하위 작업으로 쉽고 명확하게 분해할 수 있는 상황에 이상적입니다. 주요 목표는 각 LLM 호출을 더 쉬운 작업으로 만들어 지연 시간을 희생하고 정확도를 높이는 것입니다.

프롬프트 체이닝이 유용한 예:

  • 마케팅 카피를 생성한 다음 다른 언어로 번역하기
  • 문서 개요를 작성하고, 개요가 특정 기준을 충족하는지 확인한 다음, 개요를 기반으로 문서 작성하기

LangGraph에서의 구현

LangGraph에서 프롬프트 체이닝 패턴을 구현하는 방법은 워크플로우와 에이전트 패턴 - 프롬프트 체이닝을 참조하세요.

워크플로우: 라우팅

라우팅은 입력을 분류하고 특화된 후속 작업으로 안내합니다. 이 워크플로우는 관심사의 분리와 더 특화된 프롬프트 구축을 가능하게 합니다. 이 워크플로우가 없다면 한 종류의 입력에 대한 최적화가 다른 입력의 성능을 저하시킬 수 있습니다.

적용 시기: 별도로 처리하는 것이 더 나은 명확한 카테고리가 있고, LLM이나 전통적인 분류 모델/알고리즘으로 분류를 정확하게 처리할 수 있는 복잡한 작업에 적합합니다.

라우팅이 유용한 예:

  • 다양한 유형의 고객 서비스 쿼리(일반 질문, 환불 요청, 기술 지원)를 서로 다른 다운스트림 프로세스, 프롬프트, 도구로 안내하기
  • 쉬운/일반적인 질문은 Claude Haiku 4.5와 같은 소형의 비용 효율적인 모델로, 어렵거나 특이한 질문은 Claude Sonnet 4.5와 같은 더 강력한 모델로 라우팅하여 최상의 성능 최적화하기

LangGraph에서의 구현

LangGraph에서 라우팅 패턴을 구현하는 방법은 워크플로우와 에이전트 패턴 - 라우팅에이전트 아키텍처 개념 - 라우터를 참조하세요.

워크플로우: 병렬화

LLM은 때때로 작업에 대해 동시에 작업하고 출력을 프로그래밍 방식으로 집계할 수 있습니다. 이 워크플로우인 병렬화는 두 가지 주요 변형으로 나타납니다:

  • 섹션화(Sectioning): 작업을 병렬로 실행되는 독립적인 하위 작업으로 분할
  • 투표(Voting): 다양한 출력을 얻기 위해 동일한 작업을 여러 번 실행

적용 시기: 분할된 하위 작업을 속도를 위해 병렬화할 수 있거나, 더 높은 신뢰도를 위해 여러 관점이나 시도가 필요한 경우에 효과적입니다. 여러 고려 사항이 있는 복잡한 작업의 경우, LLM은 일반적으로 각 고려 사항이 별도의 LLM 호출로 처리될 때 더 나은 성능을 발휘하여 각 특정 측면에 집중된 주의를 기울일 수 있습니다.

병렬화가 유용한 예:

섹션화:

  • 가드레일 구현: 한 모델 인스턴스가 사용자 쿼리를 처리하는 동안 다른 인스턴스가 부적절한 콘텐츠나 요청을 선별합니다. 이는 동일한 LLM 호출이 가드레일과 핵심 응답을 모두 처리하는 것보다 더 나은 성능을 발휘하는 경향이 있습니다.
  • LLM 성능 평가를 위한 자동화된 평가: 각 LLM 호출이 주어진 프롬프트에 대한 모델 성능의 다른 측면을 평가합니다.

투표:

  • 취약점에 대한 코드 조각 검토: 여러 다른 프롬프트가 코드를 검토하고 문제를 발견하면 플래그를 지정합니다.
  • 주어진 콘텐츠가 부적절한지 평가: 여러 프롬프트가 다양한 측면을 평가하거나 거짓 긍정과 거짓 부정의 균형을 맞추기 위해 다른 투표 임계값을 요구합니다.

LangGraph에서의 구현

LangGraph에서 병렬화 패턴을 구현하는 방법은 워크플로우와 에이전트 패턴 - 병렬화를 참조하세요.

워크플로우: 조율자-작업자

조율자-작업자 워크플로우에서는 중앙 LLM이 작업을 동적으로 분해하고, 작업자 LLM에 위임하며, 결과를 종합합니다.

적용 시기: 필요한 하위 작업을 예측할 수 없는 복잡한 작업에 적합합니다(예: 코딩에서 변경해야 하는 파일 수와 각 파일의 변경 성격은 작업에 따라 달라질 수 있음). 위상적으로는 병렬화와 유사하지만, 주요 차이점은 유연성입니다. 하위 작업이 미리 정의되지 않고 특정 입력에 따라 조율자가 결정합니다.

조율자-작업자가 유용한 예:

  • 매번 여러 파일에 복잡한 변경을 수행하는 코딩 제품
  • 가능한 관련 정보에 대해 여러 소스에서 정보를 수집하고 분석하는 검색 작업

LangGraph에서의 구현

LangGraph에서 조율자-작업자 패턴을 구현하는 방법은 워크플로우와 에이전트 패턴 - 조율자-작업자를 참조하세요. 멀티 에이전트 시스템 관점에서도 이 패턴을 이해할 수 있습니다.

워크플로우: 평가자-최적화자

평가자-최적화자 워크플로우에서는 한 LLM 호출이 응답을 생성하고 다른 LLM이 루프에서 평가와 피드백을 제공합니다.

적용 시기: 명확한 평가 기준이 있고 반복적인 개선이 측정 가능한 가치를 제공할 때 특히 효과적입니다. 적합성의 두 가지 신호는: 첫째, 사람이 피드백을 표현할 때 LLM 응답이 명백히 개선될 수 있으며, 둘째, LLM이 그러한 피드백을 제공할 수 있다는 것입니다. 이는 사람 작가가 세련된 문서를 작성할 때 거치는 반복적인 작성 프로세스와 유사합니다.

평가자-최적화자가 유용한 예:

  • 번역 LLM이 처음에는 포착하지 못할 수 있는 뉘앙스가 있지만 평가자 LLM이 유용한 비평을 제공할 수 있는 문학 번역
  • 포괄적인 정보를 수집하기 위해 여러 라운드의 검색 및 분석이 필요한 복잡한 검색 작업(평가자가 추가 검색이 필요한지 결정)

LangGraph에서의 구현

LangGraph에서 평가자-최적화자 패턴을 구현하는 방법은 워크플로우와 에이전트 패턴 - 평가자-최적화자를 참조하세요.

에이전트

에이전트는 LLM이 복잡한 입력 이해, 추론 및 계획, 도구의 안정적 사용, 오류 복구와 같은 핵심 기능에서 성숙해짐에 따라 프로덕션에서 등장하고 있습니다. 에이전트는 사용자로부터의 명령이나 대화형 토론으로 작업을 시작합니다. 작업이 명확해지면 에이전트는 독립적으로 계획하고 작동하며, 필요에 따라 추가 정보나 판단을 위해 사용자에게 돌아갑니다.

실행 중에 에이전트가 각 단계에서 환경으로부터 “실측 정보”(도구 호출 결과나 코드 실행 등)를 얻어 진행 상황을 평가하는 것이 중요합니다. 에이전트는 체크포인트에서 또는 장애물을 만날 때 사람의 피드백을 위해 일시 중지할 수 있습니다. 작업은 종종 완료 시 종료되지만, 제어를 유지하기 위해 중지 조건(최대 반복 횟수 등)을 포함하는 것도 일반적입니다.

에이전트는 정교한 작업을 처리할 수 있지만 구현은 종종 간단합니다. 일반적으로 루프에서 환경 피드백을 기반으로 도구를 사용하는 LLM일 뿐입니다. 따라서 도구 세트와 문서를 명확하고 신중하게 설계하는 것이 중요합니다. 부록 2(“도구의 프롬프트 엔지니어링”)에서 도구 개발 모범 사례를 확장합니다.

에이전트 사용 시기: 필요한 단계 수를 예측하기 어렵거나 불가능한 개방형 문제에 사용할 수 있으며, 고정된 경로를 하드코딩할 수 없습니다. LLM은 잠재적으로 많은 턴 동안 작동하며, 의사 결정에 어느 정도 신뢰가 있어야 합니다. 에이전트의 자율성은 신뢰할 수 있는 환경에서 작업을 확장하는 데 이상적입니다.

에이전트의 자율적 특성은 더 높은 비용과 오류 누적 가능성을 의미합니다. 적절한 가드레일과 함께 샌드박스 환경에서 광범위한 테스트를 권장합니다.

에이전트가 유용한 예:

다음 예는 우리 자체 구현에서 가져온 것입니다:

  • 작업 설명을 기반으로 많은 파일을 편집하는 SWE-bench 작업을 해결하는 코딩 에이전트
  • Claude가 컴퓨터를 사용하여 작업을 수행하는 “computer use” 참조 구현

LangGraph에서의 구현

이러한 패턴 결합 및 사용자 정의

이러한 구성 요소는 규범적이지 않습니다. 개발자가 다양한 사용 사례에 맞게 형성하고 결합할 수 있는 일반적인 패턴입니다. 다른 LLM 기능과 마찬가지로 성공의 열쇠는 성능을 측정하고 구현을 반복하는 것입니다. 반복하자면: 복잡성이 결과를 명백히 개선할 때만 추가하는 것을 고려해야 합니다.

요약

LLM 분야에서 성공은 가장 정교한 시스템을 구축하는 것이 아닙니다. 필요에 맞는 올바른 시스템을 구축하는 것입니다. 단순한 프롬프트로 시작하고, 포괄적인 평가로 최적화하며, 더 간단한 솔루션이 부족할 때만 다단계 에이전트 시스템을 추가하세요.

에이전트를 구현할 때 세 가지 핵심 원칙을 따르려고 노력합니다:

  1. 에이전트 설계의 단순성(simplicity) 유지
  2. 에이전트의 계획 단계를 명시적으로 보여 투명성(transparency) 우선순위화
  3. 철저한 도구 문서화 및 테스트를 통해 에이전트-컴퓨터 인터페이스(ACI) 신중하게 제작

프레임워크는 빠르게 시작하는 데 도움이 될 수 있지만, 프로덕션으로 이동할 때 추상화 계층을 줄이고 기본 구성 요소로 구축하는 것을 주저하지 마세요. 이러한 원칙을 따르면 강력할 뿐만 아니라 신뢰할 수 있고, 유지 관리 가능하며, 사용자가 신뢰할 수 있는 에이전트를 만들 수 있습니다.

부록 1: 실무에서의 에이전트

고객과의 작업을 통해 위에서 논의한 패턴의 실용적 가치를 보여주는 AI 에이전트에 대한 두 가지 특히 유망한 애플리케이션을 발견했습니다. 두 애플리케이션 모두 대화와 행동을 모두 필요로 하고, 명확한 성공 기준이 있으며, 피드백 루프를 가능하게 하고, 의미 있는 인간 감독을 통합하는 작업에 에이전트가 가장 많은 가치를 추가한다는 것을 보여줍니다.

A. 고객 지원

고객 지원은 친숙한 챗봇 인터페이스와 도구 통합을 통한 향상된 기능을 결합합니다. 이는 다음과 같은 이유로 보다 개방형 에이전트에 자연스럽게 적합합니다:

  • 지원 상호 작용은 외부 정보 및 작업에 대한 액세스를 요구하면서 자연스럽게 대화 흐름을 따릅니다
  • 고객 데이터, 주문 기록, 지식 베이스 문서를 가져오기 위해 도구를 통합할 수 있습니다
  • 환불 발급이나 티켓 업데이트와 같은 작업을 프로그래밍 방식으로 처리할 수 있습니다
  • 성공은 사용자 정의 해결을 통해 명확하게 측정할 수 있습니다

여러 회사가 성공적인 해결에 대해서만 요금을 부과하는 사용 기반 가격 모델을 통해 이 접근 방식의 실행 가능성을 입증하여 에이전트의 효과에 대한 확신을 보여주었습니다.

B. 코딩 에이전트

소프트웨어 개발 공간은 코드 완성에서 자율적 문제 해결로 발전하는 기능과 함께 LLM 기능에 대한 놀라운 잠재력을 보여주었습니다. 에이전트는 다음과 같은 이유로 특히 효과적입니다:

  • 코드 솔루션은 자동화된 테스트를 통해 검증 가능합니다
  • 에이전트는 테스트 결과를 피드백으로 사용하여 솔루션을 반복할 수 있습니다
  • 문제 공간이 잘 정의되고 구조화되어 있습니다
  • 출력 품질을 객관적으로 측정할 수 있습니다

우리 자체 구현에서 에이전트는 이제 풀 리퀘스트 설명만을 기반으로 SWE-bench Verified 벤치마크에서 실제 GitHub 이슈를 해결할 수 있습니다. 그러나 자동화된 테스트가 기능을 검증하는 데 도움이 되지만, 솔루션이 더 넓은 시스템 요구 사항과 일치하는지 확인하기 위해서는 인간의 검토가 여전히 중요합니다.

부록 2: 도구의 프롬프트 엔지니어링

어떤 에이전트 시스템을 구축하든 도구는 에이전트의 중요한 부분이 될 것입니다. 도구를 사용하면 Claude가 API에서 정확한 구조와 정의를 지정하여 외부 서비스 및 API와 상호 작용할 수 있습니다. Claude가 응답할 때 도구를 호출할 계획이라면 API 응답에 도구 사용 블록이 포함됩니다.

도구 정의 및 사양은 전체 프롬프트만큼 프롬프트 엔지니어링에 주의를 기울여야 합니다. 이 간단한 부록에서는 도구를 프롬프트 엔지니어링하는 방법을 설명합니다.

동일한 작업을 지정하는 방법은 여러 가지가 있는 경우가 많습니다. 예를 들어, diff를 작성하거나 전체 파일을 다시 작성하여 파일 편집을 지정할 수 있습니다. 구조화된 출력의 경우 마크다운 또는 JSON 내부에 코드를 반환할 수 있습니다. 소프트웨어 엔지니어링에서 이러한 차이는 표면적이며 서로 무손실로 변환할 수 있습니다. 그러나 일부 형식은 LLM이 작성하기에 훨씬 더 어렵습니다.

diff를 작성하려면 새 코드가 작성되기 전에 청크 헤더에서 변경되는 줄 수를 알아야 합니다. JSON 내부에 코드를 작성하려면(마크다운과 비교하여) 줄바꿈 및 따옴표의 추가 이스케이프가 필요합니다.

도구 형식 결정에 대한 제안

  • 모델이 궁지에 몰리기 전에 “생각”할 수 있는 충분한 토큰을 제공하세요
  • 형식을 인터넷의 텍스트에서 자연스럽게 발생하는 모델이 본 것과 가깝게 유지하세요
  • 수천 줄의 코드를 정확하게 세거나 작성하는 모든 코드를 문자열 이스케이프하는 것과 같은 형식 “오버헤드”가 없는지 확인하세요

에이전트-컴퓨터 인터페이스(ACI) 설계

한 가지 경험 법칙은 인간-컴퓨터 인터페이스(HCI)에 얼마나 많은 노력이 들어가는지 생각하고, 좋은 **에이전트-컴퓨터 인터페이스(ACI)**를 만드는 데 똑같은 노력을 투자할 계획을 세우는 것입니다.

좋은 ACI를 만드는 방법:

  1. 모델의 입장이 되어보세요: 설명과 매개변수를 기반으로 이 도구를 사용하는 방법이 명확한가요, 아니면 신중하게 생각해야 하나요? 그렇다면 모델에게도 마찬가지일 것입니다. 좋은 도구 정의에는 종종 사용 예제, 엣지 케이스, 입력 형식 요구 사항, 다른 도구와의 명확한 경계가 포함됩니다.

  2. 매개변수 이름이나 설명을 어떻게 변경하여 더 명확하게 만들 수 있나요? 이것을 팀의 주니어 개발자를 위한 훌륭한 docstring을 작성하는 것으로 생각하세요. 이는 많은 유사한 도구를 사용할 때 특히 중요합니다.

  3. 모델이 도구를 어떻게 사용하는지 테스트하세요: 워크벤치에서 많은 예제 입력을 실행하여 모델이 어떤 실수를 하는지 확인하고 반복하세요.

  4. 도구를 포카요케(Poka-yoke) 하세요: 실수하기 어렵게 인수를 변경하세요.

실전 예: SWE-bench 에이전트

SWE-bench용 에이전트를 구축하는 동안 실제로 전체 프롬프트보다 도구를 최적화하는 데 더 많은 시간을 보냈습니다. 예를 들어, 에이전트가 루트 디렉터리에서 이동한 후 상대 파일 경로를 사용하는 도구에서 모델이 실수를 한다는 것을 발견했습니다. 이를 수정하기 위해 항상 절대 파일 경로를 요구하도록 도구를 변경했고, 모델이 이 방법을 완벽하게 사용한다는 것을 발견했습니다.

도구 설계 참고사항

LangGraph에서의 도구 구현에 대해서는 Tavily 검색 및 추출 도구를 참조하세요.

관련 문서

에이전트 아키텍처와 패턴

프롬프트 및 컨텍스트 엔지니어링

고급 개념