검색 인덱스를 만들었다가 접었다
검색 인덱스를 만들었다가 접었다
RAG를 몇 번 해봤는데 매번 찜찜했다. 벡터DB 세우고, 임베딩 모델 고르고, 청크 자르고, 인덱스 안 썩게 관리하고. 이 구조가 진짜 값을 하는 건지 아니면 “검색은 원래 어렵다”는 옛날 직관을 그냥 관성으로 들고 있는 건지 늘 헷갈렸다. 마침 작게 확인해볼 일이 생겼다.
만든 것
사내 모노레포에 결정 기록(ADR)이 43개 쌓여 있었다. 문서가 불어나니까 에이전트가 지금 유효한 결정을 못 찾고 옛날에 폐기된 걸 집어오면 어쩌나 싶었다. 그래서 카탈로그를 하나 만들었다. 각 ADR에 이미 적혀 있는 “이럴 때 읽어라” 한 줄만 모아서 보여주는, 파일로 저장 안 하고 부를 때마다 만드는 인덱스다.
만들어 놓고 나서야 의심이 들었다. 이게 그냥 에이전트가 grep 치는 거보다 나은 게 맞나. RAG에 품던 의심이 그대로 여기 있었다.
실험
두 조건을 비교했다.
- A: 도구 없음. 에이전트가 알아서 (
ls/grep/read). - B: 카탈로그 + “grep 말고 카탈로그부터 봐라” 안내.
질문도 두 묶음으로 나눴다.
- 1차는 질문에 쓴 단어가 ADR 파일명에 그대로 들어 있는 5문항. A가 유리하다.
- 2차는 질문 단어가 파일명에도 본문에도 없는 4문항. 그중 셋은 질문 키워드가 43개 본문에 한 번도 안 나온다. grep이 구조적으로 못 찾는 경우다. RAG가 “이게 내 차례”라고 말하는 바로 그 지점이고, 그래서 B가 유리하다.
정답 ADR은 실험 전에 적어두고, 정답률·폐기된 ADR을 잘못 집었는지·토큰·끝까지 읽은 파일 수를 쟀다.
결과
1차 5/5 = 5/5. 2차 4/4 = 4/4. 합치면 A가 9개, B가 9개 다 맞혔다. 토큰은 1차엔 카탈로그가 10% 더 쓰고 2차엔 5% 덜 써서, 부호가 뒤집힌다. 그냥 노이즈다.
B가 유리하라고 일부러 짠 2차에서도 A가 하나도 안 틀렸다는 게 좀 놀라웠다.
왜 안 틀렸냐면
순수 grep은 진짜로 못 찾는다. “token usage”라는 말이 본문에 한 번도 안 나오니까. 그런데 에이전트가 단어를 바꿔가며 친다.
- “token usage”가 안 걸리면
토큰,token_usage로 - “xlsx 편집”은 실제 쓰는 라이브러리 이름
univer로 - “에이전트 종료 처리”는
context,submit으로
제일 웃긴 건 2차 한 문항은 카탈로그를 쥐여준 B조차 카탈로그를 안 쓰고 그냥 grep으로 풀었다는 거다. 손에 도구가 있는데 안 썼다. 필요가 없어서.
미리 만들어둔 인덱스가 하려던 일을, 모델이 그 자리에서 검색어를 바꿔가며 해버렸다.
RAG로 넓혀보면
RAG든 임베딩이든 카탈로그든 결국 “검색을 미리 정리해두는” 같은 부류다. 이게 값을 한다는 전제는 하나뿐이다. 검색하는 쪽이 단어가 안 맞아서 못 찾는다는 것. 그래서 의미 공간에 미리 박아둔다.
근데 요즘 모델은 그 안 맞는 단어를 그 자리에서 메운다. 동의어 만들고, 파일명으로 찾아가고, 안 되면 다시 친다. 그러면 미리 정리해둔 값이 — 임베딩 비용이며 벡터DB 운영이며 청크 튜닝이며 인덱스 썩는 거 관리며 — 에이전트가 ls 한 번에 grep 몇 번 친 거랑 맞붙는다. 그리고 졌다.
전에도 비슷한 걸 봤다. 임베딩은 차원 수가 아니라 구조가 품질을 가른다는 것(local-hybrid-search-lessons-from-qmd), 챗봇 만들 때 이걸 LLM이 굳이 생성해야 하는지부터 물어야 한다는 것(education-chatbot-design-from-exact-match-to-rag).
그래도 구조가 이기는 경우
언제나 grep이 이기는 건 아니다. 다음 중 하나라도 걸리면 미리 만든 구조가 다시 산다.
- 코퍼스가 너무 커서 ls/grep으로 못 훑을 때 (수만 청크 단위)
- 모델이 약해서 동의어를 못 만들 때
- 파일명이나 메타데이터가 엉망일 때
- 쿼리마다 grep 왕복할 시간이 없을 때 (지연이 빡셀 때)
- 정답에 꼭 들어가야 할 문구가 정해져 있을 때 (exact match, 교육-도메인-챗봇-기술-선택-의사결정-기준)
내 경우엔 다섯 개 다 grep 쪽이었다. 그래서 카탈로그는 접었다.
남는 것
순서가 거꾸로였다. 도구를 먼저 만들고, 이게 없으면 진짜 실패하는지를 나중에 쟀다. 다음엔 에이전트한테 grep부터 쥐여주고 그게 실패하는 걸 본 다음에 구조를 사야겠다. 이번엔 싸게 끝났고, 자기가 만든 걸 자기가 안 쓰기로 한 게 생각보다 안 아깝다.
- cogni-pkm-agent — 이 노트가 사는 PKM도 벡터DB 대신 로컬 하이브리드를 골랐다.