AI 에이전트를 빨리 만들수록 더 자주 빠지는 함정이 하나 있습니다. 로컬에서는 잘 되는데, 실제 운영에 올리면 속도도 느리고 실패 원인도 안 보이고 비용만 샙니다. Google Developers Blog의 글인 Production-Ready AI Agents: 5 Lessons from Refactoring a Monolith(https://developers.googleblog.com/en/production-ready-ai-agents-5-lessons-from-refactoring-a-monolith/)는 바로 이 문제를 정면으로 다룹니다. 핵심 메시지는 간단합니다. 하나의 거대한 프롬프트와 하나의 긴 파이썬 스크립트로 묶인 모놀리식 에이전트는 데모용으로는 괜찮아도 운영용으로는 버티기 어렵다는 겁니다. 이 글에서 다룬 Titanium 사례는 영업 리서치 에이전트지만, 문제 구조는 거의 모든 실무 에이전트에 그대로 적용됩니다.
처음 프로토타입을 만들 때는 하나의 LLM 호출 흐름 안에 검색, 정리, 선택, 작성까지 다 넣는 편이 빠릅니다. 그런데 운영으로 가면 네 가지 문제가 반복됩니다. 첫째, 한 단계 실패가 전체 실패가 됩니다. 둘째, 출력 형식이 흔들릴수록 파서와 예외 처리 코드가 기하급수로 늘어납니다. 셋째, 하드코딩된 컨텍스트가 금방 낡습니다. 넷째, 어디서 시간이 쓰였고 어디서 토큰이 샜는지 관측이 안 됩니다.
결국 모놀리식 구조의 본질적 문제는 모델이 부족해서가 아니라 책임 분리가 없어서 생깁니다. 하나의 호출에 너무 많은 판단을 몰아넣으면, 실패했을 때 무엇을 고쳐야 하는지 찾기도 어려워집니다.
원문에서 가장 먼저 손댄 부분도 이겁니다. Company Researcher, Search Planner, Case Study Researcher, Selector, Email Drafter처럼 역할을 분리했습니다. 이 구조가 좋은 이유는 단순 병렬화가 아니라 실패 범위를 줄인다는 데 있습니다.
예를 들어 검색 단계가 실패했을 때 초안 작성 단계까지 같이 무너지는 게 아니라, 검색 결과만 다시 재시도하거나 대체 데이터 소스로 교체할 수 있습니다. 실무적으로는 아래 질문으로 나누면 됩니다.
이렇게 나누면 프롬프트도 짧아지고 디버깅도 쉬워집니다.
모놀리식 에이전트가 가장 많이 망가지는 지점이 JSON 출력입니다. 프롬프트 안에 길게 형식을 설명하고, 모델이 조금만 어기면 문자열 치환과 재파싱으로 수습하는 식이죠. 글에서 권한 방법은 Pydantic 기반의 구조화 출력 강제입니다.
이 방식의 장점은 세 가지입니다. 첫째, 출력 계약이 코드 레벨에서 명확해집니다. 둘째, 프롬프트 토큰을 줄일 수 있습니다. 셋째, 실패 원인이 “모델이 딴말함”이 아니라 “어느 필드 검증이 깨졌는가”로 바뀝니다.
실무 팁은 명확합니다. 자연어 프롬프트는 의미를 설명하고, 데이터 모양은 스키마가 담당하게 하세요. 특히 운영 파이프라인에서는 이 한 가지 원칙만 지켜도 재시도 비용이 크게 줄어듭니다.
Titanium의 원래 문제 중 하나는 12개의 케이스 스터디가 코드에 박혀 있었다는 점입니다. 이건 거의 모든 사내 에이전트의 축소판입니다. 처음에는 정적인 문서 몇 개 넣고 만족하지만, 실제로는 데이터가 계속 바뀝니다.
원문은 Playwright 크롤러와 Vector Search를 붙여 케이스 스터디를 계속 갱신했습니다. 중요한 건 도구 이름이 아닙니다. 핵심은 지식을 코드 배포와 분리했다는 점입니다. 에이전트가 쓰는 컨텍스트는 애플리케이션 릴리즈 사이클보다 더 자주 갱신돼야 합니다. 그래야 문서가 바뀌었을 때 코드 수정 없이 반영할 수 있습니다.
실무에서는 최소한 다음을 분리해야 합니다.
에이전트가 실패했을 때 “아무튼 잘 안 됨” 수준의 로그만 남으면 진짜 답이 없습니다. 원문이 OpenTelemetry를 강조한 이유도 여기 있습니다. 모델 요청, 토큰, 도구 실행, 단계별 지연시간이 다 보여야 병목을 줄일 수 있습니다.
현실적으로 가장 먼저 봐야 할 지표는 이 정도입니다.
관측이 생기면 “모델을 바꿔야 하나”와 “검색 단계만 고치면 되나”를 분리할 수 있습니다. 이 차이가 운영 비용을 크게 가릅니다.
에이전트 비용이 무섭게 불어나는 이유는 대개 모델 단가보다 루프 제어 실패입니다. 에러가 났는데 무한 재시도하고, 실패한 도구를 계속 다시 부르고, 비슷한 문맥을 매번 새로 읽으면 토큰이 눈덩이처럼 커집니다.
원문이 말하는 교훈은 명확합니다. 재시도 횟수, 타임아웃, 백오프, 단계별 종료 조건 같은 회로 차단기를 프레임워크 수준에서 먼저 두라는 겁니다. “일단 되게 만들고 나중에 최적화”가 가장 비싼 선택이 되는 이유가 여기 있습니다.
첫째, 현재 에이전트 흐름을 종이에 적으세요. 검색, 검증, 선택, 작성, 저장, 발송처럼 단계가 보일 겁니다. 둘째, 단계별 입력과 출력을 명시하세요. 셋째, 그중 구조화 가능한 결과부터 스키마로 바꾸세요. 넷째, 데이터 소스가 코드에 박혀 있으면 분리하세요. 다섯째, 관측 로깅을 넣고 한 주만 돌려도 어디가 병목인지 보입니다.
핵심은 거창한 재작성보다 실패 원인을 분리 가능한 구조로 만드는 겁니다. 에이전트 운영이 어려운 이유는 LLM이 불완전해서도 맞지만, 대부분은 시스템 설계가 실패를 설명할 수 없는 상태라서 더 어렵습니다. 이 글은 그걸 꽤 현실적으로 보여줍니다.