본문으로 건너뛰기

© 2026 Molayo

Dev.to헤드라인2026. 06. 30. 17:54

AI 에이전트가 데모에서는 작동하지만 실제 환경에서 실패하는 이유: 문제는 '라스트 마일'에 있다

요약

AI 에이전트가 데모를 넘어 실제 운영 환경에서 성공하기 위해 필요한 '라스트 마일' 문제를 다룹니다. 에이전트의 추론 능력보다 도구 호출 시의 엄격한 계약, 멱등성 보장, 사용자 권한 위임과 같은 엔지니어링적 설계가 핵심임을 강조합니다.

핵심 포인트

  • 도구 정의 시 Pydantic 등을 활용해 모델의 자유도를 제한하는 강력한 계약(Contract)이 필요함
  • 재시도 상황에서 중복 작업을 방지하기 위해 멱등성(Idempotency) 설계가 필수적임
  • 에이전트 자체 권한이 아닌, 요청한 사용자의 권한을 기반으로 행동하도록 설계해야 함
  • 에이전트의 추론보다 시스템 엔지니어링 계층의 결정론적 처리가 안정성을 결정함

데모는 언제나 설득력이 있습니다. 에이전트에게 "Acme의 연체된 청구서를 찾아 알림을 보내줘"라고 요청하면, 단계별로 추론하고 몇 가지 도구를 호출한 다음 성공했다고 보고합니다. 모두 고개를 끄덕입니다. 그러다가 실제 트래픽과 실시간 시스템에 투입하면, 재시도 과정에서 중복 인보이스를 생성하거나, 잘못된 연락처로 이메일을 보내거나, 조용히 실패한 작업에 대해 의기양양하게 성공했다고 보고하는 일이 발생합니다.

추론 자체가 어려운 부분은 아니었습니다. 어려운 부분은 **라스트 마일(last mile)**입니다. 에이전트가 말하는 것을 멈추고 CRM, 티케팅 플랫폼, 또는 ERP와 같은 기록 시스템(systems of record)에서 실제로 행동을 시작하는 계층이죠. 이 계층은 평범하고 화려하지 않은 분산 시스템 엔지니어링이며, AI에 특화된 부분은 거의 없습니다. 가장 중요한 패턴들을 소개합니다.

모든 도구는 제안이 아닌 계약(Contract)이다

에이전트가 '탈선'하는 가장 큰 원인은 느슨한 도구 정의입니다. 만약 도구가 자유 형식의 입력을 허용하고 모델이 행동할 것이라고 신뢰한다면, 결국 모델은 그렇게 하지 못하게 됩니다. 경계(boundary)에서 검증하고, 모델이 말로 우회할 수 없는 곳에 코드로 강력한 제한을 두어야 합니다.

from pydantic import BaseModel, Field

class SendReminder(BaseModel):
...

계약이 제거하는 것을 주목하세요: 모델은 절대 원시 이메일 주소를 제공하지 않으며, ID 형식이 일치하지 않는 인보이스를 선택하지 않고, "이미 결제됨" 확인 절차를 무효화하지 않습니다. 에이전트는 제안하고; 결정론적(deterministic) 코드가 처리합니다.

에이전트가 재시도하기 때문에,멱등성(Idempotency)이 필요하다

에이전트는 재시도합니다. 네트워크는 호출 도중에 실패합니다. 사용자는 더블 클릭을 합니다. 만약 동일한 논리적 행동이 두 번 실행되어 두 가지 효과를 낼 수 있다면, 발생할 사고가 기다리고 있는 것이며, "결제하기"나 "티켓 생성하기"와 같은 작업은 중복 실행될 경우 피해가 커지는 대표적인 예입니다.

상태 변경(state-changing) 작업을 무작위로 생성된 ID가 아닌, 의도(intent)에서 파생된 키를 사용하여 멱등하게 만들어야 합니다:

def create_ticket(account_id: str, summary: str, body: str) -> dict:
    # 동일한 논리적 요청 => 동일한 키 => 최대 하나의 티켓 생성.
    idem_key = sha256(f"{account_id}:{summary}:{body}".encode()).hexdigest()
...

만약 다운스트림 시스템 (downstream system)이 기본적으로 멱등성 키 (idempotency keys)를 지원한다면 (많은 결제 및 티켓팅 API가 그러합니다), 이를 그대로 전달하세요. 지원하지 않는다면, 호출 전 자체 레이어에서 이를 강제해야 합니다.

권한은 에이전트가 아닌 사용자에게 귀속됩니다

미묘하면서도 위험한 실수 중 하나는, 모든 에이전트 작업을 에이전트 자체의 서비스 계정 (service-account) 권한으로 실행하는 것입니다. 이렇게 되면 에이전트와 채팅할 수 있는 모든 사용자는 에이전트가 할 수 있는 모든 일을 암묵적으로 수행할 수 있게 되며, 여기에는 절대로 봐서는 안 될 기록을 읽는 것도 포함됩니다.

에이전트는 요청한 사용자를 대신하여 (on behalf of) 행동해야 하며, 모든 도구 호출 (tool call)에 해당 사용자의 권한 (authorization)을 실어 보내야 합니다. 검색 (Retrieval) 단계에서 이 실수를 하기 쉬운데, 사후에 결과를 필터링하는 방식은 취약하므로, 권한이 없는 기록이 후보가 되지 않도록 쿼리 자체의 범위를 제한해야 합니다.

def search_accounts(query: str, *, acting_user: User) -> list[Account]:
    # 사용자의 범위 (scope)는 사후 필터링이 아니라 쿼리의 일부여야 합니다.
    return crm.search(query, visibility=acting_user.account_scope)

부분적 실패에 대한 계획과 정직한 보고

다단계 작업은 때때로 중간 단계에서 실패할 수 있습니다. 최악의 결과는 세 번째 단계에서 예외 (exception)가 발생했는데도 에이전트가 "완료되었습니다!"라고 보고하는 것입니다. 두 가지 규칙을 기억하세요:

  • 모델이 검증하지 않은 성공을 서술하게 하지 마세요. 에이전트가 사용자에게 전달하는 내용은 모델의 낙관론이 아니라 도구의 결과 (tool results)에 의해 결정되어야 합니다. 호출이 실패했다면, 그 실패는 전파되어야 합니다.
  • 트랜잭션 시나리오를 미리 결정하세요. 시스템이 허용하는 범위 내에서 시퀀스를 원자적 (atomic)으로 만들거나, 보상 작업 (compensating actions)을 설계하세요 (예: 주문은 생성되었으나 결제에 실패했다면, 주문을 취소함). 조용히 절반만 완료된 워크플로우는 데이터 무결성 (data integrity)을 소리 없이 침식시키는 주범입니다.
def fulfill(order):
    created = create_order(order)        # 멱등함 (idempotent)
    try:
...

중요도가 높은 작업에는 인간의 확인 지점(Human Checkpoint)을 두세요

돈을 이동하거나, 데이터를 삭제하거나, 고객에게 연락하는 작업에 대해 완전한 자율성(Full autonomy)을 부여하는 것은 올바른 설계인 경우가 드뭅니다. 더 신뢰할 수 있는 패턴은 확신이 담긴 초안(draft)을 작성한 뒤 인간의 승인 단계(human approval step)를 거치는 것입니다. 이것이 비즈니스에서 실제로 승인할 시스템과 영원히 파일럿 단계에 머물러 있는 시스템을 가르는 결정적인 차이가 되는 경우가 많으며, 비용도 거의 들지 않습니다. 에이전트가 모든 작업을 수행하고, 인간은 되돌릴 수 없는(irreversible) 부분에 대해 승인 버튼만 클릭하면 됩니다.

임계값(threshold)을 명시적으로 설정하고 코드에서 이를 강제하세요:

def execute(action):
    if action.risk == "irreversible" or action.amount_cents > AUTO_LIMIT:
        return queue_for_human_approval(action)
...

추적(Trace)하지 않은 것은 디버깅할 수 없습니다

사용자가 "에이전트가 내 계정을 망쳐놓았다"라고 말할 때, 낙관적인 추측이나 불완전한 로그로 재구성하는 것이 아니라 정확히 어떤 일이 일어났는지 재생(replay)할 수 있어야 합니다. 모든 작업에 대해 전체 체인(full chain)을 캡처하세요: 사용자 입력(user input), 모델의 도구 선택(tool selection) 및 인자(arguments), 도구의 원시 결과(raw tool results), 그리고 최종 응답(final response)까지 포함해야 합니다. 이는 평가(evaluations)를 구축할 때 사용할 추적(trace)과 동일하므로, 한 번 설계하여 두 용도 모두에 사용하도록 만드세요.

trace_id: 8f2c...
  user: "send the overdue reminder for Acme"
  acting_user: u_4471 (scope: account:acme)
...

이렇게 하면 "에이전트가 망쳐놓았다"라는 상황은 추측 게임이 아니라 5분 내외의 조사로 끝날 수 있습니다.

핵심 요약 (The Takeaway)

AI 에이전트의 성능은 에이전트의 추론(reasoning)과 귀사의 기록 시스템(systems of record) 사이의 경계가 얼마나 견고하냐에 달려 있습니다. 지능(intelligence)은 헤드라인을 장식하지만, 신뢰성(reliability)은 지루한 계층(boring layer)에 존재합니다. 즉, 타입이 지정된 도구 계약(typed tool contracts), 멱등성(idempotency), 사용자별 권한 부여(per-user authorization), 부분 실패 처리(partial-failure handling), 되돌릴 수 없는 작업에 대한 인간의 확인 지점(human checkpoints), 그리고 엔드 투 엔드 추적(end-to-end tracing) 등이 그것입니다. 이 계층을 제대로 구축하면 에이전트는 비즈니스가 실제 업무를 믿고 맡길 수 있는 존재가 됩니다. 이를 건너뛴다면, 여러분은 매우 인상적인 데모(demo)만을 갖게 될 것입니다.

저는 Wizr AI에서 AI 엔지니어링을 담당하고 있으며, 이곳의 주 업무는 맞춤형 AI 애플리케이션 개발 서비스 (custom AI application development services)를 제공하는 것입니다. 저희에 대해 더 궁금하시다면 생성형 AI 소프트웨어 개발 기업 (generative AI software development company)로서의 활동을 더 살펴보시기 바랍니다. 댓글을 통해 통합 과정에서의 생생한 경험담(war stories)을 나누게 된다면 기쁘겠습니다.

AI 자동 생성 콘텐츠

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

원문 바로가기
0

댓글

0