ByteByteGo 뉴스레터 (2026-07-22) — 원문 구조를 따르되 문장 단위로 재구성한 한국어 해설 노트
서론 — 데모와 프로덕션 사이의 간극
데모에서는 훌륭하게 작동하던 AI 에이전트가 실제 프로덕션 트래픽 앞에서는 무너지는 경우가 많다. 그 이유는 실제 고객 워크로드를 처리하는 시스템일수록 겉보기와 달리 언어모델에 대한 의존도가 훨씬 낮기 때문이다. 실제 동작의 대부분은 전통적인 결정론적(deterministic) 코드가 담당하고, 모델은 몇몇 특정 의사결정 지점에서만 호출된다. 신뢰할 수 있는 에이전트와 취약한 에이전트의 차이는 결국 "모델에게 어디까지 통제권을 주고, 어디서는 통제권을 쥐고 있을 것인가"에 대한 엔지니어링 판단에서 갈린다.
2011년 Heroku 공동창업자가 발표한 "Twelve-Factor App"이 웹 서비스를 안정적으로 배포·확장하기 위한 원칙으로 한 세대의 엔지니어들에게 영향을 준 것처럼, AI 분야에서도 "Twelve-Factor Agents"라는 원칙이 널리 공유되며 프로덕션 수준의 견고한 LLM 애플리케이션을 만들기 위한 지침으로 자리잡았다. 이후 Anthropic, Cognition, Intercom 같은 회사들이 자신들의 경험을 더하며 논의가 계속 발전해왔다.
이 글은 이런 집단적 사고를 번호가 매겨진 목록을 암기시키는 대신, 네 가지 큰 축으로 정리해 설명한다.
- 컨텍스트(Context) — 매 호출마다 모델이 무엇을 보는지를 어떻게 통제하는가
- 제어 흐름(Control Flow) — 루프와 종료 조건을 어떻게 결정론적 코드에 남겨두는가
- 상태(State) — 모델은 상태를 갖지 않게 하고, 기억은 어떻게 소프트웨어 쪽에 두는가
- 범위(Scope) — 각 에이전트를 어떻게 좁고 감독 가능한 범위로 유지하는가
이 네 가지를 따라가다 보면 "프로덕션 에이전트"에 대한 실질적인 정의와, 어떤 결정이 가장 중요한지에 대한 명확한 관점이 만들어진다.
(원문 disclaimer: 이 글은 공개적으로 공유된 여러 출처의 정보를 바탕으로 작성되었으며, 출처는 글 말미에 정리되어 있다.)
루프(The Loop) — 가장 단순한 에이전트
누구나 만들 수 있는 가장 단순한 에이전트부터 시작하면, 이후 모든 논의의 기반이 되는 구조가 드러난다.
에이전트의 본질은 하나의 루프다. 모델은 지금까지 일어난 일의 기록인 "컨텍스트"를 받아, 다음에 취할 행동에 대한 단일한 결정을 반환한다. 이 결정은 자유로운 산문이 아니라 JSON 같은 기계가 읽을 수 있는 구조화된 출력(structured output)으로 나온다. 그래야 일반 코드가 이를 읽고 실행할 수 있기 때문이다. 주변 코드는 요청된 단계를 실행하고, 그 결과를 컨텍스트에 덧붙인 뒤, 다음 결정을 위해 모델에게 다시 넘긴다. 모델이 작업이 끝났다고 신호를 보낼 때까지 이 루프는 계속된다.
모델의 역할을 이해하는 유용한 방법은, 매 호출마다 "새로 시작하는 함수"로 보는 것이다. 모델은 매 호출에서 오직 자신에게 주어진 컨텍스트만 보고, 하나의 결정을 내린 뒤, 응답하는 순간 모든 것을 잊는다. 즉 컨텍스트 윈도우(모델이 그 호출에서 읽는 텍스트 묶음)가 시스템의 기억 전부 역할을 한다.
이 설계의 매력은 분명하다. 개발자는 목표와 도구 집합만 제공하면 되고, 모델이 스스로 작업 순서를 판단한다. 모델이 의사결정을 담당하므로 분기 로직도 덜 필요하다. 데모 환경에서는 이 버전의 에이전트도 꽤 잘 작동한다. 문제는 실제 트래픽이 들어올 때 시작된다.
실패 패턴(Failure Modes)
앞서 설명한 단순한 설계는 프로덕션 조건에서 다음과 같은 예측 가능한 패턴으로 실패한다.
① 복합 오류(Compounding error)가 연쇄되는 단계마다 누적된다. 모델의 매 호출은 잘못된 결정을 내릴 확률을 조금씩 안고 있고, 자유분방한 에이전트는 이런 호출을 여러 번 연쇄시킨다. 각 단계가 95%의 확률로 옳다고 가정해보자 — 얼핏 꽤 신뢰할 만해 보인다. 그런데 이런 단계를 20번 연속으로 수행하면, 전부 성공할 확률은 대략 3분의 1 수준으로 떨어진다. 이 수치는 곱셈으로 작동하기 때문에, 개별적으로는 괜찮아 보이는 신뢰도도 긴 연쇄를 거치면 빠르게 무너진다. 프로덕션 팀들이 그토록 많은 가드레일을 추가하는 이유가 바로 여기에 있다.
② 자신 있게 틀린 답(Confidently wrong output)이 사용자에게 그대로 전달된다. 2026년, 코딩 툴 Cursor의 고객지원 에이전트가 "구독당 1기기 제한 정책"을 안내했는데, 이는 회사가 나중에 확인한 바 존재하지 않는 정책이었고 이로 인해 해지 러시가 발생했다. 1년 전에는 한 재판부가 Air Canada에 대해, 챗봇이 지어낸 상(喪) 요금 정책에 대한 책임을 물어 이를 실제로 이행하라고 판결한 사례도 있었다. 연구에 따르면 그럴듯하지만 거짓인 진술(hallucination)의 발생률은 통제된 환경에서도 3~27% 수준에 달한다. 고객과 직접 대화하는 에이전트에게 이 비율은 곧 큰 비즈니스 리스크로 직결된다.
③ 종료 조건이 약한 루프는 의도한 것보다 훨씬 오래 실행되며 토큰과 비용을 계속 소모한다.
④ 계획이 오직 모델의 컨텍스트 안에만 존재할 경우, 크래시나 장시간 작업 도중 작업 전체 맥락(state)을 잃어버릴 수 있다.
뒤의 두 패턴은 눈에 잘 띄지 않지만 그만큼 비용이 크다. 각 실패 패턴은 아래 네 가지 영역의 구체적인 실천 방법과 하나씩 대응된다.
컨텍스트(Context)
컨텍스트 윈도우는 특정 호출에서 모델이 아는 모든 것이므로, 그 내용을 통제하는 것이 신뢰성을 좌우하는 가장 크고 첫 번째 레버다. 여기서 실질적인 효과를 내는 습관은 세 가지다.
첫째, 프롬프트를 직접 소유하라. 프롬프트는 모델에 보내는 지시문 텍스트이며, 다른 소스코드와 동일하게 버전관리·리뷰·테스트의 대상이 되어야 한다. 많은 에이전트 프레임워크가 프롬프트를 자동 생성하고 보이지 않는 곳에 숨겨두는데, 이는 편리하지만 동작이 바뀌었을 때 원인을 찾기 어렵게 만든다. 프롬프트를 애플리케이션 자체 코드베이스에 두면 그 효과를 재현 가능하게 유지할 수 있다.
둘째, 컨텍스트 윈도우 자체를 소유하라. 매 호출에 무엇을 담을지는 의도적인 선택이며, 이 선택은 겉보기보다 훨씬 중요하다. 초점이 맞춰진 관련성 높은 컨텍스트를 받은 모델은, 느슨하게 관련된 이력이 잔뜩 쌓인 동일한 모델보다 더 잘 작동한다. 윈도우가 주변적인 내용으로 채워질수록 품질은 떨어진다. 따라서 현재 단계에 필요한 것 이상을 적극적으로 제거하는 "의도적 가지치기(deliberate pruning)"가 모델의 정확도를 지켜준다. 요컨대 거의 모든 호출에서 "양보다 관련성"이 이긴다.
셋째, 도구(tool)를 신경 써서 설계하라. 도구는 모델이 요청할 수 있는 함수이며, 명확한 이름과 입력 스키마로 기술된다. 이 설명이 정밀할수록 모델은 올바른 도구를 고르고 인자를 정확히 채운다. 설명이 모호하면 모델이 잘못된 도구를 고를 가능성이 커진다. 도구 정의를 세심한 인터페이스 설계의 대상으로 다루는 것은 즉각적인 성과로 돌아온다.
이 세 습관을 중심으로 "컨텍스트 엔지니어링"이라는 하나의 분야가 성장해왔지만, 핵심 아이디어는 단순하다 — 매번, 의도적으로, 모델이 무엇을 볼지 결정하라는 것이다.
제어 흐름(Control Flow)
입력을 소유하는 것은, 모델이 언제 실행되고 루프가 언제 끝날지를 주변 코드가 결정할 때만 의미가 있다. 신뢰할 수 있는 에이전트에서는 제어 흐름이 모델을 둘러싼 결정론적 코드에 속하며, 모델은 선택된 몇몇 순간에만 자문을 구하는 대상이 된다.
전체 흐름을, 단계·조건문·외부 시스템 호출로 이루어진 평범한 코드라고 상상해보자. 진짜 판단이 필요한 두세 지점에서만 시스템이 모델을 호출한다. 그 외 모든 곳은 평범한 결정론적 로직이 처리하는데, 실행 비용이 저렴하고 출력이 예측 가능하며 테스트하기도 간단하기 때문이다.
이를 작동시키는 실천적 규칙은 두 가지다.
- 모든 루프에는 탈출구가 있어야 한다. 반복 횟수 상한, 타임아웃, 명시적인 완료 조건이 함께 있어야, 모델이 무한정 계속하려 하더라도 에이전트가 반드시 멈추게 된다.
- 모델은 의도적으로 호출하라. 모델은 문제가 열린 추론(open-ended reasoning)을 요구하는 곳에 배치하고, 사전에 명세할 수 있는 나머지는 평범한 코드가 처리한다.
고객서비스 제품 Intercom이 이 패턴을 대규모로 보여준다. Intercom의 최신 "Procedures" 기능은 에이전트가 자연어로 추론하게 하면서도, 주변 시스템이 결정론적 통제 — 의사결정 지점을 위한 조건부 단계, 동일 입력에 항상 동일 출력을 보장하는 소규모 코드 스니펫, 민감한 작업 전 사람이 승인하는 체크포인트 — 를 제공한다.
요약하면, 문제를 해결하는 가장 단순한 버전에서 시작해, 더 단순한 로직으로는 부족한 부분에만 모델 주도의 자율성을 더하는 것이 바람직하다.
상태(State)
주변 코드가 루프를 소유하고 나면, 자연스럽게 이런 질문이 따라온다 — 에이전트의 기억은 어디에 있는가?
답은 모델을 무상태(stateless)로 유지하고, 상태는 애플리케이션이 통제하는 소프트웨어 쪽에 두라는 것이다.
모델은 매 호출마다 새로 시작해 오직 주어진 컨텍스트만 읽는다는 점을 떠올려보자. 이 속성을 하나의 기능으로 다루면 강력해진다. 애플리케이션이 작업의 실제 상태 — 지금까지의 대화, 계획, 진행 상황 — 를 저장하고, 매 호출마다 이로부터 컨텍스트를 재구성한다. 상태가 직렬화 가능한 저장소(시스템이 저장하고 다시 불러올 수 있는 형태)에 존재하므로, 저장된 상태를 불러와 이어감으로써 에이전트는 작업 도중 일시정지했다가 나중에 재개하거나, 크래시 이후 깔끔하게 복구할 수 있다.
이 습관은 두 종류의 상태를 정렬시키는 효과도 있다 — 모델이 이해하는 현재 상황과, 실제 데이터베이스 내용 같은 애플리케이션의 진짜 기록(ground truth)이 서로 어긋나지 않도록 유지해, 모델이 오래되거나 부정확한 상태를 근거로 행동하는 것을 막아준다.
확장성 측면의 이점도 있다. 상태가 완전히 모델 바깥에 존재하면, 서비스의 어떤 실행 인스턴스든 어떤 요청이든 받아 처리할 수 있다 — 필요한 모든 것이 컨텍스트와 함께 제공되기 때문이다. 이는 여러 인스턴스를 로드밸런서 뒤에서 동시에 운영해 훨씬 많은 트래픽을 처리할 수 있게 해준다.
여기서 모델을 이해하는 깔끔한 방식은, "현재 상태와 새 이벤트를 받아 다음 상태와 수행할 행동을 반환하는 함수"로 보는 것이다. 같은 입력이 같은 결과를 낳으므로, 전체 시스템을 테스트하고 신뢰하기가 훨씬 쉬워진다.
범위(Scope)
입력, 루프, 상태를 소유하고 나서도 남는 질문이 하나 있다 — 하나의 에이전트가 얼마나 많은 일을 맡아야 하는가? 좋은 답은, 모든 것을 다 하려는 하나의 광범위한 에이전트보다 감독 가능한 범위 안에서 작고 초점이 명확한 에이전트를 여러 개 두는 쪽을 선호하는 것이다.
단일하고 명확하게 정의된 임무를 가진 좁은 에이전트는 실패 표면이 작다. 테스트하기 쉽고, 추론하기 쉽고, 오작동 시 고치기도 쉽다. 하나의 작업이 여러 개의 서로 다른 임무에 걸쳐 있을 때는, 여러 개의 좁은 에이전트가 결정론적 오케스트레이션 아래에서 조합된다. 어떤 에이전트가 언제 실행될지는 하나의 범용 에이전트에 긴 책임 목록을 부여하는 대신, 주변 코드가 결정한다.
Klarna의 고객서비스 어시스턴트가 그 이점을 보여준다. 출시 첫 달에 약 230만 건의 대화를 처리했는데, 그중 대부분은 환불 상태 확인이나 배송 추적처럼 예측 가능한 경로를 따랐고, 이런 경우는 구조화된 로직이 잘 처리하며, 모델은 정말로 필요한 사례에만 남겨두었다.
범위의 나머지 절반은 "감독(supervision)"이다. 사람에게 넘기는 핸드오프는 하나의 정식(first-class) 단계로 설계할 가치가 있다 — 작업이 민감하거나, 확신도가 낮거나, 고객이 사람을 요청할 때 의도적으로 도달하는 지점이어야 한다. Intercom은 이를 직접 내장해, 계속 진행하는 것이 더 위험한 선택일 때마다 대화를 자동으로 사람에게 넘긴다. 사람의 개입을, 담당자를 브리핑하는 데 필요한 상태까지 갖춰 하나의 계획된 경로로 다루면, 이는 뭔가 잘못됐다는 신호가 아니라 시스템의 강점이 된다.
트레이드오프(Tradeoffs)
지금까지는 오늘날 잘 작동하는 실천법들을 설명했다. 하지만 여전히 이견과 미해결 질문들도 있다.
핵심 논쟁은 단일 에이전트 대 다중 에이전트다. 2026년 6월, 코딩 에이전트 Devin을 만든 팀은 병렬 서브 에이전트들이 독립적으로 결정을 내리다 서로 충돌한다는 이유로 멀티에이전트 설계에 반대하는 주장을 폈다. 그런데 바로 하루 뒤, Anthropic은 단일 에이전트 방식 대비 약 15배의 토큰을 사용하면서 특정 리서치 작업에서 약 90% 더 높은 점수를 낸 자사의 멀티에이전트 리서치 시스템을 소개했다.
이 겉보기 모순은 이후 몇 달에 걸쳐 하나의 공유된 패턴으로 정리됐다 — 하나의 오케스트레이터가 전체 컨텍스트를 소유하고, 고립되고 수명이 짧은 서브 에이전트들을 생성하며, 각 서브 에이전트는 하나의 작업을 완료하고 요약만 반환한다. 여기서 얻는 교훈은, 작고 초점이 명확한 에이전트들이 확고한 오케스트레이션 아래 있을 때 가장 잘 작동하는 반면, 서브 에이전트들끼리 직접 소통하게 두면 서로 충돌하는 결과를 내는 경향이 있다는 것이다.
더 근본적인 질문은 Rich Sutton이 말한 "쓰라린 교훈(Bitter Lesson)"에서 나온다. 더 많은 연산에 의존하는 범용적인 방법이 시간이 지나면서 손으로 짠 정교한 기법을 결국 앞지른다는 관찰이다. 이를 여기 적용하면, 오늘날의 모델이 부족한 부분을 메우기 위해 만든 일부 스캐폴딩이 모델 성능이 좋아지면서 불필요해질 수 있다는 경고가 된다. 이는 부분적으로는 사실이며, "과잉 엔지니어링"에 반대하는 가장 강력한 논거이기도 하다.
반론은, 이런 문제들 중 상당수가 모델 성능과 무관하게 계속 남는다는 것이다. 유한한 컨텍스트 윈도우, 문서의 5쪽에 있는 수치와 800쪽에 있는 같은 수치를 일관되게 유지해야 할 필요성, 안전하게 일시정지·재개해야 할 요구사항 — 이런 것들은 더 나은 모델이 나와도 여전히 살아남는다.
비용 역시 모든 결정에서 고려해야 할 요소다. 자율성과 멀티에이전트 설계는 토큰을 더 많이(때로는 훨씬 많이) 소모하므로, 작업의 가치가 그 비용을 정당화하는 곳에서만 제자리를 얻는다.
결론
핵심만 추리면, 프로덕션 에이전트는 대부분 결정론적 소프트웨어이며, 몇몇 의도적인 지점에서만 언어모델을 호출한다. 설계 결정은 바로 그 지점을 고르고, 모델이 스스로 결정하는 범위를 얼마나 제한할지에 있다.
이 정의에 이르는 경로는 가장 단순한 에이전트와 그 예측 가능한 실패들을 거쳐 왔으며, 각 실패에는 하나의 실천법이 대응한다.
- 모델이 보는 것을 통제하라.
- 루프를 소유하고 확실한 정지 장치를 두어라.
- 상태는 소프트웨어에 두고 모델은 무상태로 유지하라.
- 각 에이전트를 좁고 감독 가능하게 유지하라.
이 주변에는 다중 에이전트를 둘러싼 미해결 논쟁, 그리고 시간이 지나며 이런 작업 일부를 무의미하게 만들 모델의 꾸준한 성능 향상이라는 진짜 트레이드오프가 자리한다. 건전한 접근법은 문제를 해결하는 가장 단순한 설계에서 출발해, 어디서 부족한지 측정하고, 그 자율성이 분명한 가치를 제공하는 곳에서만 모델에 더 많은 자율성을 부여하는 것이다.
참고 자료 (원문 References)
- The Twelve-Factor App
- 12-Factor Agents — HumanLayer
- Anthropic — Building Effective Agents
- Anthropic — How We Built Our Multi-Agent Research System
- Cognition — Don't Build Multi-Agents
- Intercom — What's New with Fin 3
- Rich Sutton — The Bitter Lesson
- Cursor 고객지원 봇 사건 — eWeek
- Air Canada 챗봇 판결 — AI Business
핵심 요약 (20줄)
- 데모에서 잘 작동하는 AI 에이전트와 프로덕션에서 안정적으로 돌아가는 에이전트 사이에는 큰 간극이 있다.
- 실제 프로덕션 시스템은 언어모델보다 결정론적 코드에 훨씬 더 많이 의존하며, 모델은 몇몇 지점에서만 호출된다.
- 이 글은 Twelve-Factor Agents 등의 논의를 컨텍스트, 제어 흐름, 상태, 범위 네 가지 축으로 정리한다.
- 가장 단순한 에이전트는 하나의 루프다 — 모델이 컨텍스트를 보고 구조화된 결정을 내리면, 코드가 이를 실행하고 결과를 다시 컨텍스트에 붙여 반복한다.
- 모델은 매 호출마다 새로 시작해 그 순간의 컨텍스트만 보고 이전 것은 모두 잊는 "무상태 함수"처럼 동작한다.
- 이런 단순 설계는 데모에서는 잘 통하지만 실제 트래픽 앞에서 여러 실패 패턴을 드러낸다.
- 첫째 실패는 복합 오류로, 각 단계가 95% 정확해도 20단계를 연쇄하면 전체 성공 확률은 약 3분의 1로 떨어진다.
- 둘째 실패는 자신 있게 틀린 답이 고객에게 그대로 전달되는 것으로, Cursor와 Air Canada의 실제 사고 사례가 이를 보여준다.
- 셋째 실패는 종료 조건이 약해 루프가 필요 이상으로 오래 돌며 비용을 낭비하는 것이다.
- 넷째 실패는 계획이 모델 컨텍스트 안에만 존재해 크래시 시 작업 상태 전체를 잃는 것이다.
- 컨텍스트 관리의 핵심은 프롬프트를 소스코드처럼 버전관리·리뷰하고, 매 호출의 컨텍스트를 관련성 위주로 적극 가지치기하며, 도구 설명을 정밀하게 설계하는 것이다.
- 제어 흐름은 결정론적 코드가 소유해야 하며, 모델은 진짜 판단이 필요한 두세 지점에서만 호출된다.
- 모든 루프에는 반복 횟수 상한, 타임아웃, 명시적 완료 조건 같은 확실한 탈출구가 있어야 한다.
- Intercom의 Procedures 기능은 자연어 추론과 결정론적 체크포인트(사람 승인 등)를 결합한 사례다.
- 상태는 모델이 아니라 애플리케이션이 소유해야 하며, 모델은 매 호출마다 저장된 상태로부터 컨텍스트를 재구성받는 무상태 구조여야 한다.
- 상태를 소프트웨어에 두면 작업을 일시정지·재개하거나 크래시 후 복구할 수 있고, 여러 인스턴스가 로드밸런서 뒤에서 요청을 분산 처리할 수 있다.
- 범위 측면에서는 하나의 광범위한 에이전트보다, 각각 좁고 명확한 임무를 가진 여러 에이전트를 결정론적으로 오케스트레이션하는 편이 더 안전하다.
- Klarna 사례처럼 예측 가능한 경로는 구조화된 로직으로 처리하고, 모델은 정말 판단이 필요한 사례에만 남겨둔다.
- 사람에게 넘기는 핸드오프는 민감한 작업이나 낮은 확신도, 고객 요청 시 도달하는 정식 설계 요소로 다뤄야 한다.
- 단일 대 다중 에이전트 논쟁은 "하나의 오케스트레이터가 전체 맥락을 쥐고, 고립된 단명 서브 에이전트를 생성해 요약만 받는" 패턴으로 수렴하며, 비용과 Bitter Lesson(모델 발전이 일부 스캐폴딩을 무의미하게 만들 수 있다는 경고) 사이에서 균형을 잡아야 한다.