이메일을 보내는 LLM 에이전트는 간단한 자동화처럼 보입니다. 하지만 실제 운영 환경에서는 여러 가지 숨겨진 질문들이 있습니다. 어떤 신원을 사용했는지? 메시지를 받은 수신자는 누구인지? 한 번만 실행되었는지? 실제 사서함에 손대지 않고도 이 경우를 반복할 수 있는지?
해답은 모델에게 이메일 제공업체에 대한 직접적인 접근 권한을 주는 것이 아닙니다. 대신, 에이전트, 이메일 기능(capability), 그리고 테스트 사서함 사이에 작은 계약을 구축해야 합니다. 이러한 접근 방식은 팀이 온보딩 흐름, 계정 복구 또는 AI가 생성한 알림을 검증할 때 유용합니다.
문제점: 자신이 무엇을 했는지 증명할 수 없는 에이전트
모델은 다음 단계를 결정하는 데 능숙하지만, 운영상의 경계까지 스스로 결정해서는 안 됩니다. 프롬프트는 “확인 이메일을 보내라”고 요청할 수 있지만, 시스템은 수신자, 템플릿, 환경, 권한, 그리고 결과를 확인해야 합니다.
실용적인 설계는 세 가지 구성 요소를 분리합니다:
- LLM 에이전트: 구조화된 인수를 사용하여 행동을 제안합니다.
- 이메일 어댑터 (Email Adapter): 해당 행동을 검증하고 관찰 가능한 이벤트로 변환합니다.
- 테스트 사서함 (Test Inbox): 개인 정보나 실제 운영 데이터와 섞이지 않은 상태에서 메시지를 수신합니다.
다시 말해, 에이전트가 제안하고; 결정론적 계층(deterministic layer)이 해당 제안이 실행 가능한지 여부를 결정합니다. 이는 위험을 낮추고 오류를 찾기 쉽게 만듭니다.
신원, 기능, 증거 분리하기
테스트의 경우, 임시 주소를 사용자의 신원이 아닌 만료 기한이 있는 '기능(capability)'으로 취급하는 것이 좋습니다. 구체적인 실행을 위해 **임시 이메일**을 생성하고, 오직 해당 실행 식별자, 테스트 수신자, 그리고 템플릿의 해시 값만 저장할 수 있습니다.
구분은 중요합니다. '신원(Identity)'은 "누가 사용자이냐"에 답하고, '수신 가능성(Capability)'은 "이 테스트 케이스가 무엇을 받을 수 있느냐"에 답합니다. 만약 에이전트가 허용된 목록 외의 주소로 전송을 시도하면, 모델이 유효한 JSON을 생성했더라도 어댑터는 해당 작업을 거부해야 합니다.
또한, 사생활 보호 측면의 이점이 있습니다. 테스트 콘텐츠가 개인 받은 편지함으로 전송될 필요가 없습니다. 최소 컨텍스트를 이용한 이메일 승인을 설계하는 팀의 경우, 이 제한은 실험적인 승인이 실제 승인과 혼동되는 것을 방지합니다.
간단한 상태 기계 (State Machine)
거대한 플랫폼이 필요하지 않습니다. 명시적인 전이(transition)를 가진 상태 기계만으로도 충분한 경우가 많습니다:
CREATED -> RESERVED -> SENT -> OBSERVED -> VERIFIED
|
v v v
...
각 전이는 run_id, agent_step, message_id 및 타임스탬프를 포함해야 합니다. SENT 상태는 공급자가 메시지를 수락했음을 의미하며, 테스트가 끝났다는 것을 의미하지 않습니다. OBSERVED는 받은 편지함이 무언가를 받았음을 나타내고, VERIFIED는 콘텐츠, 링크 및 수신자가 계약을 충족함을 의미합니다.
이 세부 사항은 에이전트가 확인을 받지 못했다고 해서 재시도하는 흔한 오류를 방지합니다. 첫 번째 메시지는 실제로 도착했지만 말입니다. 반복하기 전에, 오케스트레이터는 message_id와 실행 상태를 조회해야 합니다. 여기서 멱등성(Idempotency)은 "다시 시도"라는 지침보다 더 가치가 있습니다.
구현 및 검증 포인트
최소 계약(Minimum contract)은 다음과 같을 수 있습니다:
어댑터는 알 수 없는 필드, 허용되지 않은 도메인, 존재하지 않는 템플릿을 거부해야 합니다. 또한, 에이전트가 전송이 성공했다고 지어내기보다는 다음 제안을 수정할 수 있도록 사람이 읽을 수 있는 거부 이유를 기록해야 합니다.
결과를 관찰하려면 구체적인 신호를 기다려야 합니다: 수신된 메시지, 정확한 제목, 스테이징 호스트가 포함된 링크, 외부 수신자의 부재. 명확한 인터페이스는 팀이 이러한 상태를 이해하는 데 도움을 줍니다. 따라서 플로우가 대시보드에 표시될 때는 재시도에 대한 예측 가능한 UI를 적용하는 것이 좋습니다.
또한 받은 편지함이 얼마나 오래 유지되어야 할지 결정해야 합니다. 짧은 TTL(Time To Live)은 노출을 줄이지만, 너무 짧은 TTL은 공급업체가 지연될 때 가짜 실패를 유발합니다. 환경에서 관찰된 최악의 경우를 기준으로 시간을 선택하고, 사서함이 만료될 때 그 이유를 기록해야 합니다. 케이스가 스키마와 링크만 확인하는 것이라면 전체 본문을 보존할 필요는 없습니다.
실제 테스트 중에는 "tempail mail" 또는 "tepm mail com"과 같은 구문으로 검색이 나타날 수 있습니다. 이것들은 일반적인 오타이며 제품 계약이 아닙니다: 이를 변수 이름, 앵커(anchor), 유효성 검사 로직에서 제외해야 합니다. 시스템은 텍스트의 모호한 의도에 의존하는 것이 아니라 안정적인 식별자에 의존해야 합니다.
운영 체크리스트 (Operation Checklist)
- LLM이 하나의 액션만 제안하고 어댑터가 그 한계를 검증하는가?
- 모든 전송물에 조회 가능한
run_id와 message_id가 있는가?
- 수신자가 허용된 테스트 환경에 속하는가?
SENT에서 VERIFIED로의 전환이 관찰 가능한 증거에 의존하는가?
- 재시도가 Idempotent(멱등성)하며 제한이 있는가?
- 받은 편지함과 그 메시지에 정의된 TTL이 있는가?
- 대시보드가 문제가 모델, 공급업체(provider), 또는 검증 중 어느 것에서 발생했는지 설명하는가?
이 패턴은 의도적으로 작습니다. 격리된 메일 기능(isolated email capability), 상태 머신(state machine), 그리고 충분한 증거는 모든 실험을 실제 전송으로 변환하지 않고도 LLM 에이전트를 테스트할 수 있게 합니다. 이후 이벤트 유형이 증가하면 정책을 추가할 수 있습니다. 핵심 경계는 동일하게 유지됩니다: 모델이 의도를 결정하고 시스템이 그 영향을 제어합니다.