01데모와 운영 사이의 거리
RAG 데모는 하루면 만듭니다. 문서를 임베딩하고, 유사도 상위 5개를 찾고, 프롬프트에 붙여 넣습니다. 시연하면 잘 동작합니다.
어려운 건 그 다음입니다. 매일 사람들이 실제 질문을 던지고, 데이터가 계속 늘고, 답이 틀리면 사람들이 곧바로 안 쓰기 시작하는 환경에서 6개월을 버티는 것. 우리가 자사 서비스를 사내 전 직원 대상으로 운영하며 부딪힌 문제들을 정리했습니다.
02초기 가정 vs 현실
| 초기 가정 | 실제 |
|---|---|
| 좋은 임베딩 모델이면 검색은 해결된다 | 한국어에서 조사·부분일치 문제로 조용히 틀립니다 |
| 문서만 넣으면 답이 나온다 | 집계형 질문("몇 건이야?")은 RAG로 못 답합니다 |
| 응답은 몇 초면 된다 | 30초를 넘기는 순간 인프라 레이어에서 끊깁니다 |
| 사용자는 잘 물어볼 것이다 | 대부분 "요즘 어때?" 같은 모호한 질문을 합니다 |
| LLM만 붙이면 된다 | 진짜 병목은 모델이 아니라 데이터가 쌓이는가였습니다 |
03문제 1. "LLM이 빈 응답을 반환합니다"
가장 오래 헤맨 문제입니다. 특정 질문에서 답이 통째로 비어 돌아왔습니다. 모델 오류도, 타임아웃도 아니었습니다.
원인 추론형(reasoning) 모델을 쓰고 있었는데, 이 계열은 최종 답변을 내기 전에 내부 추론 토큰을 대량으로 소비합니다. 그런데 출력 토큰 예산은 추론과 답변이 공유합니다. 복잡한 질문일수록 추론이 예산을 다 먹고 정작 답변 자리가 0이 됩니다. 로그에는 정상 응답으로 찍히고 본문만 빕니다.
해법
- 출력 토큰 상한을 관리자 설정으로 빼고 하한선을 8,000으로 올렸습니다.
- 사용자 질의 경로에서는 추론 모드를 끄도록 했습니다.
여기서 진짜 교훈이 나왔습니다. 추론 모드를 껐는데 증상이 남았습니다. 확인해 보니 LLM 호출 경로가 코드에 두 벌 있었고 한쪽만 고쳤던 것입니다. 오래된 프로젝트에서 흔한 함정입니다. LLM 호출은 반드시 단일 진입점으로 모으십시오. 안 그러면 설정이 절반만 적용된 상태를 몇 주간 못 찾습니다.
04문제 2. 30초 넘는 응답이 통째로 끊긴다
보고서 생성처럼 긴 작업에서 응답이 사라졌습니다. 백엔드 로그에는 성공, 브라우저에는 실패로 찍혔습니다.
원인 앞단 게이트웨이의 read 타임아웃. 애플리케이션이 아무리 잘 돌아도 중간 인프라가 먼저 끊습니다.
해법 긴 작업은 예외 없이 SSE(스트리밍) 로 전환했습니다. 토큰이 오는 대로 흘려보내면 연결이 유휴 상태가 되지 않아 타임아웃에 걸리지 않고, 사용자도 진행 중임을 봅니다. 부수 효과가 더 컸습니다 — 같은 총 소요시간이어도 체감 대기가 극적으로 줄었습니다.
05문제 3. 어느 날 질문이 전부 무응답
특정 시점부터 AI 질의가 통째로 멈췄습니다. 서버는 살아 있고 다른 화면은 정상이었습니다.
원인 DB 커넥션 풀 고갈. 코드가 DB 커넥션을 쥔 채로 LLM 호출을 기다리고 있었습니다. LLM 응답이 수 초~수십 초 걸리는 동안 커넥션이 계속 점유되고, 동시 질의 몇 건이면 풀이 바닥납니다.
해법
- 파이프라인을
prepare(DB 조회) → generate(LLM 호출) → 저장(DB)으로 쪼개, LLM 호출 구간에는 커넥션을 쥐지 않도록 분리했습니다. - 풀 크기를 8 → 20으로 올렸습니다. (2번만으로는 시간을 벌 뿐 재발합니다. 1번이 본질입니다.)
06문제 4. 집계형 질문에 RAG는 무력하다
"이번 달 리스크 건수는?" 같은 질문에 답이 계속 틀렸습니다. 상위 K개 문서만 보고 세니 당연했습니다.
해법 질문 유형을 나눴습니다.
- 서술형 ("○○기업 진행 상황 알려줘") → 일반 RAG
- 집계형 ("몇 건", "누가 제일 많이") → 사전 집계된 값을 프롬프트에 주입
팀·고객사·제품 단위 집계 문서를 배치로 미리 만들어 두고, 집계형 질의에는 이 문서를 근거로 강제 투입합니다. LLM에게 계산을 시키지 않는 것이 핵심입니다. 모델은 요약과 서술을 맡고, 숫자는 DB가 냅니다.
07문제 5. 최신 정보를 못 찾는다
"요즘 어때?"류 질문에서 2년 전 문서를 근거로 답하는 일이 잦았습니다. 의미적으로는 유사했기 때문입니다.
해법 현황성 질의는 검색 범위를 최근 데이터로 한정했습니다. 유사도만 보면 오래된 문서가 이기는 경우가 많은데, 사용자가 원하는 건 대개 "지금"입니다. 시간 가중치는 선택이 아니라 필수입니다.
086개월 뒤 남은 교훈
1. 모델 선택은 생각보다 덜 중요했습니다. 답변 품질을 좌우한 건 모델 체급이 아니라 어떤 근거를 넣어주느냐였습니다. 같은 모델로도 검색을 고치면 체감 품질이 크게 달라집니다.
2. 진짜 병목은 데이터였습니다. 기록이 없으면 어떤 RAG도 답하지 못합니다. 우리가 AI보다 작성 화면에 더 많은 시간을 쓴 이유입니다.
3. "모른다"고 답하게 만든 것이 신뢰의 분기점이었습니다. 그럴듯하게 지어낸 답 한 번이면 사용자는 도구 전체를 불신합니다. 근거가 없으면 답하지 않는 편이 낫습니다.
4. 관측 가능성이 없으면 개선도 없습니다. 어떤 질문이 들어왔고 어떤 근거가 쓰였는지 로그로 남기지 않으면, 왜 틀렸는지 영원히 모릅니다.
5. 파생물은 실패해도 원문은 안전해야 합니다. 임베딩·요약·연동을 모두 비동기로 두고 멱등하게 재처리하도록 만든 덕분에, 배치가 실패한 날에도 사용자의 기록은 아무 영향을 받지 않았습니다.
