본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 05. 23. 15:01

실전 AI 에이전트 — 파트 2: 무엇이 에이전트를 만드는가

요약

성공적인 AI 에이전트는 더 똑똑한 모델이 아니라, 모델을 둘러싼 구조적 시스템에 의해 결정됩니다. 본문은 상태 인식, 중단 조건, 에스컬레이션 경로를 포함한 루프 구조의 중요성을 강조합니다.

핵심 포인트

  • 에이전트는 모델이 아닌 모델을 중심으로 구축된 시스템임
  • 프롬프트 스터핑 대신 컴포지션(Composition) 구조를 사용해야 함
  • 에이전트의 핵심 루프: 관찰 → 결정 → 실행 → 점검 → 반복
  • 상태 인식과 도구 활용이 에이전트 성능의 핵심 요소임

파트 1은 Priya의 주문이 이미 발송되었음에도 불구하고 에이전트가 자신 있게 환불을 진행하며 끝났습니다. 여기 다르게 구축된 시스템에서의 동일한 요청이 있습니다: "안녕하세요, 주문 번호 #4471을 취소하고 환불받고 싶습니다." 시스템은 주문 상태를 읽습니다 — 발송됨. 시스템은 취소 절차에 주문이 발송되지 않은 상태여야 한다는 조건이 있음을 확인합니다. 시스템은 취소를 시도하지 않습니다. 사과하며 다른 도움이 필요한지 묻지도 않습니다. 대신 다음과 같이 말합니다: "주문 번호 #4471은 이미 어제 발송되었습니다. 자동 취소는 발송 전에만 적용됩니다. 물건이 도착하면 반품 절차를 시작하거나, 지금 바로 상담원(human agent)에게 연결해 드릴 수 있습니다. 어떤 것을 원하시나요?" 그런 다음 시스템은 멈춰서 기다립니다. 이 응답의 그 어떤 부분도 더 똑똑한 모델을 필요로 하지 않았습니다. 모델은 파트 1에서 Priya에게 자신 있게 환불을 해주었던 것과 동일한 모델입니다. 변한 것은 모델 주변의 시스템입니다. 이 글은 그 시스템이 실제로 무엇인지에 관한 것입니다.

동일한 요청, 다른 시스템
파트 1의 취소 사례는 나쁜 에이전트에 관한 이야기가 아니었습니다. 그것은 적절한 구성 요소들을 적절한 위치에 배치하지 못한 시스템에 관한 이야기였습니다. 구성 요소의 이름을 아직 언급하지 않고, 이 "다른 시스템"이 무엇을 했는지 살펴보겠습니다:

행동하기 전에, 주문의 실제 상태(actual state)를 확인했습니다. 그 상태를 허용 여부를 규정하는 절차와 비교했습니다 — 그리고 "취소하지 않음"은 예외가 아닌 정당한 경로였습니다. 실제 상황에 맞는 대안을 고객에게 제안했습니다. 자신 있게 하나를 선택하는 대신, 고객이 선택할 때까지 멈춰서 기다렸습니다.

이 목록에 없는 것에 주목하십시오: 더 똑똑한 자연어(natural language), 시스템 프롬프트(system prompt)의 더 나은 문구, 더 발전된 모델. 모든 차이점은 구조적(structural)입니다. 시스템이 올바른 결정이 내려질 수 있는 공간을 만든 것입니다. 파트 1에서 나타난 세 가지 공백 — 상태 인식(state awareness), 중단 조건(stopping condition), 그리고 에스컬레이션 경로(escalation path) — 모두 여기서는 구조적인 해답을 찾았습니다. 이러한 요소들이 실제로 어떻게 결합되어 작동하는 에이전트가 되는지는 파트 6의 전체 빌드에서 다룹니다.

지금 당장 중요한 점은 이것입니다: 시스템이 적절한 순서와 적절한 점검 과정을 거쳐 작업을 수행했으며, 기존의 고장 난 에이전트가 프롬프트 스터핑 (prompt stuffing)을 사용했던 곳에 컴포지션 (composition)을 사용했다는 것입니다.

변화한 것은 모델이 아니라 루프 (loop)입니다. 모델은 하나의 구성 요소일 뿐입니다. 에이전트는 그 모델을 중심으로 구축한 시스템입니다. 에이전트를 설명하는 가장 단순하고 정확한 방법은 다음과 같습니다: 턴 (turn)을 거치며 유지되는 상태 (state)와 모델이 세상 속에서 무언가를 수행할 수 있게 해주는 도구 (tools)를 갖추고, 모델을 여러 번 실행하는 루프입니다. 이 루프는 다섯 가지의 인식 가능한 단계로 이루어집니다: 관찰 (Observe) → 결정 (Decide) → 실행 (Act) → 점검 (Check) → 반복 (Repeat).

단계발생하는 일
관찰 (Observe)현재 상태 수집 — 요청, 이전 턴들, 마지막 도구 결과, 알려진 정보 등
결정 (Decide)모델이 다음 단계 선택: 도구 호출, 사용자에게 질문, 또는 중단
실행 (Act)선택된 단계 실행 — 도구 작동, 메시지 전송, 또는 결정 기록
점검 (Check)결과 반환. 다음 관찰 단계에 결과가 포함됨
반복 (Repeat)완료, 차단, 또는 에스컬레이션 (escalated)될 때까지

이것이 그 형태입니다. 특별할 것 없습니다. 루프 자체는 단순합니다. 에이전트를 에이전트답게 만드는 것은 루프의 영리함이 아닙니다. 매 반복마다 모델이 어떤 단계를 밟을지 스스로 결정할 수 있다는 사실입니다. 그것이 핵심입니다. 고정된 스크립트가 아닙니다. 하드코딩된 흐름 (hard-coded flow)도 아닙니다. 모델은 시스템이 부여한 경계 내에서 스스로 결정합니다. (루프가 실제로 작동하는 메커니즘 — 상태, 중단 조건, 유한한 자원으로서의 컨텍스트 — 은 파트 3에서 다룹니다. 지금은 이 형태만 기억하세요.)

앞서 언급한 '다른 시스템'은 이러한 종류의 루프를 실행하고 있었습니다. 이 루프는 취소(cancellation)를 시도하기 전에 상태를 읽을 수 있는 여유를 만들어 주었습니다. 어떤 시스템에서는 모델이 그 단계를 선택할 수도 있고, 다른 시스템에서는 시스템이 게이트 (gate)로서 이를 요구할 수도 있습니다. 어느 쪽이든 중요한 점은 에이전트가 요청에서 행동으로 곧장 뛰어넘지 않는다는 것입니다.

대조를 위해 설명하자면: 워크플로 (workflow)는 개발자가 사전에 작성한 단계들을 실행합니다. 에이전트는 런타임 (runtime)에 각 단계를 결정합니다. 부품은 같지만, 배선 (wiring)이 다릅니다. 다이어그램은 그 차이를 명확히 보여줍니다. 워크플로 대 에이전트 — 동일한 부품, 다른 배선.

에이전트는 세 가지 실용적인 프리미티브 (Primitives)로 구성됩니다. 에이전트는 자신의 능력을 처음부터 스스로 발명할 필요가 없습니다. 여러분이 이미 접했을 법한 세 가지 프리미티브 (Primitives)를 조합합니다.

MCP — 행동하기 위한 것. 에이전트가 세상 속에서 무언가를 수행하는 도구 (tools)를 호출하는 표준화된 방식입니다: 데이터베이스 쿼리, API 호출, 계산 실행, 이메일 전송 등. 에이전트의 "동사 (verbs)"라고 할 수 있습니다. 이는 'MCP in Practice' 시리즈에서 다루었던 것과 동일한 MCP입니다. MCP가 처음인가요? 이 글을 따라오는 데 해당 배경지식이 반드시 필요하지는 않습니다. 지금은 이 멘탈 모델 (mental model)만으로 충분합니다: MCP는 에이전트가 깔끔한 프로토콜 (protocol)을 통해 도구를 호출할 수 있도록 돕습니다.

RAG — 알기 위한 것. 에이전트에게 지식이 필요할 때 외부 지식을 에이전트의 컨텍스트 (context)로 가져오는 검색 (Retrieval)입니다: 회사 정책, 제품 문서, 과거 사례 기록, 자격 규칙 등. 이는 'RAG in Practice' 시리즈에서 다루었던 것과 동일한 RAG입니다. RAG가 처음인가요? 마찬가지로 이 글은 그 자체로 완결성을 갖추고 있습니다. 지금은 이 멘탈 모델 (mental model)만으로 충분합니다: RAG는 에이전트가 모델이 학습한 내용에만 의존하는 대신, 검색된 사실에 기반하여 의사결정을 내릴 수 있도록 (grounding) 돕습니다.

기술 (Skills) — 재사용 가능한 절차를 따르기 위한 것. 에이전트가 반복적으로 적용할 수 있는 절차를 명시한 마크다운 (markdown) 파일입니다: 언제 사용할지, 단계별 절차, 실패 모드 (failure modes), 승인 규칙 등을 포함합니다. 매 턴마다 시스템 프롬프트 (system prompt)에 "주문이 발송되었다면 담당자에게 에스컬레이션(escalate) 하세요"라는 내용을 집어넣는 대신, 기술 파일 (skill file)에 절차를 담아두고 에이전트가 관련이 있을 때 이를 로드 (load)합니다. 예를 들어, '주문 취소 기술'은 다음과 같이 명시할 수 있습니다: 먼저 상태를 확인하고, 이미 발송되었다면 거절하며, 해당되는 경우 고객에게 반품을 제안하고, 고객이 예외를 요청하면 에스컬레이션 하라는 식입니다. 이렇게 하면 절차를 버전 관리하고 검토할 수 있으며, 계속 커지는 하나의 프롬프트에 파묻히는 대신 관련이 있을 때만 로드할 수 있습니다. 기술 (Skills)은 나중에 패턴 (patterns), 제어 표면 (control surfaces), 그리고 프로덕션 빌드 (production builds)에 대해 이야기할 때 더 중요해질 것입니다.

에이전트의 역할은 언제 어떤 것을 사용할지 결정하는 것입니다. 지금 이 순간 어떤 프리미티브 (primitive)를 적용할지 결정하는 것 — 이것이 에이전트의 핵심적인 움직임입니다. 매 턴마다 세 가지를 모두 사용하는 것이 아닙니다. 종종 단 하나만 사용합니다.

때로는 아무것도 사용하지 않고 에이전트가 직접 답변하기도 합니다. 앞서 살펴본 취소 시스템은 절차의 이름을 지정하는 스킬 (Skill)과 상태를 읽고 행동하는 MCP 도구 (Tools)를 사용했습니다. 시스템이 정확한 환불 정책 텍텍스트가 필요할 때는 RAG가 정책 세부 정보를 제공할 수 있습니다. 모델은 그중 어떤 것도 스스로 만들어낼 필요가 없었습니다. 시스템이 이미 보유하고 있는 것들 중에서 적절한 순서대로 선택했을 뿐입니다. 파트 6에서는 전체 구성을 엔드 투 엔드 (End-to-end)로 살펴봅니다. 에이전트가 구성하는 세 가지 기본 요소 (Primitives) — 행동하기 (Acting), 알기 (Knowing), 그리고 재사용 가능한 절차 따르기 (Following reusable procedures)입니다.

수동 ReAct에서 네이티브 도구 호출 (Native Tool Calling)까지. 수동 ReAct (Manual ReAct)는 모델의 출력을 코드가 파싱 (Parse)해야 하는 텍스트로 취급합니다. 네이티브 도구 호출 (Native Tool Calling)은 모델의 출력을 코드가 실행할 수 있는 구조화된 의도 (Structured intent)로 취급합니다. 이 섹션에서 다루고자 하는 것은 바로 이 단 하나의 계약 (Contract) 변화에 관한 것입니다. 파트 1에서는 개발자가 새로운 예외 케이스 (Edge cases)를 발견함에 따라 '엄격한 규칙 (STRICT RULES)' 섹션이 늘어나는 수동 ReAct 프롬프트를 보여주었습니다. 해당 프롬프트는 수동 ReAct를 수행하고 있었습니다. 즉, 모델이 특정 형식의 문자열을 반환하면, 정규 표현식 (Regex)이 "Action:" 라인을 추출하고, 시스템이 지정된 도구를 호출하며, 그 결과가 "Observation:" 라인으로서 프롬프트에 다시 삽입되는 식으로 사이클이 계속되는 방식입니다.

수동 ReAct는 프로토타입을 만들기 쉽고 데모 (Demo)에 매우 유용하기 때문에 유용합니다. 모델이 생각하고 행동하는 과정을 모두 일반 텍스트로 한곳에서 볼 수 있기 때문입니다. 하지만 프로덕션 (Production) 환경에서는 그 단순함이 그대로 취약함이 됩니다. 세 가지 문제가 발생합니다. 첫째, 모델은 정규 표현식이 파싱할 수 있는 문자열 형식으로 출력을 생성해야 합니다. 만약 모델이 행동을 약간 다르게 표현한다면 — 대소문자가 다르거나, 단어가 추가되거나, 오타가 발생하는 경우 — 정규 표현식이 이를 놓치게 되고 에이전트는 멈춰버립니다. 둘째, 모델이 어떻게 행동해야 하는지에 대한 모든 규칙이 프롬프트에 들어 있습니다. "배송된 주문은 취소하지 마세요"는 영어입니다. "Action: tool_name 형식을 정확히 사용하세요"도 영어입니다. "최종 답변 후에는 멈추세요"도 영어입니다. 모델은 때때로 이 영어 규칙을 따르기도 하고, 때로는 무시하기도 합니다. 셋째, 도구 설명 (Tool descriptions)이 프롬프트 텍스트의 일부입니다. 도구를 추가하면 프롬프트가 길어지고, 도구를 변경하면 프롬프트를 수정해야 합니다.

이 프롬프트는 스키마 (schema), 파서 (parser), 상태 머신 (state machine), 그리고 절차 매뉴얼 (procedure manual)의 역할을 하나의 블록 안에서 모두 수행하고 있습니다. 네이티브 도구 호출 (Native tool calling)은 프로덕션 단계에서의 핵심적인 움직임입니다. 이는 새로운 모델 기능이 아니라, 애플리케이션과 모델 사이의 서로 다른 계약 (contract)입니다. 이것이 Priya의 환불 실패 문제를 그 자체로 해결해주지는 않습니다. 하지만 프롬프트 내의 또 하나의 문장으로 남겨두는 대신, "배송된 주문은 취소하지 마십시오"라는 규칙을 검증 단계로서 강제할 수 있는 구조적 위치를 시스템에 제공합니다. 네이티브 도구 호출에서 도구 정의 (Tool definitions)는 프롬프트 내의 영어 문장이 아니라, API 호출의 파라미터로 모델에게 전달되는 구조화된 스키마 (structured schemas)로 존재합니다. 모델이 도구를 호출하고자 할 때, 애플리케이션이 파싱해야 하는 문자열이 아닌 구조화된 도구 사용 블록 (structured tool-use block)을 반환합니다. 애플리케이션은 {"tool": "cancel_order", "arguments": {"order_id": "4471"}}를 직접 확인합니다. 정규 표현식 (regex)도 필요 없고, 형식의 취약성 (format brittleness)도 없습니다. 시스템 프롬프트는 줄어들고, 형식 규칙은 사라집니다. 도구 설명은 더 이상 산문 (prose)이 아닙니다. 구조화된 도구 호출이 그 자체로 정책을 강제하는 것은 아닙니다. 애플리케이션이나 도구 서버 (tool server)가 여전히 인자를 검증하고, 권한을 확인하며, 안전하지 않은 동작을 거부해야 합니다. 개선점은 이러한 검증들이 프롬프트 속의 또 다른 영어 규칙으로 묻혀 있는 대신, 이제 구조화된 경계 (structured boundary)에서 발생한다는 것입니다. 쉽게 말해, 모델이 텍스트로 Action: cancel_order라고 작성하고 코드가 이를 파싱하는 대신, 모델이 앱이 직접 읽을 수 있는 구조화된 객체 (structured object)를 반환하는 것입니다. "스키마 (schema)"는 어떤 도구가 존재하고 어떤 인자를 취하는지에 대한 공식적인 설명이며, "도구 사용 블록 (tool-use block)"은 모델이 도구를 호출하고 싶을 때 반환하는 것입니다. 둘 다 텍스트가 아닌 객체 (objects)입니다. 이러한 구조적 변화는 해결의 시작점이지, 끝이 아닙니다. MCP는 이 그림에서 프로토콜 계층 (protocol layer)으로서 자리 잡습니다. 네이티브 도구 호출은 하나의 모델과 하나의 애플리케이션 사이의 계약입니다. MCP는 애플리케이션과 여러 도구 서버 사이의 표준화된 계약입니다. 네이티브 도구 호출은 모델-to-앱 (model-to-app) 경계를 구조화하며, MCP는 앱-to-도구-서버 (app-to-tool-server) 경계를 구조화합니다.

중요한 점은: 네이티브 도구 호출 (native tool calling)과 MCP는 서로 경쟁 관계가 아닙니다. 실제 프로덕션 에이전트 (production agent)는 모델 측면에서는 네이티브 도구 호출을 사용하고, 도구 서버 (tool-server) 측면에서는 MCP를 사용합니다. 이 시리즈는 파트 6의 빌드 과정 전반에 걸쳐 두 가지를 모두 사용할 것입니다. (만약 MCP나 RAG가 생소하다면, 저는 두 주제에 대해 별도의 시리즈를 가지고 있습니다. 여기서는 멘탈 모델 (mental model)만 필요합니다: MCP는 에이전트가 행동하도록 돕고, RAG는 에이전트가 알 수 있도록 돕습니다. 에이전트는 비-에이전트 시스템이 사용하는 것과 동일한 방식으로 각각을 사용합니다.)

수동 ReAct vs. 네이티브 도구 호출 (Native Tool Calling) — 동일한 에이전트, 동일한 작업, 하지만 다른 계약 (contract).

에이전트 (Agents) vs. 챗봇 (Chatbots) vs. 워크플로우 (Workflows)
"에이전트"라는 단어는 여러 가지 서로 다른 용도로 사용됩니다. 그중 일부는 에이전트이지만, 일부는 그렇지 않습니다. 이 구분을 하는 것은 선민의식을 갖기 위함이 아닙니다. 서로 다른 시스템은 서로 다른 실패 모드 (failure modes)를 가지며, 이들을 혼동하면 잘못된 것을 만들게 되기 때문입니다.

챗봇 (Chatbot). 응답 전용 (Reply-only). 사용자가 무언가를 말하면 모델이 응답합니다. 대화 기록을 기억할 수는 있지만, 도구를 호출하거나, 세상 속에서 행동을 취하거나, 제어 루프 (control loop)를 실행하지는 않습니다. 실패 모드: 모르는 내용에 대해 자신 있게 지어냅니다.

워크플로우 (Workflow). 컨트롤러 (모델이 아닌 다른 요소)가 조건에 따라 다음 단계가 무엇인지 결정합니다. 모델은 특정 작업을 수행하기 위해 특정 단계 내부에서 호출되지만, 모델이 어떤 단계를 밟을지 선택하는 것은 아닙니다. 프롬프트 체인 (prompt chain)이 가장 단순한 사례입니다: 모든 단계가 항상 동일한 순서로 실행되는, 하나의 고정된 경로를 가진 워크플로우입니다. 실패 모드: 컨트롤러의 분기 로직 (branching logic)이 예상하지 못한 예외 상황 (edge cases)이 발생하면 그대로 통과되어 버립니다.

에이전트 (Agent). 모델이 설계된 경계 내에서 매 턴마다 어떤 단계를 밟을지 결정합니다. 상태 (State)는 턴을 거쳐 지속됩니다. 도구를 사용할 수 있습니다. 루프는 완료되거나, 차단되거나, 에스컬레이션 (escalated)될 때까지 계속됩니다. 실패 모드: 자신감 있게 내리는 잘못된 결정, 그리고 파트 1에서 언급한 실패 모드들.

워크플로우가 열등한 에이전트인 것은 아닙니다. 많은 프로덕션 문제에서 워크플로우가 정답일 수 있습니다. 경로가 잘 알려져 있고, 단계가 안정적이며, 모델이 다음에 무엇이 올지 결정할 필요가 없기 때문입니다. 이 시리즈의 파트 5는 언제 무엇을 선택해야 하는지에 대해 다룹니다.

그 경계선은 "똑똑함 대 멍청함"이 아닙니다. 경계선은 다음에 무엇이 일어날지를 누가 결정하는지, 그리고 시스템이 모델에게 틀릴 수 있는 여지를 얼마나 허용하는지에 달려 있습니다. 에이전트를 정의하는 경계선: 중요한 설계 질문은 어떤 모델을 선택했느냐가 아닙니다. 시스템이 모델에게 무엇을 결정하도록 허용하느냐입니다. 이것이 이 시리즈의 핵심적인 관점입니다. 제한된 자율성 (Bounded autonomy): 설계된 경계 내에서의 모델 주도적 선택 (model-driven choice). 이 경계는 실제 엔지니어링의 영역입니다. 에이전트가 어떤 도구 (tools)를 가졌는지, 어떤 상태 (state)를 읽을 수 있는지, 어떤 상태를 쓸 수 있는지, 어떤 동작에 승인이 필요한지, 어떤 에스컬레이션 경로 (escalation paths)가 존재하는지, 그리고 중단 조건 (stopping condition)이 무엇인지 등이 이에 해당합니다. 시스템은 세 가지 프리미티브 (primitives: MCP, RAG, Skills)를 구성하고, 모델에게 이들 사이에서 선택할 수 있는 여지와 "이것은 내가 할 일이 아니다"라고 말할 수 있는 여지를 제공합니다. 무언가를 에이전트로 만드는 것은 모델이 얼마나 똑똑한가가 아닙니다. 그것은 시스템이...

AI 자동 생성 콘텐츠

본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0