쿨다운 인식 폴백
며칠 전에 폴백 플러그인을 하루 만에 만들었다.
이날은 그 플러그인을 안정화하는 단계였다.
기능 완성도를 끌어올리고 에러 분류를 정식으로 정리하고 전체 개발 과정을 기록으로 남겼다.
중요한 개선 중 하나가 쿨다운 인식이었다.
이전에는 실패한 모델을 쿨다운에서 빼놓고 폴백 대상에서 제외했다.
근데 쿨다운 상태인 모델을 폴백 대상에서 제외하는 로직이 완전하지 않았다.
그러니까 쿨다운 중인 모델한테 또 폴백을 시도하는 경우가 있었다.
계속 실패하는 모델을 계속 때리는 거다.
이걸 고쳤다.
쿨다운 상태인 모델은 폴백 대상에서 확실히 빠진다.
반복적으로 실패하는 모델을 때리지 않게 된 거다.
세션 에러 처리
에이전트 세션에서 에러 이벤트를 구독해서 에러 타입에 따라 자동으로 폴백을 트리거하는 기능도 붙였다.
이전에는 메시지 훅에서 에러를 잡았다.
근데 세션 레벨에서 발생하는 에러도 있다.
이걸 별도로 처리해서 세션 에러가 나도 자동으로 폴백 체인이 돌아가게 만들었다.
구조화된 로깅
폴백 이벤트, 에러 분류, 모델 전환을 구조화된 형태로 로깅하게 만들었다.
JSON 형태로 남기니까, 나중에 문제를 추적할 때 로그를 파싱하기 쉽다.
폴백이 왜 발생했는지, 어느 모델에서 어느 모델로 넘어갔는지, 에러가 뭐였는지. 이런 정보가 구조화되어 있으니까, 문제가 생겼을 때 원인을 빨리 찾을 수 있다.
에러 분류 6단계
이 시기에 에러 분류 로직을 6단계 우선순위로 정식화했다.
이게 플러그인의 지적 핵심이다.
첫 번째는 쿨다운 확인이다.
쿨다운 중이면 무시한다.
재시도 폭풍을 막는 거다.
두 번째는 401, 402, 403이다.
인증이나 결제 실패면 즉시 폴백한다.
재시도해봤자 같은 결과가 나온다.
세 번째는 재시도 가능 플래그가 참일 때다.
지수 백오프로 재시도한다.
네 번째는 재시도 가능 플래그가 거짓일 때다.
재시도가 의미 없으니 즉시 폴백한다.
다섯 번째는 429랑 5xx 계열이다.
속도 제한이나 서버 에러면 재시도한다.
여섯 번째는 기본값이다.
안전망으로 재시도한다.
일시적 에러와 영구적 에러
이 6단계의 핵심은 일시적 에러와 영구적 에러를 구분하는 거다.
속도 제한, 서버 과부하, 네트워크 시간 초과 같은 일시적 에러는 조건이 바뀔 수 있다.
그러니까 재시도에 의미가 있다.
반면 인증 실패나 “이 모델은 지원하지 않는다” 같은 영구적 에러는 결정론적이다.
같은 입력에 같은 실패가 나온다.
재시도는 의미가 없다.
바로 모델을 바꿔야 한다.
이 구분이 없으면 영구 에러에 재시도를 낭비한다.
지원하지 않는 기능을 계속 시도해봤자 안 된다.
빨리 다른 모델로 넘어가는 게 낫다.
대용량 컨텍스트 폴백
그리고 대용량 컨텍스트 폴백도 이 시기에 완성됐다.
대화가 길어지면 컨텍스트 윈도우가 찬다.
그러면 프로바이더가 조용히 멈추거나 세션이 깨진다.
플러그인이 컨텍스트가 일정 비율을 넘으면 미리 큰 컨텍스트 모델로 전환한다.
실패하기 전에 미리 바꾸는 거다.
임계값은 컨텍스트 사용 비율로 잡는다.
그리고 전환할 큰 모델을 설정에서 지정한다.
이 두 가지만 있으면, 긴 에이전트 세션에서 컨텍스트 초과로 멈추는 일을 막을 수 있다.
전체 개발 과정 기록
이날은 개발 과정 전체를 기록으로 정리한 날이기도 했다.
어느 버전에서 뭐가 바뀌었는지, 왜 그렇게 바뀌었는지를 위키에 남겼다.
기록을 남기는 게 왜 중요한가. 나중에 “이게 왜 이렇게 되어 있지” 궁금할 때, 기록이 있으면 바로 알 수 있다.
없으면 코드를 읽고 추론해야 한다.
코드는 “무엇”을 알려주지만 “왜”는 알려주지 않는다.
왜를 알려면 기록이 필요하다.
이 기록은 며칠 뒤에 동시성 버그를 잡을 때 큰 도움이 됐다. 변수 중복 선언이 모든 이벤트 핸들러를 깨뜨리는 버그, 컨텍스트 압축 타이밍 경쟁 조건 같은 문제가 이후에 나왔다.
기록이 있으니까 어느 시점에 어떤 변화가 들어갔는지 추적할 수 있었다.
마무리
이 시기는 플러그인이 “돌아가는” 단계에서 “안정적으로 돌아가는” 단계로 넘어가는 시기였다.
에러 분류 6단계가 지적 핵심이다.
모든 에러를 같게 대하지 않는다.
일시적인지 영구적인지, 재시도가 의미 있는지 없는지를 구분한다.
이 구분이 폴백을 똑똑하게 만든다.
그리고 쿨다운 인식이 중요했다.
실패한 모델을 때리지 않는 건 기본이다.
근데 쿨다운 상태를 정확히 반영하지 않으면, 쿨다운 중인 모델한테 또 시도하게 된다.
상태를 정확히 반영하는 게 안정성의 기반이다.