AI 제공업체가 모델을 종료했을 때
모델 교체, 오류 없이 돌아온 빈 답변, 그리고 갑자기 몇 배로 뛴 청구서. 무엇이 고장 났는지, 그리고 원인이 왜 그렇게 보이지 않았는지 이야기해요.

2026년 7월, 여러 기능이 한꺼번에 멈췄어요: AI 채팅, 티켓의 AI 답변, 티켓 요약, 서버 빌더, 그리고 아이디어와 버그 신고의 검토와 번역까지요.
원인은 우리 코드가 아니었어요. 우리가 쓰던 모델을 AI 제공업체가 종료한 거예요.
1부: 모델이 사라졌어요
이건 뻔한 부분이고, 금방 고쳤어요. 제공업체가 모델을 중단하면 후속 모델을 입력하면 끝이에요.
진짜 사고는 그다음에 시작됐어요.
2부: 오류 없는 빈 답변
교체한 뒤에 답변이 돌아왔어요. 다만 가끔 비어 있었어요. 잘리지도 않았고, 형식이 깨지지도 않았고, 아무런 오류 없이 완전히 빈 답변이었어요.
원인은 모델 이름만으로는 드러나지 않는 차이에 있었어요: 후속 모델은 추론 모델이에요. 실제 답변 전에 먼저 내부적인 생각의 흐름을 별도의 필드에 써요. 둘 다 같은 토큰 예산에서 빠져나가요.
생각하느라 예산을 다 쓰면, 형식상으로는 아무 문제 없는 답변이 돌아와요: 제공업체는 "길이 한도 도달"이라고 알리고, 내용 필드는 비어 있어요. 오류도, 힌트도 없고, try/catch가 볼 수 있는 것도 전혀 없어요.
우리는 실제 테스트로 이를 재현했어요: 5토큰짜리 예산이면 이미 모든 요청에서 빈 답변이 나와요.
3부: 청구서
가장 비싼 호출 지점인 AI 서버 빌더에서는 긴 답변이 나오기 때문에 예산을 넉넉하게 잡아 두었어요. 그래서 생각이 한도에 부딪히지 않고, 그냥 계속 이어졌어요.
결과는 두 방향에서 동시에 왔어요. 요청이 너무 오래 걸려서 시간 초과가 났어요. 그리고 매번 보이지 않는 생각의 흐름 전체에 비용을 냈기 때문에 몇 배나 더 비쌌어요. 마지막에는 계획에 없던 오류 메시지가 나왔어요: 잔액 부족.
해결책은 한 줄이에요. 요청 본문에서 생각 기능을 끌 수 있고, 그러면 모델이 다시 예전 모델처럼 작동해요. 그 뒤로 이 한 줄은 모든 호출 지점에 들어가야 하고, 저장소에는 그런 곳이 몇 군데가 넘어요.
4부: 정리하면서 발견한 것
어차피 모든 AI 호출 지점을 손보는 김에, 하나하나 살펴봤어요. 그러다 사고와는 관계없지만 비슷하게 비싼 두 번째 주제가 드러났어요.
제공업체는 앞부분이 변하지 않아 캐시에서 처리할 수 있는 프롬프트에 훨씬 적은 비용을 받아요. 캐시 적중과 실패의 차이는 대략 30배예요.
그 할인은 플래그로 얻는 게 아니에요. 우리는 그걸 직접 테스트했어요: 그 용도로 만들어진 요청 본문의 필드는 효과가 전혀 없었고, 캐시는 어차피 자동으로 적용돼요. 유일한 지렛대는 프롬프트 안의 순서예요. 안정적인 것에서 변하는 것 순으로요.
우리가 정확히 반대로 했던 곳이 네 군데 있었어요:
고정된 지시문 블록 안에 들어간 변하는 값. 버그 신고의 중복 검사는 지시문에 모듈 이름을 넣고 있었어요. 그 때문에 그 외에는 똑같은 블록이 모듈마다 별도의 캐시 줄을 갖게 됐어요. 그 값은 어차피 바뀌는 가변 부분에 들어가야 해요.
안정적인 것보다 앞에 온 새로운 것. 같은 검사가 새 버그 신고를 최대 50개의 비교 후보 앞에 보냈어요. 그 바람에 API에서 가장 큰 프롬프트가 영구적으로 완전한 캐시 실패가 됐어요.
고정된 부분에 들어간 사용자별 데이터. 공개 AI 채팅은 개별 사용자에 대한 메모를 지시문 블록에, 그러니까 공유되는 채널 기록 앞에 넣고 있었어요. 그래서 같은 채널의 모든 사용자가 저마다 다른 앞부분을 갖게 됐고, 공유된 기록에 대한 비용을 사용자마다 따로 냈어요.
기록에 대한 슬라이딩 윈도우. 메시지가 올 때마다 마지막 N개 메시지를 보내면, 모든 메시지에서 앞부분이 밀려요. 그러면 기록 전체가 영구적인 캐시 실패가 돼요. 올바른 방법은 블록 단위로, 즉 드물게 그리고 크게 잘라 내는 거예요.
여기에 사소한 트집처럼 보이지만 그렇지 않은 세부 사항이 하나 더 있어요: 프롬프트 안의 목록을 기준 하나로만 정렬하면, 값이 같을 때 순서가 바뀔 수 있어요. 지시문 블록 중간에서 한 줄만 바뀌어도 그 뒤의 모든 것에 대한 캐시 적중을 잃어요. 프롬프트 안의 정렬에는 순서를 하나로 확정하는 보조 기준이 필요해요.
우리가 얻은 것
모델 교체는 설정 변경이 아니에요. 이름은 변수 안의 값처럼 보여요. 그 뒤에는 다른 동작이 있었고, 그 동작이 망가뜨린 건 인터페이스가 아니라 우리 코드였어요.
위험한 실패는 조용한 실패였어요. 예전 모델이 사라진 건 바로 눈에 띄었고 몇 분 만에 고쳤어요. 빈 답변은 형식상 유효한 응답으로 돌아왔고, 언제까지고 그대로였을 수도 있어요.
측정은 호출 지점에 있어야 해요. 이제 모든 호출 지점에서 캐시 적중률을 기록해요. 제대로 만든 곳은 첫 요청 이후 높은 비율에 자리를 잡고, 잘못 만든 곳은 0에 머물러요. 그건 로그 한 줄에서만 보이고 다른 어디서도 보이지 않아요. 둘 다 작동은 하니까요.
그리고 불편한 결론: 남의 모델을 쓴다는 건 제품의 일부가 다른 곳에서 내린 결정에 달려 있다는 뜻이에요. 그래서 8월부터 모든 서버는 우리 것에 의존하는 대신 AI 기능을 자기만의 엔드포인트로 연결할 수 있어요.
더 많은 글
Discord 서버 4,000개 지켜보기: Grafana, PostHog, 그리고 그것들이 보여 준 것
트래픽의 절반을 조용히 만들어 낸 반복 작업, 하루 300만 건의 쓸모없는 요청, 1분 만에 끝난 공격. 우리 모니터링이 보는 것과 일부러 보지 않는 것을 이야기해요.
비하인드2,000개가 넘는 Discord 서버에서 봇을 운영하며 배운 것
코드를 쓸 때 생각하는 것과 실제 규모에서 다르게 작동하는 네 가지를 이야기해요.
비하인드AlphaCloud가 거의 모든 기능을 무료로 두는 이유
대부분의 Discord 봇은 가장 유용한 기능을 구독 뒤에 잠가 둬요. 우리는 일부러 그러지 않아요. 그리고 그건 생각보다 너그러움과는 별 관계가 없어요.