moonstruck 개발일지: 모델 선정부터 api 요청, 그리고 응답 받기까지

그른손·2024년 10월 25일
0

어떤 모델로 해야 할까?

이제 진짜 간단하게 사용자 입력 input창이랑 카드 선택 기능을 만들었으니, 이 데이터를 가공해서 AI 모델에게 보내고 적절한 응답을 받아와야 합니다.
그러면 먼저 어떤 모델을 사용할지부터 정해야 하는데, 모델을 정하면서 여러가지 고민이 많았습니다.

  • 저렴했으면 좋겠어!: 사용자 입력은 주로 한글로 이루어질텐데, 한글 입력은 영문 입력보다 토큰을 더 많이 소모하기 때문에 같은 내용을 한글과 영문으로 보낼 때를 비교하면 한글 입력이 더 비쌉니다! (최근에는 토큰 최적화가 잘 된 모델이 많아서 이전과 비교해서 가격 부담은 줄어들었지만, 그래도 영문 입력보다 비싼 건 여전하다.)
  • 똑똑했으면 좋겠어!: 물론 거의 모든 AI들은 저같은 놈보다 똑똑하긴 하지만... 사용자가 입력한 질문을 보고 사용자가 원하는 게 미래에 대한 예측인지, 아니면 현 상황에 대한 조언인지 판단할 정도의 능지가 필요합니다. 타로 카드의 상징과 의미에 대한 데이터가 잘 학습되어있으면 더 좋구요. 또한 나중에 타로점을 봐주는 캐릭터를 선택하는 기능을 추가한다면, 모델이 캐릭터 역할로 롤플레이까지 해줘야 하기 때문에 지능이 높은 편이 좋을 거라 생각했습니다.

비용 고민의 완화: Google Vertex AI

구글 클라우드 플랫폼에서는 새 사용자에게 300달러의 무료 크레딧을 포함한 90일간의 체험판 사용 기회를 제공하는데, 이 300달러의 무료 크레딧은 구글의 Vertex AI에서 여러가지 AI 모델을 활용하기 위해 사용할 수 있습니다. 그러니까 구글의 Gemini, 메타의 Llama, 앤트로픽의 Claude 등의 다양한 모델을 무료로 활용할 수 있는 겁니다! 물론 90일간의 사용기간이 정해져있고, 무료 크레딧을 모두 소진하면 비용을 내야 하지만, 체험 기간동안 서비스 운영 비용이 얼마나 드는지 실제로 체감해보면서 유연하게 계획을 세울 수 있을 것 같습니다. 거기다 추후에 사용자가 모델을 선택할 수 있는 기능을 추가한다면, 여러 모델을 사용하면서 비용 결제는 하나의 계정에서 관리할 수 있는 관리 상의 이점도 생기구요.

Anthropic Claude를 만나보자

먼저 몇몇 부분들에 있어서는 GPT-4o를 뛰어넘는 성능을 자랑하는 Anthropic Claude, 그 중에서도 경량화 모델인 3.5 Sonnet을 사용해보기로 했습니다.

앤트로픽 클로드는 3종류의 모델이 있는데, Haiku, Sonnet, Opus입니다. Opus가 가장 고급 모델이고, Sonnet은 그보다 한 티어 아래로 속도가 빠르면서 성능도 좋고, 가격 또한 비교적 합리적인 편이라 이걸 사용해보기로 했습니다.

Claude 3.5 Sonnet이 멋진 점

- 롤플레이를 잘 해줘요!

- 타로 카드 스프레드도 잘 알아요!

그 와중에 안경을 고쁘다라고 하는데 이런 표현은 없습니다... 텍스트 생성 AI들은 가끔 이렇게 한국어를 요상하게 사용하기도 합니다
- 타로 해석도 잘 해줌!

안경 그만 고쁘라고!

이제 연결해보기

  • 간단한 성능 테스트도 해봤으니, 이제 앱에 api 엔드포인트를 연결해서 요청을 보내볼 겁니다.
  • 먼저 (자세한 과정들은 캡쳐 안해놨지만) 구글 클라우드 콘솔에서 할 일들은,
    • 버텍스 AI 활성화하기
    • 버텍스 AI의 모델 가든에서 Anthropic Claude 3.5 Sonnet 사용 설정하기
      • 사용자 정보 작성: 저는 회사 이름은 moonstruck으로, 회사 홈페이지는 그냥 제 홈페이지 적었습니다.
      • 제가 이걸 하고 있던 당일에 3.5 Sonnet V2가 출시되었는데, 신나서 한 번 써보려고 애썼지만 아무리 시도해도 사용 할당량이 0으로 설정되는 현상이 있었어요. 아마 아직 못쓰는 것이겠거니 하고 V1으로 사용하기로 했습니다. 아쉽다...
    • 서비스 계정 만들기: Vertex AI 관리자 역할을 설정한 서비스 계정을 만들어줍니다!
    • 서비스 계정 키 만들기: 서비스 계정 정보가 담긴 JSON파일을 생성하여 내려받습니다. 나중에는 이걸 사용해서 인증과 엑세스 토큰 발급을 자동화할 수 있을겁니다

여기까지 했으면 어느정도 준비는 끝난 거 같고, 이제 api 요청을 실제로 해볼 차례입니다.

api 연결 진짜로 하기

  • 먼저 gcloud CLI를 설치하고, 터미널에서 로그인을 해준 다음 print-auth-token 명령어로 직접 엑세스 토큰을 발행해줍니다.
  • 발행할 엑세스 토큰을 임시로 만든 .env.local 파일에 넣어줍니다.
  • 이걸 이용해서 api 엔드포인트로 요청을 보내봅니다.

요청으로 보낼 내용은 이겁니다:

const prompt = `너는 이제부터 저명한 타로 해석가이며, 사용자의 고민을 듣고 뽑힌 카드 결과를 해석해 직접적이고 명쾌한 답변을 제시해야 한다. 사용자가 입력한 고민과 뽑힌 카드의 의미를 연관지어 명료한 해석을 제공해야 하며, 반드시 한국어로 답변해야 한다.
사용자 입력값: ${userInput}
뽑힌 카드들:${drawnCardsString}
`

지시문은 테스트용이라 아주 간략하게 썼습니다.

마주친 오류들

400 오류

api 엔드포인트를 제대로 설정하지 않아서 + 요청 형태가 형식에 맞지 않아서 발생했습니다!
이걸 해결하기 위해 vertex AI model garden의 claude 3.5 sonnet 문서를 다시 읽어봤고, 이런 부분을 참고해서 해결했습니다:

문서에서는 요청으로 보낼 때 이런 형식으로 보내라고 되어있습니다. 구조를 보니, messages 배열에 지금까지의 대화 내역이 들어가며, 메세지는 role로 메세지를 보낸 주체를 명시(여기서는 user), content로 메세지 내용을 보내는 것 같아요. 여기서는 이미지 파일을 b함께 보내는 예시를 보여주고 있어서 type:image인 객체를 첨부해서 보내고 있는데, 그렇지 않을 경우의 예시도 한 번 봤습니다.

일케 content 필드에 문자열로 메세지 내용을 보내는 듯 합니다

또한 api 엔드포인트는 "https://$LOCATION-aiplatform.googleapis.com/v1/projects/$PROJECT_ID/locations/$LOCATION/publishers/anthropic/models/$MODEL:streamRawPredict"라고 나와있더라구요! 저는 싸우스코리안이니까 아마 asia-southeast 지역일테고, 프로젝트 id는 아까 받았던 키 파일에서 가져오고, 모델은 클로드 소넷으로 하면 되겠죠?

429 오류


400 오류가 아닌 429 오류가 일어난 걸 보니, 요청이 가긴 갔습니다!
근데 요청이 너무 많이 갔다는 오류가 난 겁니다!
일단 1보 전진하긴 했는데, 요청이 너무 많이 갔다는 게 무슨 소리일까요? 저는 한 번 밖에 요청 안했는데...

원인: 요청을 허락한 적이 없음

클라우드 콘솔을 뒤져보다 그 원인을 발견했는데요, 제 구글 클라우드 프로젝트(my-first-project)에서, Vertex AI API에 설정된 anthropic-claude-3-5-sonnet의 사용 할당량이 0이었기 때문이었습니다!

  • 그러니까 0번만 요청 보낼 수 있게 허락했는데 무려 1번이나 보냈으니 요청량이 너무 많다는거죠!!!!!!
  • 근데 문제가 하나 더 있습니다. 바로 asia-southeast1 지역 뿐만 아니라, 모든 지역에서 할당량이 0으로 설정되어있다는 겁니다

    쓰지 말라는 걸까요?

놀랍게도 맞습니다!!! 쓰지말라는겁니다!!!!!!

웹에서 관련 이슈를 찾아보니 클로드ai를 사용하는 유저들 사이에서 최근 들어 흔하게 발생하는 이슈라고 합니다. 구글 클라우드 플랫폼에서 체험기간동안 무료 크레딧으로 AI 모델을 사용할 수 있는건 구글의 모든 유저에게 동일하게 제공되는 혜택인데, 아무래도 제가 사용하려는 클로드 소넷에 많은 사용자들이 몰리면서 과도한 트래픽이 발생하고, 이걸 해결하기 위해 앤트로픽 측에서 접근을 막은 것 같습니다.
커뮤니티에서 제안하는 해결법으로는 직접 할당량 조정 요청을 보내는 게 있는데, 2영업일정도 소요되고 할당량 조정 사유가 합당한지 설명할 필요가 있다고 합니다...
다른 해결법으로는 그냥 될 때까지 새 프로젝트 만들기 가 있는데, 저는 이 방식으로 해결했습니다. 의외로 프로젝트 새로 하나 만드니까 해결되더라구요! 다만 이 프로젝트도 자칫하면 할당량이 제한될지 모르니, 그 때는 앞서 얘기한대로 할당량 조정 요청을 하거나, 아니면 다른 모델을 사용하는 걸 고려해봐야 할 것 같습니다. Gemini도 새로 나왔고, 꽤 똑똑하더라구요

그래서 새 프로젝트를 생성하고, 또 관련 설정 하고...이렇게 저렇게 조물딱댄 후에 다시 요청을 보내봤습니다.

와 나 천잰가?(아님)

  • 감격스럽게도 클로드에게서 답변을 받을 수 있었습니다!
    근데 뭔가 답변이 이상한데... 요청도 그렇고...
  • 다시 보니 뽑은 카드 정보가 안 갔고, 따라서 클로드가 타로를 해석하지 못하는 상황입니다. 왜 카드 정보가 안 갔을까요? 화면에는 뽑힌 카드가 뜨는데...
타로 카드가 안 가요
  • 요청을 여러 번 해 보니, 타로 카드가 아예 안 가는 건 첫 번째 요청을 보낼 때입니다.
  • 그런데 뭔가 이상한 게, 두 번째 요청을 보낼 때는 첫 번째 시도에서 뽑았던 카드 정보를 보냅니다. 순서가 한 번씩 밀려요.
  • 원인은 React의 상태 업데이트가 비동기적으로 이루어지기 때문입니다. api 요청을 보내는 함수의 코드는 현재 다음과 같습니다.
  async function checkSubmittedForm(inputValue: string) {
    const drawnCardsResult = drawRandomCards(cardCount);
    setDrawnCards(drawnCardsResult);
    const formattedInput = formatUserInputAndCardInfo(inputValue, drawnCards);
    console.log(prompt);
    const result = await callVertexAPI(formattedInput);
    console.log(result);
  }
  • 여기서 카드를 뽑은 후에 setDrawnCards로 drawnCards 상태를 업데이트하고, 이 상태를 format 함수에 넣어 문자열로 합친 후에 요청을 보내는데,
  • React의 setState 함수는 즉각적으로 상태를 업데이트하는 게 아니라, 상태 업데이트를 요청만 한 후에 (업데이트 완료를 기다리지 않고) 다음 코드를 계속 실행합니다.
  • 따라서 drawnCards상태가 완전히 업데이트되기 전에 그 다음 줄 코드인 const formattedInput = formatUserInputAndCardInfo(inputValue, drawnCards);이 실행되어, drawnCards가 빈 상태로 들어가게 되는 겁니다.
  • 이걸 해결하려면 두 가지 방법이 있습니다:
    • 어차피 drawnCards는 drawnCardsResult와 똑같은 값이니, format 함수에 drawnCards 대신 drawnCardsResult를 넣어주거나,
    • useEffect로 drawnCards 상태가 업데이트될 때, drawnCards각 비어있지 않다면(if(drawnCards.length > 0)) 다음 코드(포매팅, api 요청)를 실행시키도록 바꾸거나
  • 저는 어차피 drawnCards를 상태로 관리하는 건 UI 업데이트를 위해서고, 카드 정보를 포매팅해서 api 요청을 보내는 건 별개의 로직이라고 판단했기 때문에 전자의 해결책을 골랐습니다.

결과: 만족스러움


이제 작성한 프롬프트와 사용자 입력값, 뽑힌 카드 정보가 제대로 전달되고, 모델로부터 정상적인 타로 해석 결과를 받아볼 수 있습니다!

profile
프론트엔드 개발자

0개의 댓글