인프라 2026.06 말, 배포 직후
로컬에서는 되는데 서버에서만 죽던 문제
로컬에서는 멀쩡하던 봇을 서버(GCP VM)에 올리자 OpenAI 호출이 전부 ERR_STREAM_PREMATURE_CLOSE로 실패했습니다. API 키 정상, 크레딧 정상, 같은 요청을 curl로 보내면 200이 나오는 상황이라 원인을 좁히기가 더 어려웠습니다.
며칠을 파서 찾은 원인은 버전 조합이었습니다. OpenAI가 응답을 gzip으로 압축해 보내는데, SDK가 쓰는 node-fetch가 VM의 Node v24에서 그 압축을 풀다가 스트림을 조기 종료합니다. 로컬은 v25라 재현조차 되지 않았습니다.
수정은 결국 헤더 한 줄이었습니다. Accept-Encoding: identity, 압축 응답을 아예 받지 않는 방식입니다.
전부 정상인데 안 될 때는 대개 버전 조합이 원인이었습니다. 원인 규명에 며칠, 수정은 한 줄.
품질 2026.07.02
회의록이 자꾸 세 줄로 요약되던 문제
1시간 회의(전사본 18,060자)를 넣었는데 논의 내용이 소제목 5개에 불릿 한두 개씩만 나왔습니다. 회의에 있던 사람이 보기에는 "이게 다야?" 소리가 나오는 분량이었습니다.
처음에는 프롬프트 문제라고 생각해서 "뭉치지 말고 개별 논점으로 복원하라"고 강화했습니다. 결과는 반대로, gpt-4o는 오히려 1,047자로 더 짧아졌습니다. 그래서 실제 전사본을 고정해 두고 모델과 프롬프트를 한 변수씩 바꿔 가며 비교했더니, 같은 강화 프롬프트를 gpt-4.1에 돌리자 3,856자가 나왔고 비용 논의, 배제된 대안, 결정의 근거까지 살아 돌아왔습니다.
문제는 프롬프트가 아니라 모델이었습니다. 모델을 교체해 해결했습니다.
프롬프트를 열 번 고치기 전에, 변인을 하나씩 분리해서 실측해야 한다는 걸 배웠습니다.
설계 2026.07.06 ~ 07.10
2시간 회의는 회의록이 아예 안 나오던 문제
긴 회의만 들어오면 429 Request too large … TPM 30000 에러가 났습니다. 모델의 분당 토큰 한도가 30,000인데 긴 전사본 하나가 6만 토큰을 넘겨서, 재시도를 해도 영원히 실패하는 구조였습니다.
1차 시도는 교과서식 map-reduce였습니다. 전사본을 조각내 정제하고 합치는 방식인데, 429는 사라졌지만 이번에는 글이 죽었습니다. 최종 회의록을 쓰는 모델이 원문을 한 번도 못 보고 남이 정리한 메모로만 쓰는 셈이라 디테일이 계속 사라졌습니다.
2차에서 접근을 뒤집었습니다. 작은 모델(mini)은 분당 한도가 200,000이라 전사본 전체를 한 번에 읽을 수 있습니다. 그래서 mini에게는 몇 번째 줄부터 몇 번째 줄이 어떤 안건인지 목차만 뽑게 하고, 본문은 gpt-4.1이 안건마다 해당 구간의 원문을 다시 읽으면서 쓰게 했습니다. 압축 손실이 없습니다.
요약을 잘하게 만드는 것보다, 요약이 필요 없는 구조를 찾는 쪽이 답일 때가 있습니다.
신뢰 2026.07.08
회의록에 아무도 안 한 말이 들어간 문제
어느 날 회의록에 "클라우드 아키텍트, 프론트엔드, LangGraph…" 같은 줄이 생겼습니다. 그날 회의에서 아무도 그런 말을 하지 않았는데요.
원인은 정확도를 올리려고 넣은 장치였습니다. 전사 모델(STT)에 팀 용어집을 프롬프트로 주면 "랭그래프" 같은 전문용어를 제대로 받아쓰는데, Whisper 계열 모델은 침묵 구간을 만나면 그 프롬프트를 자기가 들은 말처럼 그대로 출력합니다. 정확도를 높이려던 용어집이 가짜 내용의 유입구가 된 겁니다.
전사 결과에서 용어집 단어와 참석자 이름을 지워 보고, 남는 글자가 20% 미만이면 그 구간을 통째로 버리는 필터(isPromptEcho)를 넣어 해결했습니다. 요정은 이제 조용한 회의실에서 혼잣말을 하지 않습니다.
기능을 넣는 코드보다, 결과를 버릴 조건을 정하는 코드가 더 어려웠습니다.
운영 2026.07.14
아무도 없는 새벽에 CPU 93% 경고가 오던 문제
봇이 힘들면 DM으로 알려주는 기능을 넣었더니, 아무도 안 쓰는 새벽에 "주인님, 저 지금 좀 벅차요! CPU 부하 93%" 경고가 계속 왔습니다. 녹음 0개, 메모리도 정상이었는데요.
원인은 측정 방식이었습니다. CPU 사용률이라고 믿고 쓴 loadavg는 사실 실행 대기줄의 길이를 재는 값이고, 제 서버 같은 공유 vCPU에서는 같은 물리 서버의 옆 VM이 바쁜 것까지 값에 잡힙니다. 봇이 놀고 있어도 0.9가 나옵니다. 봇은 멀쩡한데 모니터링이 옆집 소음을 제 부하로 신고하고 있던 셈입니다.
process.cpuUsage()로 봇 프로세스가 실제로 쓴 CPU만 재도록 바꿨습니다. 실측 결과 놀 때 0.1%, 일부러 한 코어를 태우면 92.5%로 정확히 구분됩니다.
모니터링이 부정확하면 존재하지 않는 장애가 만들어집니다.