QA, 구글 API 비용 예측

2024년 1월 30일 화요일

Today I Learned

날짜

2024년 1월 30일 화요일

내용

계속 QA를 진행하고 있다.

키워드 추출 실패

갑자기 특정 경우에 키워드를 추출해내는데 실패하고 있다. 키워드는 nltk 관련 문제라 AI가 고장난 건 아니였다. 안되는 상품의 URL을 들어가보니 리뷰들이 모두 한글로 작성되어있었다. 그동안 리뷰가 당연히 영어로 가져와질 것이라고 생각했는데, 원어(작성 언어)와 접속시 설정한 언어 중 하나로 선택할 수 있었다. 문장을 단어 단위로 쪼개는 tokenize는 punkt 데이터를 사용하는데 어제 말했듯 정말 다양한 언어 데이터가 존재하지만, 긍정 부정을 판별하는 opinion_lexicon은 영어밖에 없었다. 리뷰를 항상 영어 번역하여 가져오도록 했더니 해결되었다.

언어의 다양성은 위젯과 마찬가지로 보고서를 해당 언어로 출력해주도록 수정하고 데이터베이스는 항상 영어로 저장하는게 좋을 것이라고 생각했다. 정말 그럴지는 추후 두고봐야할 문제일듯.

개발 문서 작성

이번 스프린트에서 나는 데이터를 분석하는 부분을 담당했다. 나의 부재(휴가, 실종, 사망 등)가 악영향을 끼치지 않도록 내가 코드를 어떻게, 왜 작성했는지 최대한 자세히 적었다. 그래도 쓰다보니 열심히 고민한 보람이 있는 부분도 있고, 진작에 생각했으면 구현할 떄 고생을 덜 했을 것 같다는 생각도 들었다. 개발할 기능에 대한 공수를 생각할 때, 이 문서의 초안을 짠다는 고민을 해보면 조금은 도움이 될 지 궁금하다. 다음 스프린트 때 해봐야지.

비용 예측

AWS와 Google cloud platform에는 사용할 서비스에 대한 비용을 알려주고, 이를 예상할 수 있는 계산기도 있다. 각 기능에 대해 어떻게 사용하면 1달 비용의 대략적인 값을 알려준다. 정확히는 힘들지만, 얼추는 알 수 있겠다 싶어 계산해보았다.

리뷰 분석을 요청할 때, AI에게 제공하는 프롬프트는 600자(명령문 + 키워드 + 상품 제목)이고 생성하는 요약은 400자 정도로 계산했다. 매일 20개의 요청을 주고받으면 월 600번을 수행하는데 계산 결과는 279원이라고 한다.

실제 2주 정도 사용하여 이뤄진 요청은 252회고 비용은 88원이 나왔다. 회당 금액 차이는 0.1원정도인데 초반 테스트에서는 입력, 출력 글자수가 달랐으니 감안할만한 오차라고 생각한다. 앞으로 새로운 기능을 이용할때 자주 사용해볼 예정

printlogging

이전에 리뷰 분석하는 함수 호출을 backgrountasks 로 처리하려다 실패했었다. 따라서 리뷰 분석하는 함수의 로직 내부에 추가해주었다. 사실 큰 차이는 없지 않나 싶다가도, 아예 별개의 작업을 독립적인 Task로 추가하는 것이 맞다고 생각한다. 아예 리뷰를 수집하는 메서드와 리뷰를 분석하는 함수를 모두 호출하는 제 3의 방식을 사용한다면 모를까, 리뷰 수집하는 메서드에 리뷰 분석함수를 껴넣은 모양새가 마음에 들지 않았다.

backgroudtasks는 API 응답 이후 처리될 작업들을 위한 기능이다. 예를 들어, 유저가 서버에 어떤 파일을 다운로드 하겠다는 요청을 보냈다면, 다운로드가 아예 완료된 후 응답을 보내면 늦는다. 유저 입장에선 응답을 기다리는 동안 “다운로드가 시간이 걸리는건지 요청이 시간이 걸리는건지..” 라고 생각하지 않을까? 다운로드가 성공적으로 진행을 시작했다면 우선 200(OK)를 반환하고 다운로드는 backgroun_tasks로 진행하면 된다. 이것이 완료되었음을 알리는 로직은 별개긴 하지만.

backgroun_tasks를 다루지 못했던 이유는 print문으로 과정을 표시하지 못했기 떄문이다. 나는 보통 함수가 어디서 막히는지를 찾기 위해 중간중간 print문을 껴넣는다. 출력되면 거기까진 OK란 뜻이니까.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@router.get("/test")
async def test(background_tasks: BackgroundTasks):
    background_tasks.add_task(a)
    background_tasks.add_task(b)
    return print("api 끝")

def a():
    return print("첫번쨰 Task가 실행됨")

@router.get("/ttest")
async def ttest(background_tasks: BackgroundTasks):
    background_tasks.add_task(a)
    return print("두번째 api 끝")

def b():
    return print("두번째 Task가 실행됨")

이렇게 작성하고 두 엔드포인트에 요청하면 예상되는 결과는 다음과 같다.

  1. test 엔드포인트로 요청
  2. “api 끝” 출력
  3. “첫번쨰 Task가 실행됨” 출력
  4. “두번째 Task가 실행됨” 출력
  5. ttest 엔드포인트로 요청
  6. “두번째 api 끝” 출력
  7. “첫번째 Task가 실행됨” 출력

2번과 3번은 초 단위의 짧은 간격으로 실행되야 한다. 하지만 실제로는 ttest로 요청을 보내기 전까지 3,4번이 실행되지 않았다. ttest로 요청을 보내면 그제서야 3,4번이 출력되었다. 그리고 7번도 출력되지 않았다. 이후 다른 요청을 보내면 그제서야 출력이 되었다.

이 상황이 도저히 이해가 되지 않았다. 메시지가 출력되었다는 것은 함수가 실행되었다는 의미인데, 다른 요청이 있어야만 함수가 실행되는건가? 만약 또다른 background tasks가 추가되어야만 기존의 작업이 실행된다면 적어도 3번 메시지는 출력이 되었어야 한다(1개의 요청에 2개의 tasks를 넣어줬으니). API 요청과 Background tasks는 도대체 무슨 연관이 있는건지 파악하지 못해 포기했었다. 근데 신기한 건 리뷰 요청은 아무이상없이 잘 진행된다는 것..

오늘 이와 관련된 공부를 하다가 저 print 문을 모두 logging.error 로 바꿔주었다. 그랬더니 정상적으로(처음 예상한 방식)으로 잘 출력된다! background_tasks를 다룰 수 있는 실마리를 찾았다. 근데 print는 왜 안돼는지 또 궁금해졌다.

| Logging in Python | Print in Python | | — | — | | 파이썬 프로그램 실행 중 발생한 이벤트와 에러 기록 | 콘솔에 디버깅 목적으로 정보 표시 | | 주로 production 환경에서 사용 | 주로 디버깅에 사용 | | Log levels, filtering, formatting 등의 기능 | 기능 없음 | | 디버그, 정보, 에러, 경고 등 다양한 출 | 그냥 통과한 것 출력 | | import logging  logging.basicConfig(level=logging.INFO)  logging.info(“Hello”) 처럼 사용. 콘솔창, 파일, 네트워크 등 다양한 곳에 표시 가능 | print(“hi”) 처럼 사용. 콘솔창에만 출력됌. |

출처 : 링크

궁금증이 해결되지 않았는데, 내친구 GPT의 말로는, 멀티스레드나 비동기 환경에서는 print 가 예상 밖의 방식으로 처리될 수 있다고 한다. background_tasks는 다중 스레드를 이용하는 skill이니 이게 맞을 수도 있겠다. 좀 더 자세한 원인을 알아볼 예정.

회고

스프린트가 마무리 되어가는 중이지만, 실서버 배포 후 처리해야 할 일들도 꼭 기록해둬야겠다.