프로덕션 LLM 에이전트의 신뢰성을 설계하는 12-Factor Agents

12-Factor Agents를 바탕으로 LLM 에이전트의 상태 관리, 멱등성, 제어 흐름, 관찰가능성을 설계하는 방법을 정리한다.

2026-08-14 · 최초 발행 2026-06-10

에이전트의 실패는 모델 밖에서 시작된다

HumanLayer의 Dex Horthy가 2025년 AI Engineer World's Fair에서 발표한 12-Factor Agents는 소프트웨어 엔지니어링의 고전인 12-Factor App을 LLM 에이전트 설계에 맞게 되살린 프레임워크다. 현재 GitHub 스타는 19,000개를 넘는다.

이 프레임워크가 던지는 메시지는 간단하다. 운영 환경에서 살아남는 에이전트는 자율적으로 모든 일을 해결하는 마법 같은 존재가 아니다. LLM을 필요한 지점에 배치하고, 나머지 동작을 명시적인 소프트웨어로 통제한 시스템이다.

연구에 따르면 AI 프로젝트의 85%, 에이전트 특화 프로젝트의 88%가 프로덕션 단계에 이르지 못한다. 최상위 솔루션도 실제 업무에서 목표 달성률이 55% 미만에 머문다. 모델을 전지전능한 두뇌로 가정한 채 상태 관리와 오류 처리까지 맡기면 네트워크 오류, 타임아웃, 예측하기 어려운 출력, 반복 실행의 불일치가 그대로 운영 장애로 번진다.

12-Factor Agents는 수십 개의 프로덕션 팀이 독립적으로 수렴한 패턴을 귀납적으로 정리한 결과다.

LLM 에이전트 실패 원인상태 분리(Dual State Bug)비멱등 도구 호출블랙박스 제어 흐름컨텍스트 오염12-Factor Agents 해결책신뢰할 있는 프로덕션에이전트

모델과 코드의 경계를 다시 긋는 원칙

HumanLayer가 제안한 원본 원칙과 기존 12-Factor App을 에이전트 구조에 적용한 해석이 함께 사용되고 있다. 여기서는 Horthy의 원본 프레임워크를 기준으로 운영 시스템에 필요한 경계를 짚는다.

자연어를 검증 가능한 도구 호출로 바꾼다

모델의 역할은 사용자 의도를 스키마 검증이 가능한 구조화 명령으로 변환하는 데 둔다. 자유 형식 문장을 그대로 실행에 연결하지 않고 JSON 객체처럼 명확한 도구 호출을 출력하게 한다.

# Bad: 모호한 텍스트 출력
"검색을 한번 해볼게요."

# Good: 구조화된 도구 호출
{"action": "WebSearch", "query": "2026 LLM benchmark results", "max_results": 10}

LLM은 의도를 해석하고 코드는 실행한다. 이 경계를 지키면 모델의 추출 정확도와 실제 실행 로직을 분리해 테스트할 수 있다.

프롬프트를 직접 소유한다

프롬프트는 버전 관리 대상인 코드다. 모델에 어떤 토큰이 전달되는지 직접 통제하지 못하면 결과가 달라졌을 때 원인을 추적하기 어렵다. 프레임워크 내부의 불투명한 조합 방식에 맡기기보다 변경 이력을 git blame으로 확인할 수 있는 형태로 관리해야 한다.

컨텍스트에 들어갈 정보를 선별한다

모델의 컨텍스트는 단계마다 명시적으로 구성한다. 오래된 기록은 압축하고, 관련 데이터만 구조화된 형식으로 넣는다.

어떤 워크플로우는 컨텍스트 최적화로 토큰 소비량을 2,000만 토큰에서 1,234 토큰까지 줄였다. 전략적인 컨텍스트 큐레이션은 토큰 사용량을 30~60% 절감하면서 정확도도 높일 수 있다.

도구 호출을 구조화된 출력으로 다룬다

도구는 LLM 바깥에 존재하는 특별한 능력이 아니라 JSON 스키마와 일치해야 하는 출력이다. 이 관점에서는 잘못된 JSON이나 비정상적인 SQL을 실행 전에 차단할 수 있다.

도구 실행 로직은 LLM과 독립적으로 단위 테스트할 수 있다. 환경이나 속도 제한에 따라 실행 경로를 바꾸거나, 호출 전에 별도의 검증 로직을 삽입하는 것도 가능하다.

실행 상태와 비즈니스 상태를 한 로그에 기록한다

에이전트의 진행 상태와 실제 업무 상태를 별도 저장소에 두면 둘 사이의 불일치가 발생한다. 이를 피하려면 에이전트 상호작용을 비즈니스 엔티티에 연결된 단일 이벤트 로그로 남겨야 한다.

Bad: 이중 상태 구조실행 상태 DB비즈니스 상태 DB상태 불일치 버그Good: 통합 이벤트 로그단일 불변 이벤트 스토어실행 상태 파생비즈니스 상태 파생일관성 보장

이벤트를 덧붙이기만 하는 append-only 구조에서는 세션을 재현해 디버깅하고, 장애 지점에서 복구하며, 무상태 실행기를 수평으로 확장할 수 있다.

시작과 중단, 재개를 단순한 API로 노출한다

에이전트는 정의된 체크포인트에서 멈추고 다시 실행할 수 있어야 한다. 특히 도구를 선택한 시점과 실제로 실행하는 시점 사이를 분리해야 한다.

이 간격은 고위험 작업에 인간 승인을 삽입하는 자리다. 예를 들어 데이터베이스 전체 삭제처럼 되돌릴 수 없는 작업은 실행 전에 사람의 확인을 받을 수 있다.

인간에게 연락하는 과정도 도구 호출로 만든다

인간 에스컬레이션은 예외적인 우회로가 아니라 에이전트가 선택할 수 있는 정식 액션이어야 한다. RequestHumanApproval 같은 전용 출력 스키마를 두면 요청과 응답을 이벤트 스트림 안에서 추적하고 감사할 수 있다.

이 메커니즘은 스레드를 직렬화하고 알림을 보낸 뒤 웹훅 응답을 기다리는 과정을 표준화한다. 임시 분기문에 의존하지 않으므로 승인 흐름을 구성하기도 쉽다.

제어 루프를 직접 계측한다

에이전트 내부 루프를 프레임워크의 블랙박스에 숨기지 않는다. 한 화면에 들어갈 정도의 간단한 루프를 직접 작성하고 각 지점을 관찰할 수 있게 만드는 방식이 권장된다.

직접 소유한 제어 흐름에는 브레이크포인트와 LLM-as-Judge 검증, 컨텍스트 압축, 단계별 계측, 클라이언트 측 속도 제한을 넣을 수 있다. 반복 횟수의 상한과 비수렴 감지도 같은 위치에서 다룬다.

오류를 다음 호출의 컨텍스트로 전달한다

도구 예외는 명확한 형식으로 정리해 다음 LLM 호출의 컨텍스트에 추가한다. 모델에는 호출을 수정할 기회를 주되 반복 실패 횟수는 코드에서 관리한다. 동일한 도구가 3번 연속 실패하면 인간에게 에스컬레이션한다.

사람도구에이전트사람도구에이전트(1) 도구 호출오류 응답오류를 컨텍스트에 추가(2) 수정된 호출오류 응답오류 카운터 +1(3) 재시도오류 응답에스컬레이션 요청(연속 3회 실패)개입·수정

이렇게 하면 모델의 자기 수정 가능성을 남기면서 무한 반복은 막을 수 있다.

역할이 좁은 에이전트를 연결한다

거대한 에이전트 하나에 모든 책임을 몰아넣기보다 범위가 좁은 에이전트를 조합한다. 연구에 따르면 컨텍스트가 32K 토큰을 넘으면 모델 정확도가 급격히 낮아진다.

집중된 에이전트는 310개 단계에서 90% 이상의 정확도를 보이는 반면, 모놀리식 에이전트의 정확도는 7080%다. 복잡한 워크플로우는 전문 역할을 가진 에이전트의 체인으로 나누는 편이 맞다.

인터페이스와 에이전트 로직을 분리한다

에이전트가 Slack, 이메일, 웹훅, 크론 잡 가운데 어느 곳에서 호출되더라도 같은 로직을 실행할 수 있어야 한다. 응답 역시 요청이 들어온 채널로 돌려보낼 수 있어야 한다.

일시정지와 재개, 인간 연락을 함께 설계하면 에이전트 코어를 특정 채널에 종속시키지 않을 수 있다.

에이전트를 무상태 리듀서로 구현한다

마지막 원칙은 에이전트를 내부 메모리를 가진 객체가 아니라 순수 함수로 취급하는 것이다.

agent(event_log, input) → updated_event_log

함수형 프로그래밍의 foldl과 같은 형태다. 실행에 필요한 메모리는 객체 내부에 숨기지 않고 명시적인 컨텍스트로 전달한다.

상태는 현재값이 아니라 이벤트에서 파생한다

전통적인 구현은 현재 단계와 재시도 횟수 같은 실행 상태를 한 저장소에, 주문이나 사용자 데이터 같은 비즈니스 상태를 다른 저장소에 보관한다. 한쪽만 갱신되면 두 상태가 서로 다른 현실을 가리키게 된다.

12-Factor Agents의 상태 관리 원칙은 두 상태를 단일 불변 이벤트 로그에 합치는 것이다.

Event: user_request입력: '주문 취소 요청'불변 이벤트 로그Event: tool_call도구: lookup_order결과: {order_id: 123}Event: tool_call도구: cancel_order멱등 키: req-abc-123결과: 성공Event: human_approval승인자: admin타임스탬프: 2026-05-31실행 상태 파생(현재 단계, 재시도 횟수)비즈니스 상태 파생(주문 상태, 사용자 데이터)

로그를 재생하면 특정 시점의 상태를 복원할 수 있고, 스레드를 직렬화하면 중단했던 위치에서 실행을 이어갈 수 있다. 실행기는 무상태이므로 여러 인스턴스가 같은 로그를 처리하도록 수평 확장할 수도 있다.

재시도는 멱등성을 전제로 설계한다

분산 시스템에서는 네트워크 오류와 타임아웃에 따른 재시도를 피할 수 없다. 결제나 이메일 발송처럼 부작용이 있는 작업을 같은 요청으로 두 번 실행하면 문제가 커진다. 모든 의도에는 멱등 키를 포함해 중복 실행을 차단해야 한다.

신규 요청이미 처리됨에이전트 재시작멱등확인도구 실행캐시된 결과 반환결과 저장(키 + 결과)중복 실행 방지성공 응답

실행 상태를 영속화하고 멱등 재시작을 지원하면 장애 복구 뒤 같은 요청이 다시 전달돼도 안전하게 처리할 수 있다.

비결정적인 시스템을 추적하는 방법

고전적인 분산 추적은 결정론적 시스템을 대상으로 설계됐다. LLM은 같은 프롬프트에도 매번 다른 출력을 만들 수 있고, 환각률이나 근거 점수 같은 품질 지표도 시간에 따라 달라진다.

에이전트 워크플로우에서는 루트 스팬 아래에 도구, 추론, 상태 전이, 인간 개입을 구분해 기록한다.

루트 스팬(단일 요청 전체)도구 스팬name, args, outputduration, retry_count,error추론 스팬모델의 계획 액션 선택상태 전이 스팬before_state, after_state인간 개입 스팬approver, timestamp,decision

OpenTelemetry를 적용할 때는 W3C Trace Context로 서비스 경계를 넘는 인과관계를 유지한다. HTTPx와 FastAPI 같은 표준 컴포넌트는 자동 계측하고, MCP 서버나 커스텀 에이전트 루프처럼 특수한 영역은 수동으로 계측한다.

운영 지표도 일반 서비스와 조금 다르게 잡아야 한다.

지표 설명 알림 임계값 예시
단계별 레이턴시 각 LLM 호출 및 도구 실행 시간 p99 > 10s
토큰 비용 추적 워크플로우별 실행 비용 일 예산 초과
오류 분류 도구 실패, 타임아웃, 파싱 오류 오류율 > 5%
인간 에스컬레이션율 자동화 실패 비율 에스컬레이션 > 10%
연속 오류 횟수 동일 도구 반복 실패 연속 3회 초과
컨텍스트 크기 토큰 사용량 추이 32K 토큰 근접

무상태 리듀서를 코드로 옮기기

내부 상태를 가진 에이전트는 테스트와 재현에 필요한 정보가 객체 안에 감춰진다. 이벤트 로그와 새 입력만 받는 함수로 바꾸면 상태의 출처가 명확해진다.

# 전통적인 상태 유지 에이전트 (안티패턴)
class StatefulAgent:
    def __init__(self):
        self.memory = {}      # 내부 상태
        self.step = 0         # 실행 상태
    
    def run(self, input):
        # 상태가 클래스 내부에 숨겨져 있어 테스트·재현 불가
        ...

# 무상태 리듀서 에이전트 (권장 패턴)
def agent_step(event_log: list[Event], new_input: Input) -> list[Event]:
    """
    순수 함수: 이벤트 로그 + 새 입력 → 업데이트된 이벤트 로그
    내부 상태 없음. 테스트 가능. 재현 가능.
    """
    context = build_context_from_log(event_log)
    llm_response = call_llm(context, new_input)
    tool_result = execute_tool(llm_response.tool_call)
    
    new_event = Event(
        input=new_input,
        llm_response=llm_response,
        tool_result=tool_result,
        idempotency_key=generate_key(new_input)
    )
    return event_log + [new_event]

이 패턴에서는 이벤트 로그와 입력만으로 모든 상태를 재현할 수 있다. 프로덕션 버그가 발생하면 로그를 재생해 같은 상황을 만들고, 특정 지점에서 스레드를 분기해 다른 시나리오를 시험할 수도 있다. 인스턴스 내부에 상태가 없으므로 실행기 수를 늘리는 일도 단순해진다.

프레임워크의 편의보다 통제권을 본다

12-Factor Agents는 특정 라이브러리를 선택하라고 요구하지 않는다. 대신 LangChain이나 AutoGPT 같은 프레임워크가 프롬프트와 제어 흐름을 과도하게 추상화해 블랙박스로 만들 수 있다고 경고한다.

직접 구현하는 에이전트 루프에는 LLM 호출, 응답 파싱과 도구 디스패치, 이벤트 로그 기록, 수렴 확인 또는 인간 에스컬레이션, 반복 한계 확인이 들어간다. 전체 흐름은 한 화면에서 파악할 수 있을 만큼 단순하게 유지한다.

핵심 판단 기준은 프레임워크 사용 여부 자체가 아니다. 모델에 전달되는 입력과 실행 경로, 상태 변경을 팀이 직접 확인하고 통제할 수 있는지가 기준이다.

기존 시스템에 적용할 순서

낮은 위험으로 시작할 수 있는 작업은 도구 호출의 JSON 스키마 검증, 프롬프트 버전 관리, 반복 한계 설정, 연속 오류 카운터와 에스컬레이션 로직 추가다.

  • 모든 도구 호출에 JSON 스키마 검증 추가
  • 프롬프트를 코드로 버전 관리
  • 에이전트 루프에 반복 한계 설정
  • 연속 오류 카운터와 에스컬레이션 로직 추가

그다음에는 상태와 승인 흐름을 손본다. 이 단계는 아키텍처 변경 범위가 커지므로 중간 위험에 해당한다.

  • 이중 상태를 단일 이벤트 로그로 마이그레이션
  • launch/pause/resume API 엔드포인트 구현
  • 인간 승인을 RequestHumanApproval 도구 호출로 표준화
  • 컨텍스트 윈도우 관리 로직 커스텀화

장기 전환에서는 에이전트의 경계와 실행 모델 자체를 바꾼다. 모놀리식 구조를 분해하고 무상태 리듀서로 옮기며 추적 체계와 채널 계층을 분리하는 작업이다.

  • 모놀리식 에이전트를 전문화된 소규모 에이전트로 분해
  • 무상태 리듀서 패턴으로 전면 전환
  • OpenTelemetry 기반 4-스팬 추적 체계 구축
  • 에이전트 독립적인 채널 인터페이스 레이어 구현

12-Factor Agents가 다루는 것은 모델의 지능이 아니라 그 지능을 운영 가능한 시스템 안에 가두는 방법이다. 상태 통합과 멱등 실행, 무상태 리듀서, 에이전트 전용 관찰가능성을 함께 설계해야 장애를 재현하고 복구할 수 있다. 프로덕션에 안착한 에이전트들은 이 이름을 알지 못했더라도 이미 같은 방향의 원칙을 따르고 있었다.

Sources

LLM 에이전트이벤트 소싱멱등성관찰가능성상태 관리