LLM 실패를 감지하고 폴백하는 플러그인 하루 만에 만들기

26년 04월 29일

문제는 세션이 죽는 거다

이날은 하루 종일 하나의 플러그인을 만든 날이었다.

LLM 에이전트가 작업하다가 API 호출이 실패하면 세션이 죽어버리는 문제가 있었다.

그걸 막기 위한 폴백 플러그인을 만들었다.

30개 커밋에 npm 릴리스 7개를 하루 만에 쏟아냈다.

AI 에이전트가 작업을 하다가 LLM API 호출이 실패하면, 그냥 세션이 죽었다.

작업 내역이 다

날아간다.

실패 이유는 다양하다.

속도 제한, 인증 에러, 할당량 소진, 서버 일시적 장애. 근데 실패 이유가 뭐든 간에, 에이전트가 멈추면 처음부터 다시 해야 한다.

이건 너무 비싸다.

그래서 실패를 감지하고 백업 모델로 전환하는 플러그인을 만들었다.

세션이 죽지 않고 다른 모델로 넘어가서 계속 작업하는 거다.

에러를 두 종류로 나누다

핵심 통찰은 에러가 다

똑같지 않다는 거였다.

어떤 에러는 영구적이다.

인증 실패, 할당량 소진, 결제 문제. 이런 건 재시도해도 같은 결과가 나온다.

그러니까 바로 백업 모델로 넘어가야 한다.

어떤 에러는 일시적이다.

속도 제한, 서버 과부하. 이런 건 잠시 기다렸다가 재시도하면 될 수도 있다.

그러니까 지수 백오프로 재시도를 먼저 해보고 그래도 안 되면 넘어간다.

이 두 가지를 구분하는 게 핵심이었다.

영구 에러에 재시도를 낭비하지 않고 일시 에러에 너무 빨리 포기하지 않는 거다.

모델 단위 추적

그리고 실패 추적을 프로바이더 단위에서 모델 단위로 바꿨다.

처음에는 프로바이더 단위로 추적했다.

OpenAI가 고장 났으면 OpenAI 전체를 피하는 식이다.

근데 이러면 한 모델이 고장 났을 때 같은 프로바이더의 다른 모델까지 못 쓴다.

한 모델이 실패했다고 해서 같은 프로바이더의 다른 모델까지 실패하는 건 아니다.

그래서 모델 단위로 추적했다.

실패한 모델만 피하고 같은 프로바이더의 다른 모델은 계속 쓴다.

이게 훨씬 정확하다.

시간 제한 쿨다운

또 하나 중요한 결정이 있었다.

실패한 모델을 영구적으로 블랙리스트에 넣지 않는 거다.

처음에는 실패한 모델을 집합에 넣어서 영구적으로 안 쓰게 만들었다.

근데 이러면 일시적 에러로 실패한 모델이 복구돼도 다시 안 쓴다.

속도 제한은 잠깐 걸리는 건데, 영원히 못 쓰게 되는 거다.

그래서 시간 제한 쿨다운으로 바꿨다.

실패한 모델은 60초 동안만 피한다.

60초가 지나면 다시 쓸 수 있게 복구된다.

일시적 에러는 잠깐이니까, 쿨다운이 끝나면 다시 시도해보는 게 맞다.

컨텍스트가 찰 때 큰 모델로

이날의 하이라이트 기능은 컨텍스트 폴백이다.

에이전트가 작업하다가 컨텍스트 윈도우가 차면, 자동으로 컨텍스트가 더 큰 모델로 전환한다.

큰 모델이 작업을 끝내고 쉴 때가 되면, 자연스럽게 컨텍스트가 압축된다.

그러면 원래 모델로 다시 돌아간다.

컨텍스트가 찼다고 작업이 멈추면 안 된다.

큰 모델로 넘어가서라도 작업을 이어가는 게 낫다.

이 기능이 그걸 가능하게 만들었다.

이 기능은 첫 번째 버전이었다. 이후 몇 주에 걸쳐 포크 기반 구조랑 상태 머신 구조로 더 발전했다.

근데 핵심 아이디어는 이날 잡은 거다.

컨텍스트가 차면 큰 모델로 넘어가고 압축되면 돌아온다.

설정 없이 바로 쓰게

사용자 경험도 신경 썼다.

처음 실행하면 설정 파일을 자동으로 만든다.

JSON 스키마 참조까지 넣어서 에디터에서 자동 완성이랑 검증이 된다.

설정도 단축형을 지원한다.

프로바이더랑 모델을 객체로 쓰는 대신, 문자열 하나로 쓸 수 있다.

openai/gpt-5.5처럼 쓰면 된다.

간단한 걸 간단하게. 그리고 자동 업데이트도 넣었다.

플러그인이 시작할 때 npm 레지스트리를 확인해서 새 버전이 있으면 자동으로 설치한다.

패키지 매니저는 잠금 파일에서 감지한다.

사용자가 신경 쓸 게 없다.

테스트 64개

이날 테스트를 23개에서 64개로 늘렸다.

모킹 컨텍스트를 만들어서 플러그인 훅을 통째로 테스트했다.

재시도 처리, 즉시 폴백, 폴백 체인 순회, 되돌림, 알림 표시까지 전부 테스트로 감쌌다.

에러 분류는 복잡한 로직이다.

영구 에러인지 일시 에러인지, 어느 모델이 실패했는지, 쿨다운이 끝났는지. 이런 결정이 틀리면 폴백이 엉뚱하게 동작한다.

그래서 테스트가 필수적이었다.

마무리

하루 만에 폴백 플러그인의 뼈대를 다

잡았다.

30개 커밋에 릴리스 7개를 쏟아냈지만 핵심 결정은 몇 개 안 됐다.

에러를 영구랑 일시로 나누고 모델 단위로 추적하고 시간 제한 쿨다운을 두고 컨텍스트가 차면 큰 모델로 넘어가고. 이 네 가지 결정이 플러그인의 뼈대가 됐다.

가장 중요한 통찰은 “모든 에러가 같지 않다”는 거였다.

같은 실패라고 다

똑같이 대응하면 비효율적이다.

영구 에러는 바로 넘기고 일시 에러는 재시도부터 한다.

이 구분이 폴백을 똑똑하게 만들었다.

그리고 쿨다운도 중요한 결정이었다.

영구 블랙리스트는 단순하지만 너무 가혹하다.

일시적 에러로 실패한 모델이 복구돼도 다시 안 쓰니까. 시간 제한 쿨다운이 더 현실적이다.

세계는 영구적이지 않다.