anthropic

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

요약

앤트로픽의 경험을 바탕으로, LLM 에이전트 개발 시 복잡한 프레임워크보다 단순하고 조합 가능한 패턴에 집중하고, 워크플로우와 에이전트의 차이를 이해하여 적절한 시스템을 구축하는 실용적인 방법을 알려주는 글입니다.

인사이트

  • 복잡성을 최소화하고 단순하게 시작하는 것이 중요해요. 복잡한 프레임워크에 얽매이기보다, 단순하고 조합 가능한 패턴으로 시작하고, 오직 성능 향상이 확실할 때만 복잡성을 추가해야 해요.
  • 워크플로우와 에이전트의 차이를 명확히 이해하고 적절하게 선택하세요. 잘 정의된 작업에는 예측 가능한 워크플로우를, 유연하고 모델 기반의 의사결정이 필요할 때는 자율 에이전트가 더 나은 선택이에요.
  • 도구 설계에 프롬프트 엔지니어링만큼 공을 들여야 해요. 에이전트의 효율성을 높이려면, 도구의 출력 형식을 단순화하고 '에이전트-컴퓨터 인터페이스(ACI)' 관점에서 명확하게 설계하는 것이 아주 중요해요.

왜 중요한가

LLM 에이전트 개발은 빠르게 발전하고 있지만, 불필요한 복잡성으로 인해 개발자들이 어려움을 겪는 경우가 많아요. 이 글은 앤트로픽의 실제 경험과 고객 사례를 바탕으로, 복잡한 프레임워크 없이도 단순하고 효과적인 에이전트 시스템을 구축할 수 있는 실용적인 가이드를 제시해요. 이를 통해 개발자는 더 신뢰할 수 있고 유지보수하기 쉬운 에이전트를 만들고, 궁극적으로 시간과 비용을 절감하며 더 나은 사용자 경험을 제공할 수 있게 되죠.

개발자 뉴스레터를 받아보세요

제품 업데이트, 사용법, 커뮤니티 소식 등 다양한 정보가 매월 받은 편지함으로 배달됩니다.

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

이번 포스팅에서는 고객들과 함께 일하고 직접 에이전트를 만들면서 배운 점들을 공유하고, 효과적인 에이전트를 구축하는 개발자들을 위한 실용적인 조언을 드릴게요.

'에이전트'는 여러 가지 방식으로 정의될 수 있어요. 어떤 고객들은 에이전트를 장기간 독립적으로 작동하며 다양한 도구를 사용해 복잡한 작업을 수행하는 완전 자율 시스템으로 정의하곤 하죠. 또 다른 고객들은 미리 정의된 워크플로우를 따르는 더 규범적인 구현을 설명하는 데 이 용어를 사용하고요. 앤트로픽에서는 이 모든 변형을 에이전틱 시스템으로 분류하지만, 워크플로우에이전트 사이에는 중요한 아키텍처적 차이를 두고 있어요.

아래에서는 두 가지 유형의 에이전틱 시스템을 자세히 살펴볼 거예요. 부록 1("실제 에이전트 활용 사례")에서는 고객들이 이러한 시스템을 사용하여 특별한 가치를 찾았던 두 가지 영역을 설명해 드릴게요.

LLM으로 애플리케이션을 구축할 때는 가능한 한 가장 간단한 해결책을 찾고, 필요할 때만 복잡성을 늘리는 것을 추천해요. 심지어 에이전틱 시스템을 전혀 구축하지 않는 경우도 있을 수 있죠. 에이전틱 시스템은 종종 더 나은 작업 성능을 위해 레이턴시와 비용을 교환하는데요, 이런 트레이드오프가 언제 합리적인지 잘 고려해야 해요.

더 복잡성이 필요할 때는, 워크플로우는 잘 정의된 작업에 대해 예측 가능성과 일관성을 제공하는 반면, 에이전트는 대규모에서 유연성과 모델 기반 의사결정이 필요할 때 더 나은 선택이 될 수 있어요. 하지만 많은 애플리케이션에서는 검색 증강 생성(RAG)과 인컨텍스트 예시를 통해 단일 LLM 호출을 최적화하는 것만으로도 충분한 경우가 많아요.

에이전틱 시스템을 더 쉽게 구현할 수 있도록 돕는 많은 프레임워크들이 있는데요, 예를 들어 다음과 같아요.

  • LangChain
  • LlamaIndex
  • DSPy
  • CrewAI
  • Autogen

이런 프레임워크들은 LLM 호출, 도구 정의 및 파싱, 호출 체인 연결 같은 표준적인 저수준 작업을 단순화해서 시작하기 쉽게 만들어줘요. 하지만 종종 추가적인 추상화 계층을 생성해서 밑단의 프롬프트와 응답을 가려버리곤 하는데, 이러면 디버깅이 더 어려워질 수 있어요. 또, 더 간단한 설정으로도 충분할 때 굳이 복잡성을 추가하고 싶게 만들 수도 있죠.

저희는 개발자들이 LLM API를 직접 사용하는 것부터 시작하는 것을 제안해요. 많은 패턴이 몇 줄의 코드로 구현될 수 있거든요. 만약 프레임워크를 사용한다면, 반드시 그 밑단 코드를 이해하고 있는지 확인하세요. 내부 작동 방식에 대한 잘못된 가정은 고객 오류의 흔한 원인이에요.

몇 가지 샘플 구현은 저희 쿡북을 참고해 보세요.

이 섹션에서는 프로덕션에서 흔히 볼 수 있었던 에이전틱 시스템의 일반적인 패턴들을 살펴볼 거예요. 기본 빌딩 블록인 증강된 LLM부터 시작해서, 간단한 구성 워크플로우부터 자율 에이전트까지 점진적으로 복잡성을 늘려갈 예정이에요.

에이전틱 시스템의 기본 빌딩 블록은 검색 증강 생성(RAG), 도구, 메모리 같은 증강 기능으로 강화된 LLM이에요. 저희의 현재 모델들은 이런 기능들을 적극적으로 사용할 수 있어요. 스스로 검색 쿼리를 생성하고, 적절한 도구를 선택하며, 어떤 정보를 유지할지 결정할 수 있죠.

The augmented LLM

구현의 두 가지 핵심 측면에 집중하는 것을 추천해요. 바로, 이런 기능들을 특정 사용 사례에 맞춰 조정하고, LLM을 위한 쉽고 잘 문서화된 인터페이스를 제공하는 것이죠. 이러한 증강 기능들을 구현하는 방법은 여러 가지가 있지만, 한 가지 접근 방식은 최근 저희가 출시한 모델 컨텍스트 프로토콜을 통하는 거예요. 이 프로토콜은 개발자들이 간단한 클라이언트 구현으로 성장하는 서드파티 도구 생태계와 통합할 수 있도록 해줘요.

이 포스팅의 남은 부분에서는 각 LLM 호출이 이런 증강된 기능들에 접근할 수 있다고 가정할게요.

프롬프트 체이닝 (Prompt Chaining)

프롬프트 체이닝은 작업을 일련의 단계로 분해하고, 각 LLM 호출은 이전 호출의 출력을 처리하는 방식이에요. 어떤 중간 단계에서든 프로그램적인 검사("게이트"를 아래 다이어그램에서 확인하세요)를 추가하여 프로세스가 제대로 진행되고 있는지 확인할 수 있죠.

The prompt chaining workflow

이 워크플로우를 사용해야 할 때: 이 워크플로우는 작업이 고정된 하위 작업으로 쉽고 깔끔하게 분해될 수 있는 상황에 이상적이에요. 주요 목표는 각 LLM 호출을 더 쉬운 작업으로 만들어 정확도를 높이는 대신 레이턴시를 감수하는 것이죠.

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

  • 다단계 질문에 답하기 (예: "이 접근 방식의 장단점은 무엇인가요?")
  • 문서를 분석한 다음 특정 대상(예: 엔지니어를 위한 기술 요약, 관리자를 위한 고수준 요약)을 위한 요약을 생성하기

라우팅 (Routing)

라우팅은 입력을 분류하고 이를 전문화된 후속 작업으로 연결해요. 이 워크플로우는 관심사 분리(separation of concerns)를 가능하게 하고, 더 전문화된 프롬프트를 구축할 수 있게 해줘요. 이 워크플로우가 없으면, 한 가지 유형의 입력에 최적화하는 것이 다른 입력에서의 성능을 저하시킬 수 있어요.

The routing workflow

이 워크플로우를 사용해야 할 때: 라우팅은 서로 다른 범주가 있고 이를 개별적으로 처리하는 것이 더 좋은 복잡한 작업에 잘 맞아요. 그리고 분류가 LLM이나 더 전통적인 분류 모델/알고리즘에 의해 정확하게 처리될 수 있을 때 유용하죠.

라우팅이 유용한 예시:

  • 고객 지원 티켓을 분류하고 특정 응답 흐름으로 전달하기 (예: "기술 문제", "청구 문의", "기능 요청")
  • 입력 특성에 따라 특정 작업을 위한 모델 선택하기 (예: 마케팅 문구를 위한 고도로 창의적인 모델, 법률 문서를 위한 고도로 사실적인 모델)

병렬화 (Parallelization)

LLM은 때때로 한 작업을 동시에 처리하고 그 출력을 프로그램적으로 통합할 수 있어요. 이 병렬화(Parallelization) 워크플로우는 두 가지 주요 변형으로 나타나요.

  • 다중 동시(Multiside-by-side): 여러 LLM 호출이 다른 관점에서 답변을 제공합니다 (예: 장단점, "사용자"와 "개발자"와 같은 다른 페르소나).
  • 다중 시도(Multishot): 여러 LLM 호출이 동일한 작업을 시도하여 신뢰도를 높이거나 여러 솔루션을 제공합니다.
The parallelization workflow

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

병렬화가 유용한 예시:

  • 여러 창의적인 옵션 생성하기 (예: 마케팅 문구, 헤드라인, 사용자 스토리)
  • 어떤 입장에 대한 찬반 논거를 생성하여 균형 잡힌 논증 에세이 초안 작성하기
  • 프로그래밍 문제 해결을 위한 다양한 접근 방식 평가하기

오케스트레이터-워커 (Orchestrator-Workers)

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

The orchestrator-workers workflow

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

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

  • 자율 소프트웨어 개발 (예: 버그 수정, 새 기능 추가)

평가자-최적화자 (Evaluator-Optimizer)

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

The evaluator-optimizer workflow

이 워크플로우를 사용해야 할 때: 이 워크플로우는 명확한 평가 기준이 있고, 반복적인 개선이 측정 가능한 가치를 제공할 때 특히 효과적이에요. 잘 맞는다는 두 가지 징후는, 첫째, 사람이 피드백을 명확하게 표현할 때 LLM 응답이 확실히 개선될 수 있다는 점이고요, 둘째, LLM이 그러한 피드백을 제공할 수 있다는 점이에요. 이건 마치 인간 작가가 잘 다듬어진 문서를 만들어낼 때 거치는 반복적인 글쓰기 과정과 비슷하다고 할 수 있죠.

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

  • 특정 제약 조건을 가진 창의적인 콘텐츠 초안 작성하기 (예: 특정 단어 수 이하의 어린이 이야기, 특정 교훈에 초점)
  • 가독성, 성능 또는 스타일 가이드 준수를 위해 코드 최적화하기

에이전트 (Agents)

에이전트는 LLM의 핵심 기능들이 성숙해짐에 따라 프로덕션 환경에서 부상하고 있어요. 복잡한 입력을 이해하고, 추론 및 계획에 참여하며, 도구를 안정적으로 사용하고, 오류에서 복구하는 능력들이요. 에이전트는 인간 사용자의 명령이나 상호작용적인 토론을 통해 작업을 시작해요. 일단 작업이 명확해지면, 에이전트는 독립적으로 계획하고 작동하며, 추가 정보나 판단을 위해 인간에게 다시 돌아갈 수도 있어요. 실행 중에는 에이전트가 각 단계에서 환경으로부터 "그라운드 트루스"(예: 도구 호출 결과 또는 코드 실행)를 얻어 진행 상황을 평가하는 것이 중요해요. 에이전트는 체크포인트나 블로커를 만났을 때 인간의 피드백을 위해 잠시 멈출 수도 있고요. 작업은 보통 완료 시 종료되지만, 제어를 유지하기 위해 중단 조건(예: 최대 반복 횟수)을 포함하는 것도 흔하죠.

에이전트는 정교한 작업을 처리할 수 있지만, 구현은 종종 간단한 편이에요. 일반적으로 환경 피드백을 기반으로 도구를 반복적으로 사용하는 LLM에 불과하거든요. 따라서 도구 세트와 그 문서화를 명확하고 사려 깊게 설계하는 것이 아주 중요해요. 도구 개발을 위한 모범 사례는 부록 2("도구 프롬프트 엔지니어링")에서 더 자세히 설명해 드릴게요.

Autonomous agent

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

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

에이전트가 유용한 예시:

  • 자율 소프트웨어 개발 (예: 버그 수정, 새 기능 추가)
  • 지능형 고객 지원

다음 예시는 저희 자체 구현 사례들이에요.

High-level flow of a coding agent

이 빌딩 블록들은 정해진 지침은 아니에요. 개발자들이 다양한 사용 사례에 맞춰 형태를 만들고 조합할 수 있는 일반적인 패턴들이죠. 다른 LLM 기능들과 마찬가지로, 성공의 핵심은 성능을 측정하고 구현을 반복 개선하는 것이에요. 다시 한번 강조하지만, 복잡성은 오직 결과가 확실히 개선될 때만 추가하는 것을 고려해야 해요.

LLM 분야에서의 성공은 가장 정교한 시스템을 구축하는 것에 있지 않아요. 여러분의 필요에 적합한 시스템을 구축하는 것이죠. 간단한 프롬프트로 시작하고, 포괄적인 평가를 통해 최적화하며, 더 간단한 해결책이 부족할 때만 다단계 에이전틱 시스템을 추가하세요.

에이전트를 구현할 때, 저희는 세 가지 핵심 원칙을 따르려고 노력해요.

  • 단순하게 시작하세요: 가능한 경우 단일 LLM 호출을 사용하세요. 복잡성은 오직 성능을 확실히 개선하거나 새로운 기능을 제공할 때만 추가하세요.
  • 반복하고 평가하세요: 에이전트를 지속적으로 테스트하고 개선하세요. 개선 사항을 검증하고 추가 최적화 영역을 식별하기 위해 측정 지표를 사용하세요.
  • 인간 개입을 유지하세요: 에이전트는 특히 미묘한 차이나 외부 검증이 필요한 작업에서 인간의 감독과 함께 잘 작동합니다. 인간의 개입과 피드백을 허용하도록 시스템을 설계하세요.

프레임워크는 빠르게 시작하는 데 도움이 될 수 있지만, 프로덕션으로 나아갈 때는 추상화 계층을 줄이고 기본 구성 요소로 구축하는 것을 주저하지 마세요. 이런 원칙들을 따른다면, 강력할 뿐만 아니라 신뢰할 수 있고, 유지보수하기 쉬우며, 사용자들에게 믿음을 주는 에이전트를 만들 수 있을 거예요.

Erik S.와 Barry Zhang이 작성했습니다. 이 글은 앤트로픽에서 에이전트를 구축하며 얻은 경험과 고객들이 공유해 주신 소중한 통찰을 바탕으로 합니다. 깊이 감사드립니다.

부록 1: 실제 에이전트 활용 사례

고객들과의 협업을 통해, 위에 설명된 패턴들의 실질적인 가치를 보여주는 두 가지 특히 유망한 AI 에이전트 애플리케이션을 발견했어요. 두 애플리케이션 모두 에이전트가 대화와 행동이 모두 필요한 작업, 명확한 성공 기준이 있는 작업, 피드백 루프를 가능하게 하는 작업, 그리고 의미 있는 인간 감독을 통합하는 작업에서 가장 큰 가치를 추가하는 방법을 보여줘요.

지능형 고객 지원

지능형 고객 지원은 익숙한 챗봇 인터페이스와 도구 통합을 통한 향상된 기능을 결합한 거예요. 이는 더 개방형 에이전트에 자연스럽게 잘 맞는데, 그 이유는 다음과 같아요.

  • 많은 대화가 자동화를 가능하게 할 만큼 유사하지만, 하드코딩된 솔루션을 취약하게 만들 만큼 충분히 다르기 때문이에요.
  • 도구를 사용할 수 있는 능력은 에이전트가 질문에 답하는 것을 넘어 행동을 수행할 수 있게 해줘요(예: 계정 업데이트, 문제 해결).
  • 명확한 성공 기준(예: 문의 성공적인 해결)은 효과적인 피드백 루프를 가능하게 하죠.

여러 회사들이 성공적인 해결에 대해서만 요금을 부과하는 사용량 기반 가격 모델을 통해 이 접근 방식의 타당성을 입증했어요. 이는 에이전트의 효율성에 대한 자신감을 보여주는 거죠.

자율 소프트웨어 개발

소프트웨어 개발 분야는 코드 완성에서 자율적인 문제 해결에 이르기까지 LLM 기능의 놀라운 잠재력을 보여주었어요. 에이전트가 특히 효과적인 이유는 다음과 같아요.

  • 현대 소프트웨어 개발은 LLM이 뛰어난 기호 조작 작업이 대부분이기 때문이에요.
  • 많은 작업이 잘 정의된 하위 작업으로 나뉠 수 있어요(예: 버그 보고서가 특정 파일 변경 및 테스트 케이스로 이어지는 경우).
  • 테스트 스위트 및 기타 자동화된 검사는 반복적인 에이전트 개선을 위한 훌륭한 피드백 루프를 제공해요.

저희 자체 구현에서는 에이전트가 이제 풀 리퀘스트 설명만으로 SWE-bench Verified 벤치마크의 실제 GitHub 문제를 해결할 수 있어요. 하지만 자동화된 테스트가 기능 확인에 도움이 되더라도, 솔루션이 더 넓은 시스템 요구 사항과 일치하는지 확인하려면 인간의 검토가 여전히 중요해요.

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

어떤 에이전틱 시스템을 구축하든, 도구는 여러분의 에이전트에서 중요한 부분이 될 가능성이 높아요. 도구는 클로드가 저희 API에 정확한 구조와 정의를 지정함으로써 외부 서비스 및 API와 상호 작용할 수 있게 해줘요. 클로드가 응답할 때, 도구를 호출할 계획이라면 API 응답에 도구 사용 블록을 포함할 거예요. 도구 정의 및 사양에는 전체 프롬프트만큼이나 많은 프롬프트 엔지니어링 주의를 기울여야 해요. 이 짧은 부록에서는 도구를 프롬프트 엔지니어링하는 방법을 설명해 드릴게요.

도구 출력 형식 단순화하기

동일한 동작을 지정하는 방법은 여러 가지가 있는 경우가 많아요. 예를 들어, 파일 편집을 diff로 작성하거나 전체 파일을 다시 작성하여 지정할 수 있죠. 구조화된 출력의 경우, 마크다운 안에 코드를 반환하거나 JSON 안에 코드를 반환할 수 있어요. 소프트웨어 공학에서는 이런 차이들이 겉보기일 뿐이며, 손실 없이 서로 변환될 수 있죠. 하지만 어떤 형식은 다른 형식보다 LLM이 작성하기 훨씬 더 어려워요. diff를 작성하려면 새 코드를 작성하기 전에 청크 헤더에서 몇 줄이 변경되는지 알아야 해요. JSON 안에 코드를 작성하는 것(마크다운과 비교할 때)은 새 줄과 따옴표에 대한 추가 이스케이프 처리가 필요하죠.

도구 형식을 결정하기 위한 저희 제안은 다음과 같아요.

  • 구조화된 출력에는 JSON 객체나 마크다운 코드 블록을 선택하세요. LLM이 특수 문자(예: diff 문법)로 출력을 포맷하도록 요구하는 것보다요.
  • LLM에게 도구 인자에서 문자를 이스케이프 처리하도록 요구하지 마세요(예: 문자열에 이스케이프된 새 줄 \n을 넣으라고 하지 말고, 그냥 리터럴 새 줄을 넣으세요).
  • 도구를 사용할 때는 템플릿화된 문자열에 인자를 채워 넣게 하는 것보다, LLM이 도구에 대한 전체 입력을 생성하도록 하는 것이 더 나은 경우가 많아요.

에이전트-컴퓨터 인터페이스(ACI)에 집중하기

한 가지 일반적인 규칙은 인간-컴퓨터 인터페이스(HCI)에 얼마나 많은 노력이 들어가는지 생각해보고, 좋은 에이전트-컴퓨터 인터페이스(ACI)를 만드는 데도 그만큼의 노력을 투자할 계획을 세우는 거예요. 그렇게 하는 방법에 대한 몇 가지 생각은 다음과 같아요.

  • 에이전트가 스스로 오류를 수정하고 복구할 수 있도록 도구를 설계하세요(예: "파일 읽기" 도구는 "파일 쓰기" 오류에서 복구할 수 있죠).
  • 도구의 목적, 사용법, 예상 출력에 대한 명확하고 간결한 문서를 제공하세요.
  • 도구 이름이 설명적이고 모호함을 피하도록 하세요. 예를 들어, ls 대신 list_files_in_directory를 사용하세요.

SWE-bench용 에이전트를 구축할 때, 저희는 전체 프롬프트보다 도구를 최적화하는 데 더 많은 시간을 할애했어요. 예를 들어, 에이전트가 루트 디렉토리에서 벗어난 후 상대 파일 경로를 사용하는 도구에서 모델이 실수를 저지르는 것을 발견했죠. 이 문제를 해결하기 위해, 도구가 항상 절대 파일 경로를 요구하도록 변경했고, 모델이 이 방식을 완벽하게 사용하는 것을 확인했어요.

제품 업데이트, 사용법, 커뮤니티 소식 등 다양한 정보가 매월 받은 편지함으로 배달됩니다.

anthropic · 원문 보기 · 2024-12-19

이 글은 원문을 한국어로 번역한 것입니다. 저작권은 원 저작자에게 있습니다.