
실전 AI 에이전트 — 파트 6: 프로덕션 에이전트 루프 (Production Agent Loop) 구축하기
요약
데모 수준의 AI 에이전트를 넘어 실제 프로덕션 환경에서 견고하게 작동하는 에이전트 루프 구축 방법을 다룹니다. 단순한 루프 구조를 넘어 상태 관리, 도구 계약, 검증 단계, 예산 제한 등 엄격한 구현 요소의 필요성을 강조합니다.
핵심 포인트
- 프로덕션 에이전트는 단순 루프를 넘어 상태 관리와 도구 계약이 필요함
- 실행(act) 단계에서는 멱등성 키와 계약을 통한 도구 호출이 중요함
- 확인(check) 단계는 단순 응답 확인을 넘어 실제 상태 변화를 검증해야 함
- 비용 및 오류 제어를 위한 예산 설정과 중단 규칙이 필수적임
8부작 중 6부 — 실전 AI 에이전트 (AI Agents in Practice) 시리즈.
이전 편 — 워크플로, 에이전트, 또는 단일 LLM 호출 — 어떻게 결정할 것인가 (파트 5)
데모용 루프만으로는 충분하지 않습니다
파트 3에서는 루프가 어떻게 작동하는지(관찰(observe), 결정(decide), 실행(act), 확인(check), 반복(repeat))를 보여주었습니다. 파트 5에서는 해당 루프가 아예 필요한지, 아니면 워크플로(workflow)나 단일 호출(single call)로 더 적은 비용으로 작업을 수행할 수 있는지 결정하는 데 도움을 드렸습니다.
결정을 내렸다고 가정해 봅시다. 작업에 진정으로 에이전트가 필요한 상황입니다. 즉, 다음 단계가 당신이 설정한 경계 내에서 런타임(runtime) 중에 선택되어야 합니다. 이제 이를 구축해야 하는데, 데모에서 작동하는 에이전트와 프로덕션(production) 환경에서 견뎌내는 에이전트 사이의 간극은 루프 다이어그램이 암시하는 것보다 훨씬 넓습니다.
데모에서는 루프만으로 충분합니다. 모델이 관찰하고, 결정하고, 도구(tool)를 호출하고, 결과를 확인하고, 계속 진행하면 됩니다. 하지만 프로덕션에서는 이 다섯 단어가 더 큰 무게를 가져야 합니다. 프로덕션 에이전트는 단순히 모델과 몇 가지 도구의 조합이 아닙니다. 그것은 루프를 정직하게 유지해 주는 요소들로 감싸인 루프입니다. 즉, 턴(turn) 사이에서도 유지되는 작동 상태(working state), 각 호출이 무엇을 약속하는지 명시하는 도구 계약(tool contracts), 반복적인 작업을 위한 절차(procedure), 중대한 행동 앞의 승인 경계(approval boundary), 행동이 실제로 완료되었는지 확인하는 검증 단계(verification step), 오류 발생 시 비용을 제한하는 예산 및 중단 규칙(budget and stop rules), 그리고 발생한 일을 기록하는 트레이스(trace)가 포함됩니다.
이 글은 특정 프레임워크(framework)나 플랫폼(platform)이 아니라, 프로덕션 루프의 '형태(shape)'에 초점을 맞춥니다. 하나의 실행 예시를 사용하여 데모 구현이 어디에서 더 엄격해져야 하는지를 보여줍니다.
루프는 여전히 유효합니다
파트 3에서 사용했던, 변경되지 않은 루프는 다음과 같습니다:
관찰(observe) → 결정(decide) → 실행(act) → 확인(check) → 반복(repeat)
프로덕션에서도 이 구조 자체는 변하지 않습니다. 변하는 것은 각 단어가 '포함하는 내용'입니다.
데모에서는 observe가 "마지막 도구(tool) 결과 읽기"라면, 프로덕션에서 observe는 단순히 대화 내용뿐만 아니라 작업 상태(working state)를 읽습니다. 데모에서 act가 "도구 호출"이라면, 프로덕션에서 act는 계약(contract)과 멱등성 키(idempotency key)를 사용하여 도구를 호출합니다. 데모에서 check가 "도구가 무언가를 반환했는가"라면, 프로덕션에서 check는 때때로 더 어려운 질문을 던져야 합니다. 즉, "도구가 말한 방식대로 세상이 실제로 변했는가?"입니다. 200 OK는 "요청이 수락됨"을 의미할 수 있지만, "완료됨"을 의미하지는 않습니다. 환불과 같은 작업에서 이 간극은 매우 중요합니다.
마지막 질문이 이 글의 핵심입니다. 하지만 루프의 형태는 세 파트 전과 정확히 동일합니다.
루프는 변하지 않았습니다. 구현 방식이 더 엄격해졌을 뿐입니다.
작은 빌드: 취소, 그리고 환불
하나의 구체적인 작업을 예로 들어보겠습니다. TechNova의 고객이 주문 번호 #4471에 대해 문의를 남겼습니다. 주문을 취소하고 환불받고 싶다는 내용입니다. 주문은 아직 배송 대기 중이므로 취소 요청은 정당합니다. 에이전트의 임무는 주문을 취소하고 환불을 처리하는 것입니다.
단순한(naive) 루프는 짧습니다. 에이전트가 cancel_order를 호출하고, 200 OK를 받은 뒤, issue_refund를 호출합니다. 두 개의 동작이 차례대로 수행되며 모두 성공합니다. 데모에서는 이 방식이 매번 작동합니다.
하지만 프로덕션 환경에서 이는 실제적인 위험 요소이며, 비용이 많이 드는 방식으로 실패하기 전까지는 종종 보이지 않는 상태로 남아 있습니다.
문제는 200 OK가 실제로 무엇을 의미하느냐에 있습니다. 그것은 "주문이 취소되었다"는 뜻이 아닙니다. "취소 요청이 수락되었다"는 뜻입니다. 이 둘은 같은 문장이 아닙니다. 수락된 상태와 취소된 상태 사이에는 여러 가지 일이 발생할 수 있습니다. 취소가 다른 작업 뒤에 대기열(queue)에 쌓여 여전히 보류 중일 수 있습니다. 한 서비스에서는 수락되었지만, 환불을 담당하는 서비스에는 아직 전파(propagate)되지 않았을 수도 있습니다. 재시도(retry) 과정에서 일부만 적용되었을 수도 있습니다. 에이전트가 볼 수 없는 부정 결제 검토(fraud-review) 대기 상태에 있을 수도 있습니다. 응답은 당신이 "보낸 요청"에 대해 알려준 것이지, 그 결과로 나타난 "세상의 상태"에 대해 알려준 것이 아닙니다.
따라서 단순한(naive) 루프는 아직 실제로 취소되지 않았을 수도 있는 주문에 대해 환불을 실행합니다. 이제 당신은 아직 완료되지 않은 동작을 근거로 돈을 움직인 것입니다. 이것이 바로 이 글이 다루고자 하는 실패 사례이며, 하나의 원칙으로서 명시할 가치가 있습니다:
도구 응답(tool response)은 요청을 설명하는 것이지, 반드시 세상의 상태를 설명하는 것은 아닙니다. 되돌릴 수 없는 동작(irreversible action)의 경우, 에이전트는 동작이 성공했다는 가정하에 행동하기 전에 반드시 세상의 상태를 확인해야 합니다.
우리는 나중에 루프를 강화할 때 이 문제를 해결할 것입니다. 지금은 이 퍼즐을 머릿속에 담아두세요. 에이전트는 200 OK를 믿고 그에 따라 행동했지만, 믿음은 확인(confirmation)이 아닙니다.
[

최소 아키텍처 맵 (The minimum architecture map)
루프를 수정하기 전에, 프로덕션 에이전트가 실제로 루프 주변에 필요로 하는 모든 것을 살펴보는 것이 도움이 됩니다. 데모에는 모델과 도구가 있습니다. 프로덕션 에이전트에는 더 많은 것들이 있으며, 각 요소는 그 자리에 있을 만한 이유가 있습니다.
[

맵은 다음과 같은 부분들로 구성됩니다:
- 사용자 요청 / 입력 (User request / input): 루프에 진입하는 작업입니다.
- 작업 상태 (Working state): 대화 텍스트와는 별개로, 단계 사이에서 루프가 알고 있는 정보입니다.
- 모델 결정 (Model decision): 에이전트가 다음 행동을 선택하는 단계입니다.
- 도구 레지스트리 (Tool registry): 에이전트가 호출할 수 있도록 허용된 도구들의 집합입니다.
데모에서는 도구가 프롬프트 내의 "주문을 취소합니다"와 같은 한 줄짜리 설명이 포함된 단순한 함수인 경우가 많습니다. 모델이 이를 호출하기에는 그것으로 충분합니다. 하지만 안전하게 호출하기에는 충분하지 않습니다.
프로덕션 환경의 도구에는 계약 (contract)이 필요합니다. 반드시 무거운 사양 (spec)일 필요는 없지만, 최소한 모든 도구는 다음 다섯 가지 질문에 답할 수 있어야 합니다.
- 입력 형태 (Input shape): 어떤 인자를 어떤 형식으로 받는지. (
order_id,idempotency_key등) - 출력 형태 (Output shape): 성공 시 구조적으로 무엇이 반환되는지. (상태, 식별자 등: 자유 형식의 텍스트가 아닌 기계적으로 확인 가능한 것)
- 실패 모드 (Failure modes): 도구가 작업을 수행할 수 없을 때 어떻게 동작하는지. (예외를 발생시키는지? 에러 상태를 반환하는지? 조용히 타임아웃이 발생하는지?)
- 멱등성 기대치 (Idempotency expectation): 두 번 호출해도 안전한지? 돈을 움직이거나 상태를 변경 (mutate)하는 모든 작업에 대해, 재시도가 중복 적용되지 않도록 계약에 멱등성 키 (idempotency key)를 요구해야 합니다.
- 검증 방법 (Verification method): 해당 작업이 실제로 발생했음을 어떻게 확인하는지.
cancel_order의 경우, "주문 상태를 다시 읽어 최종 취소 상태에 도달했는지 확인"하는 것입니다.
마지막 필드는 데모에서는 생략하지만 프로덕션에서는 생략할 수 없는 부분입니다. 도구 자체의 응답은 요청에 대한 주장 (claim)일 뿐이며, 검증 방법은 그 주장을 실제 세상과 대조하여 확인하는 방법입니다.
이 지점은 스키마 (schema)가 단순한 문서화를 넘어 제어 표면 (control surface)이 되는 지점이기도 합니다. 타입이 지정된 출력 형태는 도구가 무엇을 반환하는지 설명할 뿐만 아니라, 에이전트가 무엇을 유효한 결과로 취급할 수 있는지 제약 (constrain)합니다. 또한 다음 단계에서 문장이 일치하기를 기대하는 대신, 기계적으로 결과를 확인할 수 있게 해줍니다. 계약을 정의하는 것은 도구를 만드는 과정의 일부이지, 사후 고려 사항이 아닙니다.
기술: 절차를 패키징하기
도구는 에이전트에게 무엇을 호출할 수 있는지 알려줍니다. 지식 검색 (RAG, 본 시리즈의 앞부분에서 다룸)은 에이전트에게 무엇을 알고 있는지 알려줍니다. **기술 (skill)**은 에이전트에게 반복되는 작업이 어떻게 수행되어야 하는지, 즉 중요한 확인 절차를 포함하여 순서대로 진행되는 절차 (procedure)를 알려줍니다.
취소 후 환불 (cancel-then-refund) 작업의 경우, 해당 절차 (procedure)는 명확하지 않고 재사용될 가능성이 높기 때문에 패키징할 가치가 있습니다. 다음은 축약된 버전입니다:
# cancel_order_skill
name: cancel_order
...
이 파일에는 의도적인 두 가지 특징이 있습니다.
첫째, 짧다는 점입니다. 스킬 (skill)은 제목, 설명, 그리고 절차 (procedure)이지 매뉴얼이 아닙니다. 실제로는 이름과 설명만이 항상 에이전트 앞에 놓여 있을 필요가 있으며, 본문은 에이전트가 작업을 인식했을 때 불러와집니다. 절차 (procedure)는 모델이 이미 알고 있는 단계가 아니라, 틀리기 쉬운 단계(검증 단계)를 담고 있어야 합니다.
둘째, 스킬 (skill)을 만드는 유용한 방법은 작성하기 전에 미리 만드는 것이 아니라, 수동 실행이 성공한 후에 작성하는 것입니다. 작업을 한 번 실행해 보고, 어디서 실패하거나 불분명해지는지 기록한 다음, 성공했던 단계들을 포착하십시오. 상상력을 동원해 작성된 스킬 (skill)은 당신이 작업이 무엇이라고 생각하는지를 인코딩합니다. 성공적인 실행을 바탕으로 작성된 스킬 (skill)은 작업이 실제로 무엇이었는지를 인코딩합니다. 절차 (procedure)는 단순히 당신이 작동할 것이라고 기대했던 것이 아니라, 실제로 작동했던 것을 반영해야 합니다.
상태 (State)
상태 (State)는 루프 (loop)가 단계 사이에서 알고 있는 정보 중 대화 텍스트에 포함되지 않은 것입니다. 대화는 고객의 말이 존재하는 곳이며, 상태 (state)는 루프가 확립한 사실들이 존재하는 곳입니다. 이 둘을 분리해 두어야 에이전트가 매 턴마다 채팅 기록으로부터 상황을 다시 유도(re-deriving)하거나, 단순히 누군가 말했다는 이유만으로 무언가를 믿어버리는 것을 방지할 수 있습니다.
취소 후 환불 (cancel-then-refund) 작업의 경우, 작동 상태 (working state)는 최소한 다음 항목들을 보유합니다:
order_idcustomer_intentapproval_statuscancellation_statusrefund_statusverification_statusstep_countbudget_remaininglast_tool_result
여기서 중요한 필드는 상태 (status) 필드들입니다. cancellation_status는 "도구 (tool)가 반환한 값"이 아닙니다. 그것은 "우리가 세상에 대해 확인한 것"입니다. 이 둘은 서로 일치하지 않을 수 있으며, 검증 게이트 (verification gate)의 핵심 목적은 루프 (loop)가 가공되지 않은 도구 (tool) 결과가 아니라 확인된 필드를 신뢰하도록 만드는 것입니다.
이 작업 상태 (working state)는 파트 4에서 정의한 의미에서의 단기 기억 (short-term memory)입니다. 즉, 현재 케이스 (case)를 위해 애플리케이션이 관리하는 컨텍스트 (context)이며, 케이스가 종료되면 폐기됩니다. 이는 의도적으로 장기 기억 (long-term memory)이 아니도록 설계되었습니다. 이곳의 어떤 것도 케이스 간에 지속 (persist)되지 않습니다. 에이전트가 케이스 사이 (between) 에 무엇을 기억해야 하는지, 그리고 특정 인물에 대해 무엇을 보유하도록 허용할지는 파트 8에서 다룰 거버넌스 (governance) 문제입니다.
승인 (Approval)과 검증 (Verification)은 서로 다른 두 가지입니다
인간의 승인 (approval) 단계가 중대한 동작 (consequential action)을 안전하게 만든다고 가정하기 쉽습니다. 하지만 승인 그 자체만으로는 그렇지 않습니다. 승인 (approval)과 검증 (verification)은 서로 다른 질문에 답합니다.
승인 (Approval)은 의도 (intent)에 관한 것입니다. 인간은 "주문 #4471에 대해 $740 환불"을 보고 그것이 일어나야 하는지 결정합니다. 이는 중대한 동작에 대한 실제적이고 필수적인 게이트 (gate)이며, 환불 동작 이전에 위치해야 합니다.
검증 (Verification)은 결과 (outcome)에 관한 것입니다. 이는 일어나기로 되어 있었던 동작이 세상에서 _실제로 일어났는지_를 묻습니다. 승인 (approval) 단계에서는 동작이 아직 실행되지 않았기 때문에 승인이 이 질문에 답할 수는 없습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기