견주기 운영 비용이 폭발해서 열심히 최적화한 이야기
어느 날 앤스로픽에서 메일이 하나 왔다.
Your recipt from Anthropic (Receipt #0000-0000)
- Subtotal: $5.00
- Vat: $0.50
- Amount paid: $5.50
Questions? Visit our support site at https://support.anthropic.com/.
저어어는 API를 결제한 적이 없는데요? 최근에 앤스로픽의 제품을 돈 주고 이용한 적이 없는데.. 라고 생각하던중 머리에 스쳐 지나가는 견주기. 설마 하고 Cloudflare Pages의 트래픽 분석을 확인해보았다.
견주기는 내게 그저 군소 프로젝트였는데, 내 지갑에 영향을 줄 정도까지 성장을 하다니 참 감격스러웠다. 그리고 서비스를 사랑해주신 많은 분들께 감사한 마음도 컸다. 앞으로 며칠간은 이용자 수가 늘 것으로 예상이 되었는데, 때 마침, 이제서야, 이게 유료 API를 사용하는 서비스라는 생각이 들었다.
생각해보니 웃을 때가 아님
빈곤한 제 지갑으로 서비스 이용료를 감당하기 어려울 수 있겠다는 생각이 문득 들었다. 일단 앤스로픽 API 대시보드를 켰습니다. 지난 하루동안 나온 금액을 보니
$7.79였다. 하루에 만이천원. 후임에게 해마루 커피 세잔을 사줄 수 있는 돈이다.. 어머니, 이제 저는 어떡하면 좋죠? 앤스로픽이 제 지갑을 털어가서 신용불량자가 될 수 있을 것 같아요. 심호흡을 크게 하고 일단 여러 생각을 해보았다.
- 현실적으로 내가 이 돈을 매일 부담할 수 있겠는가
-> 안됩니다 - 이 돈을 부담하고서라도 견주기를 운영할 가치가 있겠는가
-> ...안됩니다 부담 못합니다 - 서비스 운영 비용을 줄일 수 있는 방법은 없겠는가
-> 열심히 알아보아야겠죠 - 일단 지금 당장 낼 돈은 있는가
-> 3일 정도는 가능할지도요
3일동안 열심히 비용을 줄여보고, 안된다면 서비스를 접을 결심을 했다. 내게 주어진 시간은 3일이였다.
어떻게 비용을 줄일 수 있을까?
싼 모델을 이용한다
일단 빌링 메일을 받자마자 LLM 모델 변경부터 들어갔다. 2024년 44주차 주간정리에서 언급했듯, Claude 3.5 Sonnet (New) 모델을 이용중이였다. 3.5 Sonnet은 앤스로픽의 제품중 가장 고성능, 고비용 모델이다. 성능을 낮추면 비용을 줄일 수 있었고, 3.5 Haiku로 모델을 변경하였다. 이후 늘어나는 이용자가 부담되어 결국엔 3 Haiku까지 모델을 낮추었다.
https://github.com/rycont/gyeon/commit/47f9a4542dc98f162a94731853ece8006ed620e9
각 모델별 가격 차이는 다음과 같다:
Unit: $/MToks | Inputs | Outputs |
---|---|---|
3.5 Sonnet | 3 | 15 |
3.5 Haiku | 0.8 | 4 |
3 Haiku | 0.25 | 1.25 |
그렇게 가격을 1/10으로 줄였다. 이제 하루에 700원이다.
프롬프트 캐싱을 활성화한다 (실패)
프롬프트 캐싱은 반복되는 프롬프트의 연산결과를 미리 저장해두어서 가격은 낮추고 성능은 높힐 수 있는 기능이다. 캐싱을 켰을 때 입력 가격은 25% 늘어나지만, 캐시에 저장되어있는 텍스트는 90% 저렴하게 연산할 수 있다. 견주기에서는 매일 동일한 프롬프트를 반복 사용하기에 프롬프트 캐싱으로 비용을 줄일 수 있을 것으로 예상했다. 그러나 앤스로픽에서는 최소 2048 토큰(Haiku Family), 또는 1024(Sonnet and the larger models) 토큰부터 캐싱에 저장할 수 있었고, 견주기의 시스템 프롬프트 길이는 이보다 짧아서 캐싱을 사용할 수 없었다.
프롬프트 길이를 줄인다
기존엔 게임에서 더 다양한 힌트를 제공하기 위해 이전 시도 내역을 모두 참고해서 새 힌트를 생성하도록 되어있었다. 그러다보니 한 세션 내에서 N번 시도를 한다면 사용한 토큰 수는 총 R(프롬프트당 토큰 수) * (N ** 2)
이 된다. 어려운 문제가 나와서 세션이 길어진다면 운영 비용이 급수적으로 늘어나는 구조였다.
문제는 12월 1일에 발생했다. 대단히 추상적인 단어(양국)가 오늘의 문제로 나와버렸고, 모델 성능은 떨어져서 제대로 힌트 문장도 생성이 안되다 보니, 한 세션이 100회 이상으로 길어지는 상황이 발생했다(이 날에 빌링메일을 두개 받았다..). 도저히 감당할 수 없을 정도가 되어서, 힌트를 생성할 때 이전 시도 내역을 참고하지 않도록 했다. 그렇게 세션에서 사용하는 토큰 비용을 R * N
으로 줄였다.
응답 캐싱을 사용한다
앤스로픽의 API를 호출할 때, 앤스로픽에 직접 호출하는게 아닌 Cloudflare의 AI Gateway라는 제품을 사용해서 한 단계 넘어서 호출하고 있었다. 딱히 그렇게 결정한 이유는 없었고, 그냥 왠지 좋아보여서 사용했다. 그러나 이번엔 AI Gateway가 나를 살렸다. AI Gateway에는 응답 캐싱(Cache Response)이라는 기능이 있는데, 이를 사용하면 모든 요청과 응답을 저장해두었다가 캐시에 저장된 것과 동일한 요청이 오면 LLM API를 호출하지 않고 저장된 값을 그대로 응답한다.
프롬프트 캐싱은 여러 요청 프롬프트에서 공통되는 시작 부분을 임시저장해두었다가 중간부터 저렴하게 재개하는 방식이라면, 응답 캐싱은 완결된 요청-응답 쌍을 기억해두었다가 이전에 호출한 것과 동일한 요청이 오면 연산 없이 응답을 바로 반환하는 기능이라는 점에서 차이가 있다.
특히 프롬프트 캐싱은 3. 프롬프트 길이를 줄인다의 변경점과 큰 시너지를 이루었는데, 이전 맥락을 참조하지 않으니 모든 응답이 시스템 프롬프트와 오늘의 문제, 사용자의 시도만으로 구성되었고, 여기서 사용자의 시도만이 변인이 되어 다른 사용자가 이미 시도한 단어는 연산 없이 응답할 수 있었다! 한 초성에 대해 시도할 수 있는 단어들은 제한되어있었기에, 현재는 전체 요청의 70% 이상이 캐싱으로 처리된다.
그래서 지금은
사실 일주일만에 이용자는 많이 꺾였다. 접속자 수 기준으로 400명이 찾아주었지만, 지금은 100명 중반대에서 크게 변하지 않고 있다. 좋아요, 이 정도면 저도 쉽게 감당할 수 있어요. 비용 대책도 세우고 이용자도 줄어서, 모델은 다시 3.5 Sonnet(New)으로 롤백했다. 그렇게 질좋은 답변을 소수의 이용자가 재밌게 누리는 중이다.
12월 9일 #견주기 3번째 시도에 성공했습니다.
초성이 ㅇㅇ인 단어를 맞출 수 있을까요?
https://gyeon.postica.app
우리 견주기 많이 사랑해주세요.