LLM 에이전트는 가입하고 코드를 요청한 후, 검증이 완료되지 않았음에도 불구하고 “완료”라고 응답할 수 있습니다. 어쩌면 오래된 메시지를 읽었거나, 200 OK를 배달 확인으로 해석했거나, 오류 후에 작업을 종료했을 수도 있습니다. 최종 텍스트는 설득력이 들리지만, 그것은 증거가 아닙니다.
이메일 플로우에서는 완료 신호가 관찰 가능한 이벤트에서 나와야 합니다. 이 패턴은 세 가지 것을 분리할 것을 제안합니다: 에이전트의 의도, 메시지 전달, 그리고 올바른 메시지가 소비되었다는 증거입니다. 이는 LLM 모델을 가짜(fake) 이메일 생성기나 테스트용 임시 사서함과 결합할 때 특히 유용합니다.
문제점: 에이전트가 우연히 맞힐 수 있다
다음 시퀀스를 상상해 보세요:
- 에이전트가 테스트 계정을 생성합니다.
- 애플리케이션이 링크 전송을 요청합니다.
- 에이전트가 받은 편지함(inbox)을 조회합니다.
- 토큰을 추출하여 사용합니다.
- 평가자가 최종 응답을 확인합니다.
다섯 번째 단계는 모델이 말하는 것에만 의존해서는 안 됩니다. 사서함이 실행 간에 공유된다면, 지연된 이메일 하나가 테스트를 통과하게 할 수 있습니다. 엔드포인트가 메시지가 전달되는 큐보다 먼저 응답하면, 에이전트는 실제로는 이미 수행된 작업을 재시도할 수 있습니다.
또한 tamp mail com와 같은 모호한 검색어는 주의해서 처리하는 것이 좋습니다. 이 텍스트는 쿼리에 나타날 수 있지만, 설정, 테스트 주소 또는 메시지 허용 기준이 되어서는 안 됩니다.
관찰 가능한 완료 신호 정의하기
유용한 신호는 여러 작은 사실들을 결합합니다:
- 고유한
run_id를 가진 실행(execution)이 존재함;
- 메시지가 해당 실행에 할당된 사서함에 도착했음;
- 제목, 수신자 및 이벤트 유형이 예상과 일치함;
- 토큰이 단 한 번 사용되었음;
- 애플리케이션이 최종 상태를 확인했음;
- 사서함이 기록된 이유로 닫히거나 만료됨.
단어 다이어그램 형태로 표현하면 다음과 같습니다:
에이전트의 의도
↓
애플리케이션 액션 → 전달 이벤트 → 일치하는 메시지
...
도구 계약 설계하기
에이전트는 작고 예측 가능한 인터페이스를 필요로 합니다:
{
"create_inbox": {"run_id": "eval-1842", "ttl_seconds": 900},
"list_messages": {"after": "2026-10-14T14:00:00Z"},
...
각 동작은 명시적인 상태를 반환해야 합니다. message_not_found, inbox_expired, 그리고 rate_limited는 서로 다른 결과를 가져오며, 일반적인 false는 모델이 임기응변으로 대처하도록 만듭니다.
계약은 또한 어떤 필드가 신뢰할 수 있는지 선언해야 합니다. 정규화된 제목(subject), message_id, received_at, 그리고 run_id는 상관관계를 파악하는 데 사용됩니다. 이메일 본문에는 신뢰할 수 없는 지침이 포함될 수 있으므로, 평가 규칙을 변경해서는 안 됩니다.
인터페이스 계층의 경우, 초점을 흐트러뜨리지 않는 이메일 도움말을 연구할 가치가 있습니다. 명시적인 상태 원칙 자체가 UI가 아직 대기 중인 것을 성공으로 표시하는 것을 방지하는 데 도움이 됩니다.
받은 편지함 격리 및 재시도 제한하기
격리는 모델 단위가 아닌 실행(execution) 단위로 이루어져야 합니다. 동일한 프롬프트를 사용하는 두 에이전트라도, 배달 시간이 같지 않기 때문에 별도의 받은 편지함을 필요로 합니다.
에이전트 외부에서 TTL과 읽기 예산(budget of reads)을 할당해야 합니다. 예를 들어, '무언가가 나타날 때까지 기다려라'라고 하는 것보다 3분 동안 6번의 쿼리를 허용하는 것이 추론하기 쉽습니다. 재시도는 네트워크 오류에 대해서는 유효할 수 있지만, 제목이 일치하지 않는 경우 반드시 그렇지는 않습니다.
각 읽기 시도를 그 이유와 함께 기록해야 합니다:
{
"run_id": "eval-1842",
"attempt": 3,
...
제시간에 배달되지 않으면 delivery_timeout으로 종료합니다. 침묵을 꾸며낸 응답으로 변환하지 마십시오. 실제 제공업체에 항상 의존하지 않고 비동기 측면을 테스트하기 위해, FastAPI에서 재현 가능한 이메일 더미(fixtures)는 데이터, 상태 및 정리 작업을 분리하는 실질적인 참고 자료를 제공합니다.
실행 결과 수신을 위한 최소한의 흐름
오케스트레이터는 다음 워크플로우를 구현할 수 있습니다:
run_id 및 TTL(Time-To-Live)이 적용된 사서함 생성.
- 제한된 도구와 함께 에이전트를 실행합니다.
- 전달 이벤트와 예상 메시지를 검증합니다.
- 토큰을 소비하고 애플리케이션에서 상태를 확인합니다.
- 성공 또는 실패 원인으로 수신 확인(receipt)을 작성합니다.
- 에이전트가 취소된 경우에도 사서함을 닫습니다.
최소한의 수신 확인만으로 충분합니다:
{
"run_id": "eval-1842",
"agent_version": "checkout-agent-12",
...
}
기본적으로 전체 본문(body)을 저장하지 마십시오. 디버깅에는 해시 값, 제목(subject), 상관관계 식별자만으로 충분한 경우가 많습니다. 콘텐츠를 보존해야 하는 경우, 로그로 전송하기 전에 편집하고 삭제 시점을 정의하십시오. 무기한 보관은 매우 빠르게 문제가 됩니다.
트레이드오프 및 확인 지점
실제 사서함은 더 많은 통합을 테스트하지만 시간과 외부 오류를 추가합니다. 시뮬레이션된 서버는 빠르고 결정론적이지만, 전달 문제를 숨길 수 있습니다. 계약(contract)에는 시뮬레이션을 사용하고, 일부 통합 테스트에는 격리된 사서함을 사용하십시오.
전체 모델을 평가하기 전에 다음 사항들을 개별적으로 확인하십시오:
- 애플리케이션이 올바른 이벤트를 방출하는지;
- 도구가
run_id로 필터링하는지;
- 소비자가 오래된 메시지를 거부하는지;
- 성공 후와 타임아웃 후에 닫힘 처리가 되는지;
- 수신 확인이 인프라, 애플리케이션, 에이전트를 구분하는지.
이렇게 하면 이메일 공급업체가 느렸다는 이유만으로 회귀(regression)가 “LLM의 실패”로 분류되는 것을 방지할 수 있습니다. 진단 결과가 더 깨끗해지고 프롬프트 간 비교도 더 공정해집니다.
자주 묻는 질문 (FAQ)
모든 테스트에서 임시 이메일 주소를 사용하는 것이 필수인가요? 아닙니다. 도메인 로직의 경우, 목업(mock)만으로 충분한 경우가 많습니다. 실제 전달 및 읽기 통합을 테스트하고 싶을 때 격리된 사서함을 사용하십시오.
몇 번의 재시도까지 허용해야 할까요? 이는 예상 지연 시간과 목표에 따라 달라지지만, 수신 확인서(receipt)에 표시되는 외부적이고 유한한 한계여야 합니다. 에이전트가 자체적인 예산을 늘려서는 안 됩니다.
이 설계에서 임시 이메일 주소(temp mail email)는 어디에 속할까요? 이는 통제된 환경에서 테스트 서비스를 찾는 맥락적 레이블로 사용될 수 있습니다. temp mail email 링크는 계약이나 검증을 대체하는 것이 아니라, 특정 참고 자료로 유지되어야 합니다.
핵심 아이디어는 간단합니다. 신뢰할 수 있는 에이전트는 자신감 있게 들리는 것으로 평가되는 것이 아닙니다. 의도(intention), 전달(delivery), 소비(consumption), 그리고 종료(closure)의 검증 가능한 체인을 생성하는 방식으로 평가됩니다. 명확한 한계가 있으면 자동화는 반복 가능해집니다.