원문: Building Effective AI Agents

2024년 12월 19일 게시

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

이 글에서는 고객들과 협력하고 우리 스스로 에이전트를 구축하며 배운 내용을 공유하고, 효과적인 에이전트 구축에 관한 실용적인 조언을 개발자들에게 제공합니다.

에이전트란 무엇인가?

“에이전트”는 여러 방식으로 정의될 수 있습니다. 일부 고객은 에이전트를 장기간 독립적으로 작동하며 다양한 도구를 사용하여 복잡한 작업을 수행하는 완전 자율 시스템으로 정의합니다. 다른 이들은 미리 정의된 워크플로우를 따르는 더 처방적인 구현을 설명하기 위해 이 용어를 사용합니다. Anthropic에서는 이러한 모든 변형을 에이전틱 시스템으로 분류하지만, 워크플로우에이전트 간에 중요한 아키텍처적 구분을 둡니다:

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

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

에이전트를 사용해야 할 때(그리고 사용하지 말아야 할 때)

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

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

프레임워크를 사용해야 할 때와 방법

에이전틱 시스템을 더 쉽게 구현할 수 있도록 돕는 많은 프레임워크가 있습니다:

이러한 프레임워크는 LLM 호출, 도구 정의 및 파싱, 호출 연결과 같은 표준 저수준 작업을 단순화하여 시작을 쉽게 만듭니다. 그러나 종종 추가적인 추상화 레이어를 생성하여 기본 프롬프트와 응답을 불명확하게 만들어 디버깅을 어렵게 할 수 있습니다. 또한 더 단순한 설정으로 충분할 때 복잡성을 추가하고 싶은 유혹을 받을 수 있습니다.

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

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

빌딩 블록, 워크플로우, 그리고 에이전트

이 섹션에서는 프로덕션 환경에서 본 에이전틱 시스템의 일반적인 패턴을 살펴보겠습니다. 기본 빌딩 블록인 증강 LLM부터 시작하여, 단순한 조합 워크플로우에서 자율 에이전트까지 점진적으로 복잡성을 증가시켜 나가겠습니다.

빌딩 블록: 증강 LLM

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

증강 LLM

구현의 두 가지 핵심 측면에 집중할 것을 권장합니다: 이러한 기능을 특정 사용 사례에 맞게 조정하고 LLM에 쉽고 잘 문서화된 인터페이스를 제공하는 것입니다. 이러한 증강을 구현하는 방법은 여러 가지가 있지만, 한 가지 접근법은 최근 출시된 Model Context Protocol을 통한 것입니다. 이를 통해 개발자는 간단한 클라이언트 구현으로 성장하는 서드파티 도구 생태계와 통합할 수 있습니다.

이 글의 나머지 부분에서는 각 LLM 호출이 이러한 증강 기능에 접근할 수 있다고 가정하겠습니다.

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

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

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

이 워크플로우를 사용해야 할 때: 이 워크플로우는 작업을 고정된 하위 작업으로 쉽고 깔끔하게 분해할 수 있는 상황에 이상적입니다. 주요 목표는 각 LLM 호출을 더 쉬운 작업으로 만들어 지연 시간을 더 높은 정확도로 절충하는 것입니다.

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

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

워크플로우: 라우팅

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

라우팅 워크플로우

이 워크플로우를 사용해야 할 때: 라우팅은 별도로 처리하는 것이 더 나은 뚜렷한 카테고리가 있고, LLM이나 더 전통적인 분류 모델/알고리즘으로 분류를 정확하게 처리할 수 있는 복잡한 작업에 적합합니다.

라우팅이 유용한 예:

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

워크플로우: 병렬화

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

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

병렬화 워크플로우

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

병렬화가 유용한 예:

  • 섹셔닝:
    • 한 모델 인스턴스가 사용자 쿼리를 처리하는 동안 다른 인스턴스가 부적절한 콘텐츠나 요청을 검사하는 가드레일 구현하기. 이는 동일한 LLM 호출이 가드레일과 핵심 응답을 모두 처리하는 것보다 더 나은 성능을 보이는 경향이 있습니다.
    • LLM 성능 평가를 위한 자동화된 평가에서, 각 LLM 호출이 주어진 프롬프트에 대한 모델 성능의 다른 측면을 평가하기.
  • 투표:
    • 취약점에 대한 코드 검토에서, 여러 다른 프롬프트가 코드를 검토하고 문제를 발견하면 플래그를 지정하기.
    • 주어진 콘텐츠가 부적절한지 평가하는 데 있어, 여러 프롬프트가 다양한 측면을 평가하거나 거짓 양성과 거짓 음성의 균형을 맞추기 위해 다른 투표 임계값을 요구하기.

워크플로우: 오케스트레이터-워커

오케스트레이터-워커 워크플로우에서, 중앙 LLM이 동적으로 작업을 분해하고, 워커 LLM에 위임하며, 그 결과를 합성합니다.

오케스트레이터-워커 워크플로우

이 워크플로우를 사용해야 할 때: 이 워크플로우는 필요한 하위 작업을 예측할 수 없는 복잡한 작업에 적합합니다(예를 들어, 코딩에서 변경해야 할 파일의 수와 각 파일의 변경 특성은 작업에 따라 달라질 가능성이 높습니다). 위상적으로 유사하지만, 병렬화와의 주요 차이점은 유연성입니다—하위 작업이 미리 정의되지 않고 특정 입력에 따라 오케스트레이터가 결정합니다.

오케스트레이터-워커가 유용한 예:

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

워크플로우: 평가자-옵티마이저

평가자(Evaluator)-옵티마이저 워크플로우에서, 한 LLM 호출이 응답을 생성하는 동안 다른 호출이 루프에서 평가와 피드백을 제공합니다.

평가자-옵티마이저 워크플로우

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

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

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

에이전트

에이전트는 LLM이 핵심 역량에서 성숙해짐에 따라 프로덕션 환경에서 등장하고 있습니다—복잡한 입력 이해, 추론 및 계획 참여, 도구를 안정적으로 사용, 오류로부터 복구. 에이전트는 사람 사용자의 명령 또는 대화형 토론으로 작업을 시작합니다. 작업이 명확해지면, 에이전트는 독립적으로 계획하고 작동하며, 잠재적으로 추가 정보나 판단을 위해 사람에게 돌아갑니다. 실행 중에, 에이전트가 각 단계에서 환경으로부터 “근거 진실”을 얻는 것이 중요합니다(도구 호출 결과나 코드 실행 등) 진행 상황을 평가하기 위해. 그런 다음 에이전트는 체크포인트나 차단 요소를 만났을 때 사람의 피드백을 위해 일시 중지할 수 있습니다. 작업은 종종 완료 시 종료되지만, 제어를 유지하기 위해 중지 조건(예: 최대 반복 횟수)을 포함하는 것도 일반적입니다.

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

자율 에이전트

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

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

에이전트가 유용한 예:

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

코딩 에이전트의 고수준 흐름

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

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

요약

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

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

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

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

감사의 말

Erik Schluntz와 Barry Zhang이 작성했습니다. 이 작업은 Anthropic에서 에이전트를 구축한 우리의 경험과 고객들이 공유한 귀중한 통찰력을 바탕으로 하며, 이에 대해 깊이 감사드립니다.

부록 1: 실제 에이전트

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

A. 고객 지원

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

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

여러 회사가 성공적인 해결에 대해서만 요금을 부과하는 사용량 기반 가격 모델을 통해 이러한 접근 방식의 타당성을 입증했으며, 이는 에이전트의 효과성에 대한 확신을 보여줍니다.

B. 코딩 에이전트

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

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

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

구축하는 에이전틱 시스템이 무엇이든 간에, 도구는 에이전트의 중요한 부분일 가능성이 높습니다. 도구는 Claude가 API에서 정확한 구조와 정의를 지정하여 외부 서비스 및 API와 상호 작용할 수 있게 합니다. Claude가 응답할 때, 도구를 호출할 계획이라면 API 응답에 도구 사용 블록을 포함합니다. 도구 정의와 사양은 전체 프롬프트만큼이나 프롬프트 엔지니어링 주의를 기울여야 합니다. 이 간단한 부록에서는 도구를 프롬프트 엔지니어링하는 방법을 설명합니다.

동일한 작업을 지정하는 방법은 종종 여러 가지가 있습니다. 예를 들어, diff를 작성하거나 전체 파일을 다시 작성하여 파일 편집을 지정할 수 있습니다. 구조화된 출력의 경우, 마크다운 내부 또는 JSON 내부에 코드를 반환할 수 있습니다. 소프트웨어 엔지니어링에서 이러한 차이는 외관상의 것이며 하나에서 다른 것으로 무손실로 변환될 수 있습니다. 그러나 일부 형식은 LLM이 작성하기 훨씬 더 어렵습니다. diff를 작성하려면 새 코드가 작성되기 전에 청크 헤더에서 변경되는 줄 수를 알아야 합니다. JSON 내부에 코드를 작성하려면(마크다운과 비교하여) 개행과 따옴표를 추가로 이스케이프해야 합니다.

도구 형식을 결정하기 위한 우리의 제안은 다음과 같습니다:

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

한 가지 경험 법칙은 인간-컴퓨터 인터페이스(HCI)에 얼마나 많은 노력을 기울이는지 생각하고, 좋은 에이전트-컴퓨터 인터페이스(ACI)를 만드는 데 그만큼의 노력을 투자할 계획을 세우는 것입니다. 다음은 그렇게 하는 방법에 대한 몇 가지 생각입니다:

  • 모델의 입장이 되어보세요. 설명과 매개변수를 기반으로 이 도구를 사용하는 방법이 명확합니까, 아니면 신중하게 생각해야 합니까? 그렇다면 모델에게도 아마 그럴 것입니다. 좋은 도구 정의는 종종 사용 예시, 엣지 케이스, 입력 형식 요구 사항, 다른 도구와의 명확한 경계를 포함합니다.
  • 매개변수 이름이나 설명을 어떻게 변경하여 더 명확하게 만들 수 있습니까? 이것을 팀의 주니어 개발자를 위한 훌륭한 docstring을 작성하는 것으로 생각하세요. 이는 많은 유사한 도구를 사용할 때 특히 중요합니다.
  • 모델이 도구를 어떻게 사용하는지 테스트하세요: 우리의 워크벤치에서 많은 예제 입력을 실행하여 모델이 어떤 실수를 하는지 확인하고 반복하세요.
  • 도구를 포카-요케하세요. 실수하기 어렵도록 인수를 변경하세요.

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