LLM 타임아웃과 재시도 설계, 지수 백오프가 비용을 키우는 구간

지수 백오프를 켜두면 재시도가 안전해지고 비용도 자연히 안정된다고 생각하는 분들이 많습니다. 하지만 LLM 호출처럼 요청 하나의 처리 시간이 길고 단가도 큰 환경에서는, LLM 타임아웃과 재시도 설계를 지수 백오프 하나로 밀어붙이는 순간 대기 시간과 토큰 비용이 동시에 불어나는 구간이 생깁니다. 원인은 재시도 정책 자체가 아니라, 타임아웃이 걸리는 시점에 서버가 이미 토큰을 얼마나 생성해 놓았는지를 구분하지 않는다는 … Read more

LLM 응답 캐시, 정확 일치로는 못 잡는 오답 위험

정확 일치 캐시는 같은 질문을 토씨 하나 안 틀리고 다시 물어야만 작동하고, 시맨틱 캐시는 의미가 비슷한 질문까지 잡아내 LLM 응답 캐시의 적중률을 크게 끌어올리지만 그 대가로 서로 다른 질문에 같은 답을 돌려주는 오답 위험을 함께 짊어집니다. 둘 중 한 방식만 쓰면 어느 한쪽 문제를 반드시 만나게 되므로, 실제 서비스에서는 질문의 성격에 따라 캐시 전략을 나눠 … Read more

문서가 바뀔 때마다 다시 임베딩하고 있다면 — 변경 감지와 증분 색인 설계

문서가 바뀔 때마다 컬렉션 전체를 다시 임베딩하는 파이프라인은 문서 수가 늘어날수록 비용과 처리 시간이 그대로 늘어납니다. 바뀐 문서만 골라 다시 임베딩하도록 해시 기반 변경 감지와 증분 색인을 붙이면, 같은 임베딩 API 호출 횟수를 실제 변경분 수준으로 줄일 수 있습니다. 지금 파일 하나만 고쳐도 컬렉션 전체를 재색인하는 구조로 운영하고 있다면, 이 글에서 다루는 레코드 매니저와 해시 … Read more

임베딩 모델을 바꾸면 기존 벡터는 어떻게 될까 — 전체 재색인 없이 넘어가는 전략

검색 결과 품질이 갑자기 이상해졌다는 리포트를 받고 로그를 뒤져보면, 최근에 임베딩 모델을 바꾸면서 기존 벡터를 지우지 않고 새 모델 결과와 그대로 섞어 쓴 게 원인인 경우가 꽤 많습니다. 서로 다른 모델이 만든 벡터는 좌표계 자체가 다르기 때문에 같은 인덱스 안에서 유사도를 비교하면 결과가 통째로 무너집니다. 그렇다고 수백만 건을 한 번에 재색인하려면 시간과 API 비용이 만만치 … Read more

리랭커를 넣을까 말까, 품질과 지연시간을 같이 재는 법

‘리랭커를 넣을까 말까’ — RAG 파이프라인에 검색 단계를 붙이다 보면 누구나 한 번은 검색창에 쳐보는 질문입니다. 답부터 드리면, 같은 테스트셋으로 정확도 지표(nDCG@10, Recall@k)가 얼마나 오르는지와 P95 지연시간이 서비스 허용치를 넘는지를 동시에 재본 다음 결정해야 합니다. 둘 중 하나만 보고 판단하면 품질은 좋아졌는데 응답이 느려져 이탈률이 오르거나, 반대로 속도만 지키다 답변 품질이 그대로인 채 리랭커를 뺀 … Read more

벡터 검색이 엉뚱한 문서를 물어올 때, BM25 하이브리드 검색 판단 기준

벡터 검색을 붙이면 키워드 검색보다 항상 똑똑하게 답을 찾아줄 거라 기대하는 분이 많은데요, 실제로는 벡터 검색이 엉뚱한 문서를 물어오는 상황이 꽤 자주 벌어집니다. 결론부터 정리하면, 질의에 제품 코드·에러 번호·고유명사가 섞이거나 재현율이 눈에 띄게 흔들린다면 BM25를 함께 쓰는 하이브리드 검색을 도입할 시점입니다. 반대로 질의가 대부분 자연어 질문이고 문서 수가 많지 않다면 굳이 인덱스를 두 개로 쪼갤 … Read more

SQLite로 버티다 PostgreSQL로 옮겨야 하는 시점의 신호

“동시 접속자가 늘어나면 SQLite를 버려야 한다”고 생각하는 분이 많은데, 정확한 신호는 접속자 수가 아니라 동시 쓰기 충돌과 잠금 대기 시간입니다. 읽기 위주 트래픽은 SQLite WAL 모드에서도 꽤 오래 버팁니다. 반대로 쓰기 요청이 겹치기 시작하면 접속자가 몇 명 안 되는 개인 프로젝트에서도 database is locked 에러가 뜹니다. 이 글에서는 그 신호가 코드 레벨에서 정확히 어떻게 나타나는지, … Read more

LLM 앱 로깅, 프롬프트와 응답을 어디까지 남겨야 할까

새벽에 “환불 절차 알려줘”라고 물었던 사용자 문의가, 로그 서버에는 이름과 전화번호까지 통째로 남아 있는 걸 발견한 적 있으신가요? LLM 앱 로깅은 디버깅을 위해 반드시 필요하지만, 프롬프트와 응답을 원문 그대로 저장하면 그 자체가 개인정보 저장소가 되어버립니다. 결론부터 말씀드리면 원문 전체 저장 대신 마스킹·요약·짧은 보관 기간을 조합하는 쪽이 안전하면서도 디버깅 효율을 크게 해치지 않습니다. 이 글은 Python … Read more

asyncio 앱에서 블로킹 코드 찾아내기: 이벤트 루프 지연 측정법

asyncio 앱에서 블로킹 코드를 잡아내는 가장 확실한 방법은 이벤트 루프에 짧은 대기를 반복 예약해두고, 실제로 깨어난 시각과 예약한 시각의 차이를 재는 것입니다. 이 차이가 커진다는 건 그 사이에 무언가가 루프를 붙잡고 놓아주지 않았다는 뜻이거든요. 이 글에서는 asyncio 표준 라이브러리의 디버그 모드와, 직접 짜는 워치독 코루틴 두 가지 방식으로 이벤트 루프 지연을 실제로 측정하고, 어떤 코드가 … Read more

APScheduler 예약이 재시작마다 날아갈 때, DB를 진실의 원천으로

이 글에서는 APScheduler 예약이 재시작마다 날아가는 근본 원인과, 잡스토어를 바꾸는 것만으로는 왜 부족한지, 그리고 DB를 진실의 원천(source of truth)으로 두는 틱 루프 구조로 이 문제를 근본적으로 우회하는 방법을 확인하실 수 있습니다. 핵심 답부터 말씀드리면, APScheduler의 잡 저장소를 영속화하는 것보다 “예약 정보는 DB에만 두고, APScheduler는 주기적으로 DB를 훑는 틱(tick) 하나만 담당하게” 구조를 바꾸는 편이 재시작에 훨씬 … Read more