쌓은 지식을 어떻게 찾아내나
LLM wiki + GraphRAG·하이브리드 검색을 처음부터 끝까지 — 원리는 직관적으로
/search는 /knowledge-manager(자료를 정리해 주는 AI)가 노트에 달아둔 [[링크]]·태그를 지식 그래프(개념을 점, 관계를 선으로 이은 지도)로 바꾸고, 그 그래프 + 의미로 찾는 검색 + 단어로 찾는 검색을 한데 섞어(하이브리드) 답을 찾습니다. 이 파트는 그 안의 개념을 하나도 건너뛰지 않고 — 왜 필요한지, 어떻게 작동하는지, 숫자로는 어떻게 되는지 — 풀어 보여줍니다.- 검색 한 번에 무슨 일이 벌어지는지 (큰 그림)
- 의미로 찾기(임베딩) vs 단어로 찾기(BM25), 그리고 둘을 합치는 법(RRF)
- 지식 그래프와 GraphRAG가 평범한 검색과 무엇이 다른지
- 좋은 검색인지 숫자로 증명하는 법 — 그리고 실제 벤치마크 결과
검색 한 번에 무슨 일이 벌어지나

/search "MCP가 뭐야?" 한 줄이 답이 되기까지, 네 갈래로 찾아 → 공정하게 합치고 → 정밀 심사해 → Top-5를 냅니다. 다음 장들이 이 흐름의 단어를 하나씩 풉니다.검색이라고 하면 보통 "단어가 들어간 문서 찾기"를 떠올립니다. 하지만 좋은 검색은 그보다 훨씬 많은 일을 합니다. 같은 뜻을 다른 단어로 쓴 글도 찾아야 하고("반려견"으로 물어도 "강아지" 글), 흔한 단어에 묻힌 핵심 문서를 위로 끌어올려야 하고, 여러 방식이 추천한 결과를 하나로 합쳐야 합니다. 우리 /search는 이걸 다섯 단계로 합니다.
- 네 갈래로 동시에 찾기. ㉠뜻으로(의미검색) ㉡단어 그대로(키워드검색) ㉢질문을 작은 질문으로 쪼개서 ㉣개념(엔티티) 매칭. 한 갈래만 쓰면 놓치는 걸 서로 메웁니다.
- 공정하게 합치기. 네 갈래가 각자 추천한 순위표를 RRF(순위 융합)로 합칩니다. 점수 척도가 제각각이어도 "순위"만 쓰니 안전합니다.
- 정밀 심사. 합쳐서 추린 상위 후보만 리랭커가 질문과 한 묶음으로 정독해 다시 줄세웁니다. 1차 서류전형 → 2차 면접.
- 결과 내기. 최종 Top-5 문서 + 신뢰도 4단계(high/medium/low/very_low). 여기까지가 검색의 일입니다.
- (선택) 답변 만들기. 원하면 그 Top-5를 근거로 LLM이 자연어 답까지 합성(기본 OFF). 이게 RAG의 'G(생성)' 부분 — 마지막 장에서.
[[링크]]·태그)는 /knowledge-manager(AI)가 자료를 정리하며 만든 것입니다(직접 손으로 단 게 아닙니다). 런타임 인덱서는 그 AI가 만든 링크를 규칙대로(rule-based) 그래프 DB로 옮기기만 합니다(이 단계엔 LLM 없음). 한편 "인덱싱 때 LLM이 관계를 새로 추출"·"커뮤니티 LLM 요약" 같은 마이크로소프트식 GraphRAG의 일부는 아직 미작동이라 "GraphRAG 계열"로 부릅니다(8·9장에서 자세히).RAG — 오픈북 시험의 원리
거대 언어모델(LLM)은 학습할 때 본 지식을 신경망 가중치 안에 욱여넣습니다. 이걸 파라메트릭 메모리(parametric memory, 모델 속 기억)라고 합니다. 문제는 셋입니다. ①내 개인 지식(내 vault 메모)은 학습에 없었으니 모름. ②지식이 옛날 것이라 최신을 모름. ③근거 출처를 못 대니 그럴듯하게 지어내기 쉬움(환각, hallucination). 모델을 다시 학습시키는 건 비싸고 느립니다.
RAG(검색증강생성, Retrieval-Augmented Generation)는 이 셋을 한 번에 푸는 아이디어입니다. 답하기 전에 외부에서 관련 문서를 검색해 와서, 그 문서를 질문과 함께 모델에 넣어 줍니다. 모델은 "기억"이 아니라 "눈앞의 자료"를 보고 답합니다.
- R (Retrieval, 검색): 질문과 관련된 문서를 외부 저장소에서 찾는다. ← 이 가이드북의 거의 전부가 바로 이 단계 이야기입니다(우리
/search). - A (Augmented, 증강): 찾아온 문서를 질문에 "붙여서" 모델에게 줄 입력을 만든다. ("아래 자료를 참고해 답하라: [문서1][문서2]… 질문: …")
- G (Generation, 생성): 모델이 그 자료를 근거로 자연어 답을 쓴다.
최신·개인 지식을 반영하고, 출처를 제시해 환각을 줄이며, 재학습 없이 지식만 갈아끼울 수 있습니다. 다만 답 품질이 검색 품질에 종속됩니다. 검색이 엉뚱한 문서를 가져오면 모델은 그 위에 그럴듯한 틀린 답을 씁니다("garbage in, garbage out"). 그래서 RAG의 핵심은 사실 '생성'보다 '검색'입니다 — 이 가이드북이 검색에 집중하는 이유입니다.
/search는 우리 vault에 대한 RAG의 R(검색)입니다. G(생성)는 옵션으로 켜고 끕니다(기본 OFF). 즉 평소엔 "관련 문서 Top-5"를 돌려주고, 원할 때만 그걸 LLM이 자연어 답으로 합성합니다. (논문 원전: Lewis 외 2020, arXiv:2005.11401.)임베딩 & Dense 검색 — 의미를 좌표로

키워드 검색만으로는 "반려견 키우는 법"을 물었을 때 "강아지 기르기" 글을 못 찾습니다. 글자가 다르니까요. 사람은 두 문장이 같은 뜻인 걸 압니다. 컴퓨터도 그렇게 하려면 의미를 비교 가능한 형태로 바꿔야 합니다. 그게 임베딩입니다.
임베딩(embedding)은 문장을 고정 길이 숫자 배열(벡터)로 바꾼 결과입니다. 우리 모델은 한 문장을 384개 숫자로 만듭니다. 이 숫자들은 사람이 정한 게 아니라, 신경망이 방대한 글을 학습하며 "비슷한 맥락에 쓰이는 문장은 비슷한 숫자가 되도록" 스스로 익힌 결과입니다.
"가깝다"는 보통 코사인 유사도로 잽니다. 두 벡터가 가리키는 방향이 얼마나 비슷한지를 봅니다(크기가 아니라 방향). 같은 방향이면 1.0, 직각이면 0, 반대면 -1.
이 벡터를 Dense(밀집) 벡터라 부릅니다. 384칸이 거의 전부 0이 아닌 값으로 빽빽이 차 있기 때문입니다. 다음 장에 나올 BM25의 'Sparse(희소)' 벡터(대부분 0)와 정확히 대비됩니다 — 이 둘의 차이가 하이브리드 검색의 출발점입니다.
말바꿈·동의어·번역에 강합니다("자동차"↔"승용차"↔"car"). 다만 정확한 고유명사·코드·아주 드문 용어에는 의외로 약합니다(임베딩이 그 단어를 뭉뚱그려 비슷한 것들과 섞어버림). 긴 문서는 한 점으로 압축하며 디테일이 뭉개집니다.
paraphrase-multilingual-MiniLM-L12-v2로 노트·엔티티를 384차원 벡터로 만듭니다. LLM이 아니라 작고 빠른 전용 모델이라 무료·즉시·다국어(한/영)입니다. 노트는 제목·태그·본문 앞 500자를 임베딩합니다. (왜 이 모델인지, 더 좋은 건 없는지는 12장에서 깊게 다룹니다. 논문 계보: Karpukhin 외 2020 DPR, arXiv:2004.04906.)Sparse & BM25 — 단어를 정직하게 세기
"이 문서가 이 질문에 얼마나 맞나"를 단어로 답하는 가장 오래된 질문입니다. Ctrl+F는 단어가 "있나/없나"만 봅니다. 하지만 우리는 얼마나 관련 있나를 점수로 줄세워야 합니다. 그 점수 공식의 사실상 표준이 BM25입니다(검색엔진·엘라스틱서치 등이 다 씁니다).
BM25는 순진한 방법이 왜 실패하는지를 짚으며 세 번 고쳐나가면 가장 잘 이해됩니다.
첫 직관: 질문 단어가 문서에 많이 나올수록 관련 높다(TF, Term Frequency). 맞습니다. 그런데 함정이 있습니다 — "강의 강의 강의 강의…"를 30번 도배한 문서가 3번 자연스럽게 쓴 문서보다 10배 관련 높을까요? 아니죠.
그래서 BM25는 TF를 포화(saturation)시킵니다. 처음 몇 번은 점수가 쑥 오르지만, 곧 천장에 수렴해 더 나와도 거의 안 오릅니다.
둘째 문제: "그리고", "방법" 같은 단어는 거의 모든 문서에 있습니다. 이런 흔한 단어가 많이 나온다고 관련 높은 게 아닙니다. 반대로 "가나다정밀" 같은 희귀한 단어가 들어있으면 그 문서는 거의 확실히 관련 있습니다. (예시에 쓰인 기업명 ‘가나다정밀’은 설명을 위한 가상의 이름입니다)
그래서 IDF(역문서빈도)로 단어마다 가중치를 줍니다 — 전체 문서 중 그 단어가 나오는 문서가 적을수록 가치↑.
셋째 문제: 5,000단어짜리 긴 문서는 짧은 문서보다 모든 단어를 더 많이 담습니다. 그냥 두면 긴 문서가 무조건 유리해집니다. 그래서 BM25는 문서 길이를 평균 길이와 비교해 보정합니다(파라미터 b≈0.75). 평균보다 긴 문서는 TF 점수를 깎고, 짧은 문서는 살려줍니다.
질문: 가나다정밀 강의. 두 문서를 비교합니다.
| 문서 | 길이 | "가나다정밀" | "강의" |
|---|---|---|---|
| A 짧은 강의 노트 | 200 단어 | 3회 | 2회 |
| B 거대 종합 MOC | 3,000 단어 | 8회 | 12회 |
순진한 셈이면 B 압승(8+12 > 3+2). BM25는 세 보정을 겁니다:
결과: 희귀 핵심 단어("가나다정밀")의 밀도가 높고 짧은 문서 A가 더 높게 나올 수 있습니다. "단어를 많이 담은 문서"가 아니라 "핵심 단어를 군더더기 없이 담은 문서"가 이깁니다.
BM25식 검색을 Sparse(희소) 검색이라 부릅니다. 문서를 "사전의 모든 단어 = 한 칸"인 거대한 벡터로 본다면, 한 문서엔 그중 몇 칸만 켜지고 나머지는 전부 0이라서 텅 빈(희소) 벡터가 되기 때문입니다. 앞 장 Dense(밀집, 거의 다 채워짐)와 정반대입니다.
정확한 고유명사·제품명·코드·드문 전문용어에 매우 강합니다(임베딩이 약한 바로 그 지점). 하지만 동의어·말바꿈에는 약합니다 — "반려견"으로 찾으면 "강아지"만 쓴 문서를 못 찾습니다. 그래서 다음 장의 하이브리드(Dense+Sparse)가 필요합니다.
FTS5 trigram 채널(4채널 중 가중치 0.4로 가장 큼)입니다. trigram은 글자를 3개씩 잘라 색인하는 방식인데, 한국어처럼 띄어쓰기·형태소가 까다로운 글에서 부분 문자열 매칭에 유리합니다. 다만 trigram은 2글자 미만(예 "AI", "팀")은 색인 못 해, 코드가 그런 짧은 토큰엔 LIKE 보조 검색을 둡니다. (코드: graphrag_core.py 검증 — 손석희. 원전: Robertson & Zaragoza 2009.)ANN & HNSW — 수만 점에서 빨리 찾기
2장의 의미 검색을 다시 떠올리면, 질문 벡터에서 가장 가까운 문서 벡터를 찾아야 합니다. 가장 정확하게 하려면 전부 하나씩 비교(brute-force)합니다 — 우리 엔티티 7,281개면 질문마다 7,281번 비교. 이건 우리 규모에선 순식간이지만, 벡터가 수백만~수십억 개인 대형 서비스에선 한 번 검색에 수 초가 걸립니다.
ANN(근사 최근접 이웃, Approximate Nearest Neighbor): "정확히 1등"을 포기하는 대신 "거의 1등"을 훨씬 빠르게 찾는 기법들의 총칭입니다. 약간의 정확도를 속도와 맞바꿉니다.
HNSW(계층적 탐색 가능한 작은 세계 그래프): 가장 널리 쓰이는 ANN 인덱스입니다. 점들을 여러 층의 지름길 그래프로 연결해 둡니다. 위층은 듬성듬성(멀리 점프), 아래층은 촘촘(정밀 탐색). 검색은 맨 위층에서 대충 가까운 곳으로 점프 → 한 층 내려가 더 좁히기 → … → 맨 아래층에서 정밀 탐색으로 끝냅니다.
덕분에 비교 횟수가 전체(N)에서 로그(log N) 수준으로 줄어, 수백만 점에서도 밀리초 안에 찾습니다.
대규모에서 압도적으로 빠릅니다. 하지만 근사라서 가끔 진짜 1등을 놓칩니다(속도↔정확도 trade-off). 또 인덱스를 메모리에 올리고 관리하는 운영 부담이 있습니다.
arXiv:1603.09320.)하이브리드 & RRF — 공정하게 합치기

2~3장에서 봤듯 Dense와 Sparse는 약점이 정확히 반대입니다. 한쪽이 놓치는 걸 다른 쪽이 잡습니다. 그래서 둘(실제로는 네 채널)을 같이 돌려 결과를 합치는 게 하이브리드 검색입니다.
| 질문 유형 | Dense(의미) | Sparse(BM25) |
|---|---|---|
| "반려견" → "강아지" 글 | 강함 | 약함 |
| "가나다정밀" 정확 매칭 | 약함 | 강함 |
| 코드·제품명·드문 용어 | 약함 | 강함 |
| 번역·말바꿈 | 강함 | 약함 |
두 검색의 점수 척도가 완전히 다릅니다. 의미 유사도는 0~1, BM25는 0~수십. 그냥 더하면 BM25가 항상 이깁니다. 점수를 정규화(0~1로 스케일)하는 방법도 있지만, 한 채널에 이상치 하나만 있어도 전체가 흔들립니다.
RRF(역순위 융합, Reciprocal Rank Fusion)의 발상은 단순하고 강력합니다. 점수는 버리고 순위만 쓴다. 각 채널에서 k등인 문서에게 1/(60+k)점을 주고, 채널들끼리 더합니다.
왜 상수가 60일까요? 1등과 2등의 점수 차가 지나치게 벌어지지 않게 완충하는 역할입니다(작으면 1등이 과하게 지배). 또 새 채널을 끼워 넣어도 기존 순위가 안 망가집니다 — 각 채널이 독립적으로 순위만 내면 되니까요.
| 문서 | Dense | Sparse | 분해 | 엔티티 | RRF 합 |
|---|---|---|---|---|---|
| X 두루 상위 | 2등 | 1등 | 5등 | 3등 | 0.064 |
| Y 한 채널만 1등 | 1등 | — | — | — | 0.016 |
한 채널에서만 1등인 Y보다, 여러 채널이 동시에 추천한 X가 약 4배 높습니다. "여러 관점이 합의한 문서"를 신뢰하는 것 — 이게 RRF의 본질입니다.
우리는 두 개가 아니라 네 채널을 RRF로 합칩니다. 각 채널에 가중치를 곱해 합산합니다.
| 채널 | 무엇으로 찾나 | 가중치 |
|---|---|---|
| Dense 의미 | 문장 의미 벡터(MiniLM) | 0.30 |
| Sparse 키워드 | FTS5/BM25 어휘 | 0.40 |
| Decomposed 분해 | 질문을 작은 질문들로 쪼개 검색 | 0.15 |
| Entity 엔티티 | 그래프 엔티티 벡터 매칭 | 0.15 |
리랭커 — 마지막 정밀 심사
2장의 임베딩 검색은 빠릅니다. 비결은 질문과 문서를 각자 따로 벡터로 만들어 두고(미리 계산) 비교하기 때문입니다. 이런 방식을 bi-encoder(두 개를 따로 인코딩)라 합니다. 빠른 대신, 질문과 문서가 서로를 보며 판단하지 못해 미묘한 관련성을 놓칩니다.
리랭커(reranker)는 보통 cross-encoder입니다. 질문과 문서를 하나로 이어 붙여 한 모델에 같이 넣고, "이 둘이 얼마나 맞는지"를 직접 점수 냅니다. 질문과 문서가 서로를 참고하며 판단하니 훨씬 정밀합니다.
| bi-encoder (1차 검색) | cross-encoder (리랭커) | |
|---|---|---|
| 방식 | 질문·문서 따로 벡터화 | 질문+문서 같이 입력 |
| 속도 | 빠름(미리 계산) | 느림(매번 계산) |
| 정밀도 | 보통 | 높음 |
| 쓰는 곳 | 전체에서 1차 후보 추리기 | 추려진 소수만 재정렬 |
1차 검색이 대충 끌어올린 후보의 순서를 크게 개선합니다(논문 기준 MRR@10 +27%). 하지만 느려서 전체에는 못 씁니다. 또 1차 검색이 애초에 못 끌어올린 문서는 리랭커도 살릴 수 없습니다(없는 후보는 못 고름).
BAAI/bge-reranker-base cross-encoder가 재채점한 뒤, 커뮤니티·중심성 보강을 거쳐 Top-5를 냅니다. (원전: Nogueira & Cho 2019, arXiv:1901.04085.)bge-reranker-v2-m3로 바꿔봤더니(이전 R10 실험), 오히려 top-1 정확도가 8→7로 퇴행해 폐기했습니다. "더 큰 모델 = 항상 더 좋음"이 아니라는 산 증거입니다. 12장에서 재실험 가치를 다시 따집니다.지식 그래프 & 커뮤니티

[[링크]]·태그에서 가져온다 — 지식 정리와 검색이 한 AI 파이프라인.지금까지(2~7장)는 "질문과 비슷한 문서 찾기"였습니다. 하지만 어떤 질문은 관계를 묻습니다 — "X가 무엇과 연결돼 있지?", "이 주제들이 어떻게 이어지지?". 문서를 따로따로 보면 답이 안 나옵니다. 지식 그래프는 개념·인물·문서를 점(엔티티)으로, 그 사이 연결을 선(관계)으로 그려 "이어짐"을 직접 다룹니다.
지식 그래프는 두 층입니다. TBox(개념틀)는 "세상에 어떤 종류가 있고 어떻게 연결될 수 있나"를 정한 설계도예요(예: '사람'·'프로젝트'가 있고, '사람—참여—프로젝트' 연결이 가능하다). ABox(사실)는 그 틀에 담긴 실제 데이터고요(예: '재경—참여—패스트캠 강의'). 합치면 TBox(틀) + ABox(사실) = 지식 그래프입니다.
여기가 가장 흔히 오해하는 지점입니다. ① 링크를 "만드는" 단계(지식 정리)와 ② 링크를 "그래프로 옮기는" 단계(인덱싱)를 꼭 나눠야 합니다.
- ① 링크 저작 = AI가 한다. 자료를
/knowledge-manager(LLM 에이전트)로 정리하면, AI가 내용을 읽고 관련 노트끼리[[링크]]를 걸고#태그를 답니다. 즉 "무엇이 무엇과 연결되나"(그래프의 구조)를 AI가 정합니다. (사용자가 손으로 위키링크를 다는 게 아닙니다.) - ② 인덱싱 = 규칙(rule)이 한다. 런타임 인덱서(
entity_extractor.py)는 그 AI가 달아둔 링크·태그를 정해진 규칙대로 그래프 DB로 옮깁니다 —[[위키링크]]→cites,#태그→belongs_to, 같은 문장 동시등장→co_occurs. 이 옮기는 단계엔 LLM이 전혀 없습니다.
그래프에는 촘촘히 연결된 점 무리가 자연스레 생깁니다. 이걸 자동으로 묶는 게 커뮤니티 탐지이고, 가장 널리 쓰는 알고리즘이 Louvain입니다(모듈러리티라는 "안은 빽빽, 밖은 성김" 지표를 최적화). 우리는 4계층(C0~C3)으로 묶습니다.
검색에서는 "이 커뮤니티에 속한 문서"라는 신호를 가산점으로 씁니다(관련 주제 묶음을 함께 끌어올림).
GraphRAG Local / Global — 우리의 정직한 위치
마이크로소프트가 2024년 정리한 GraphRAG는 질문을 두 종류로 나눕니다.
- Local(국소) 질문 — "X가 무엇과 연결됐지?" 특정 엔티티의 이웃을 그래프에서 탐색해 답합니다. 범위가 좁고 구체적.
- Global(전역) 질문 — "내 지식 전체의 큰 주제는?" 이건 문서 몇 개로 안 됩니다. 먼저 커뮤니티마다 LLM이 요약을 만들고(Map), 그 요약들을 다시 종합해(Reduce) 답합니다. 이 Map-Reduce 요약이 MS GraphRAG의 시그니처입니다.
우리는 그래프(엔티티·관계·커뮤니티)와 Local 탐색 구조는 실제로 갖췄습니다. 단, MS GraphRAG의 시그니처 중 인덱싱 시점의 LLM 작업은 아직 placeholder(빈 껍데기)입니다 — ①인덱서가 글을 다시 읽어 LLM으로 관계를 추출하는 부분(우리 인덱서는 그 대신 AI가 ingest 때 달아둔 링크·태그를 규칙으로 옮김), ②커뮤니티를 LLM이 문장으로 요약하는 부분(현재 요약은 "구성원 나열" 자동 라벨 9,100개 = LLM 요약 0건), ③전역 Map-Reduce 종합. 주의: 그래프의 링크 자체는 AI(KM ingest)가 만든 것이고, "LLM이 없다"는 인덱싱·요약 단계에 한정된 이야기입니다.
arXiv:2404.16130.)평가 지표 — 숫자로 증명하기
- hit@5 — 상위 5개 안에 정답이 하나라도 들어갔나(예/아니오). 24개 질문 중 몇 개 성공인지로 봅니다(우리 = 10/24). "찾았나/못 찾았나"의 기본 척도.
- precision@5 — 상위 5개 추천 중 정답(gold) 비율. 2개가 정답이면 2/5 = 0.4. "추천 5개가 얼마나 깨끗한가."
- MRR(평균 역순위) — 첫 정답이 몇 등에 나오나 →
1/등수의 평균. 1등이면 1.0, 3등이면 0.33. "정답을 얼마나 위로 올렸나." - coverage — 검색이 결과를 하나라도 돌려준 비율(우리 = 1.0, 한 번도 빈손 아님). ⚠️ 주의: "정답을 맞췄나"는 coverage가 아니라 hit@5가 봅니다. 둘은 다릅니다.
| 질문 | 첫 정답 등수 | MRR 기여 = 1/등수 |
|---|---|---|
Q01 GraphRAG | 1등 | 1 / 1 = 1.00 |
| Q12 프롬프트 설계 영향 | 2등 | 1 / 2 = 0.50 |
| Q09 Anthropic AI Safety | 3등 | 1 / 3 = 0.33 |
| Q04 Claude Code 스킬 | 못 찾음 | 0 |
한 지표만 보면 안 됩니다. 같은 "정답 1개 찾음"이라도 1등에 올렸으면(MRR 1.0) 5등에 올린 것(MRR 0.2)보다 훨씬 나은 검색입니다. hit@5는 이 차이를 못 봅니다 — 그래서 함께 읽습니다.
왜 SQLite·MiniLM인가 + 더 좋은 대안
우리 검색은 엔티티·관계·커뮤니티를 SQL로 join하면서, 동시에 이름·한국어명·설명을 빠르게 키워드 검색해야 합니다. SQLite는 서버가 필요 없고(in-process), 설정이 0이고, 트랜잭션을 지원하는 SQL 엔진이며 전체가 파일 하나(vault_graph.db, 현재 435MB)로 운용됩니다. 우리 규모에선 별도 검색 클러스터보다 "단일 DB + WAL + 읽기전용 서버"가 단순하고 안전합니다.
키워드 채널은 SQLite의 FTS5 trigram입니다. 한국어처럼 띄어쓰기·형태소가 까다로운 글에서 3글자 단위 부분 매칭이 잘 듣습니다. 이름·설명·출처를 한 FTS 테이블로 묶고 id로 join해 동명이인 충돌을 피합니다. 다만 FTS5는 어휘 검색이라 관계·말바꿈·frontmatter 전용 별칭에는 약하고(그래서 Dense·그래프로 보강), 2글자 미만 토큰은 LIKE 보조가 필요합니다.
paraphrase-multilingual-MiniLM-L12-v2는 문장을 384차원 벡터로 만드는 작은 다국어 모델입니다. 우리 인덱스는 노트 3,740 + 엔티티 7,278 벡터라, 384차원이면 전부 정확 비교(exact cosine)해도 충분히 작고 빠릅니다. API 호출이 없어 비용·지연·프라이버시 위험이 0입니다. 같은 모델로 노트와 엔티티를 모두 임베딩하고, 모델이 바뀌면 전체 재색인을 강제해 "서로 다른 의미공간이 섞이는 사고"를 막습니다. 다만 경량(384차원)이라 최신 다국어 모델보다 품질 천장이 낮고, 노트는 앞 500자만 보므로 긴 문서·관계 질의에서 신호가 약해집니다(벤치의 L1 약점·Q24 교차언어 miss가 그 신호).
| 대안 | 장점 | 정직 판정 |
|---|---|---|
| SQLite FTS5 (현재) | embedded·단일파일·트랜잭션·SQL join+그래프를 한 DB에 | 유지. 우리 규모에 최적의 단순성 |
| Tantivy | Rust 풀텍스트, BM25, 빠른 시작 | 실익 낮음 — 별도 인덱스 동기화 부담 |
| Postgres+pgvector | SQL+ACID+벡터를 한 서버에, HNSW | 조건부 — 멀티유저·10만+ 벡터면 유력. 지금은 운영비>실익 |
| LanceDB | 로컬 임베디드 벡터DB | 유지 — 벡터만 바꿔선 L1 관계 문제 안 풀림 |
| Qdrant/Chroma | 벡터DB, 메타 필터, 하이브리드 | 유지 — 서비스 운영이 늚. QPS·멀티테넌트면 검토 |
| OpenSearch | BM25+k-NN 프로덕션 검색엔진 | 유지 — 대규모·분산·검색팀 있을 때. 개인 vault엔 과함 |
| 대안 | 장점 | 정직 판정 |
|---|---|---|
| MiniLM (현재) | 384차원·로컬·무료·빠름·비용0 | 유지 — baseline으로 적합 |
| BAAI/bge-m3 | 1024차원·8192토큰·100+언어·dense/sparse/멀티벡터 한 모델 | 바꿀 가치 1순위 A/B. 교차언어·L1 의미 recall 개선 후보(재색인 비용↑) |
| multilingual-e5-large | 다국어 E5, 1024차원 | 2순위 A/B — 쿼리 prefix 등 적용 세부 필요 |
| gte-multilingual-base | 75언어·8192컨텍스트·768차원 | 2순위 A/B — 긴 문서·한국어 후보 |
| jina-embeddings-v3 | 다국어·8192·차원 축소 가능 | 실험 후보 — 장문·다국어 강점 |
| OpenAI/Cohere/Voyage | 고성능 API 임베딩 | 조건부 — 품질↑지만 API 비용·외부전송·로컬원칙과 충돌 |
리랭커는 현 bge-reranker-base 유지가 기준점이고, bge-reranker-v2-m3는 과거 퇴행 이력이 있어 새 벤치로 재실험 후 판단합니다(7장 일화).
검색이 정답 문서를 잘 찾나
난이도(레벨)별 — 어디가 강하고 약한가
| 레벨 | 설명 | hit@5 | MRR |
|---|---|---|---|
| L0 직접조회 | "GraphRAG", "MCP" 같은 키워드 | 0.83 | 0.72 |
| L1 관계 | "reranker 재정렬 원리" 같은 개념 | 0.20 | 0.13 |
| L2 멀티홉 | "프롬프트 설계가 성능에 미치는 영향" | 0.40 | 0.14 |
| L3 합성 | "내 지식 전체 핵심 주제 3가지" | 0.33 | 0.33 |
직접 키워드 조회(L0)는 아주 강함(0.83). 관계·개념(L1)이 최약점(0.2) — 12장에서 본 임베딩 보강의 1순위 타깃입니다.
실제 사례 — 무엇을 검색했고 어떻게 나왔나
MCP를 검색하면 vault에 946개가 매칭됩니다. CLI는 관련도 순위가 없어(경로·이름순) 정작 핵심 문서가 1,000개 밖으로 묻혀 못 찾았던 케이스 — 재경님이 직접 발견하신 문제.MCP-생태계-현재와-미래-MOC를 1등으로. hit@5 ✓ · MRR 1.0.Journey-13-Search-v2-4Channel-Server, Journey-16-…-CE-Mastery (우리가 정답으로 정해둔 노트)Reranker-검색-재정렬-기법, ettin-reranker-graphrag — 리랭커를 정확히 다룬 더 좋은 노트를 올림! 그런데 gold 목록에 없어 'miss'.임베딩과-벡터공간-기하학는 그 영어구가 frontmatter(aliases)에만 있고 본문은 한국어. = 메타데이터 색인 + 교차언어 이중 난이도(일부러 어렵게 낸 스트레스 테스트).Vector-Embeddings, Embedding-벡터-의미의-숫자화 — 관련은 있으나 정확 gold 아님. 진짜 retrieval 갭.전체 24개 질문 결과표 펼치기 ▾
| ID | Lv | 질문 | hit@5 | P@5 | MRR |
|---|---|---|---|---|---|
| Q01 | L0 | GraphRAG | 1 | 0.4 | 1 |
| Q02 | L0 | 프롬프트 엔지니어링 | 1 | 0.4 | 0.33 |
| Q03 | L0 | 얼룩소 아카이브 | 1 | 0.4 | 1 |
| Q04 | L0 | Claude Code 스킬 | 0 | 0 | 0 |
| Q05 | L0 | 김재경 | 1 | 0.2 | 1 |
| Q19 | L0 | MCP | 1 | 0.2 | 1 |
| Q06 | L1 | 얼룩소에서 AI에 대해 쓴 글 | 0 | 0 | 0 |
| Q07 | L1 | 에이전트 팀 구성하는 방법 | 0 | 0 | 0 |
| Q08 | L1 | 가나다정밀 마스터 강의 내용 | 0 | 0 | 0 |
| Q09 | L1 | Anthropic AI Safety 관련 연구 | 1 | 0.2 | 0.33 |
| Q10 | L1 | Obsidian 자동화 워크플로우 | 0 | 0 | 0 |
| Q20 | L1 | RAG와 GraphRAG의 차이 | 1 | 0.2 | 1 |
| Q21 | L1 | reranker 재정렬 원리 | 0 | 0 | 0 |
| Q22 | L1 | 벡터 임베딩 의미 검색 | 0 | 0 | 0 |
| Q23 | L1 | agent orchestration patterns | 0 | 0 | 0 |
| Q24 | L1 | Embedding Vector Space Geometry | 0 | 0 | 0 |
| Q11 | L2 | AI가 민주주의에 미치는 영향 (내 글) | 0 | 0 | 0 |
| Q12 | L2 | 프롬프트 설계가 에이전트 성능에 | 1 | 0.2 | 0.5 |
| Q13 | L2 | Knowledge Manager에서 GraphRAG | 1 | 0.2 | 0.2 |
| Q14 | L2 | 2026 AI 에이전트 트렌드·사례 | 0 | 0 | 0 |
| Q15 | L2 | 얼룩소 플랫폼 분석·콘텐츠 전략 | 0 | 0 | 0 |
| Q16 | L3 | 내 지식 핵심 주제 3가지 | 0 | 0 | 0 |
| Q17 | L3 | AI 연구→강의→실무 전체 여정 | 0 | 0 | 0 |
| Q18 | L3 | 정치/사회 글쓰기와 AI 연구 연결 | 1 | 0.2 | 1 |
haiku · sonnet · opus가 답하면
모델 종합 점수 (24문항 평균)
| 티어 | answer | 정확도 | 근거 | 환각↓ | 검색거부 |
|---|---|---|---|---|---|
| haiku | 61.1 | 59.8 | 52.9 | 0.29 | 2 |
| sonnet | 70.8 | 74.4 | 71.5 | 0.18 | 0 |
| opus | 66.6 | 67.2 | 60.5 | 0.24 | 1 |
sonnet이 sweet spot — 정확도·근거·환각·안정성(검색거부 0회) 모두 최고이면서 opus보다 쌉니다. (환각↓ = 낮을수록 좋음)
난이도별 — 큰 모델이 항상 낫진 않다
| 레벨 | haiku | sonnet | opus | 한 줄 |
|---|---|---|---|---|
| L0 직접 | 43.5 | 64.2 | 64.8 | sonnet=opus, sonnet이 저렴 |
| L1 관계 | 62.1 | 78.9 | 78.6 | sonnet 최강 |
| L2 멀티홉 | 77.8 | 67.4 | 39.6 | opus 붕괴(날조) |
| L3 합성 | 65 | 63 | 75 | opus 우위(깊은 종합) |
실제 사례 — 모델이 어떻게 다르게 답하나
전체 24개 질문 × 3모델 점수표 펼치기 ▾
| ID | Lv | 질문 | haiku | sonnet | opus |
|---|---|---|---|---|---|
| Q01 | L0 | GraphRAG | 72 | 58 | 15✗ |
| Q02 | L0 | 프롬프트 엔지니어링 | 5✗ | 72 | 82 |
| Q03 | L0 | 얼룩소 아카이브 | 25 | 72 | 89 |
| Q04 | L0 | Claude Code 스킬 | 72 | 85 | 80 |
| Q05 | L0 | 김재경 | 15 | 20 | 35 |
| Q19 | L0 | MCP | 72 | 78 | 88 |
| Q06 | L1 | 얼룩소 AI 글 | 78 | 82 | 72 |
| Q07 | L1 | 에이전트 팀 구성 | 62 | 85 | 58 |
| Q08 | L1 | 가나다정밀 강의 | 75 | 82 | 78 |
| Q09 | L1 | Anthropic AI Safety | 42 | 68 | 72 |
| Q10 | L1 | Obsidian 자동화 | 62 | 78 | 85 |
| Q20 | L1 | RAG와 GraphRAG 차이 | 72 | 80 | 88 |
| Q21 | L1 | reranker 재정렬 | 78 | 92 | 85 |
| Q22 | L1 | 벡터 임베딩 검색 | 72 | 68 | 82 |
| Q23 | L1 | agent orchestration | 5✗ | 72 | 81 |
| Q24 | L1 | Embedding Vector Space | 75 | 82 | 85 |
| Q11 | L2 | 민주주의 영향 (내 글) | 82 | 15 | 10 |
| Q12 | L2 | 프롬프트 설계 영향 | 88 | 90 | 0 |
| Q13 | L2 | KM에서 GraphRAG | 72 | 82 | 85 |
| Q14 | L2 | 2026 에이전트 트렌드 | 65 | 72 | 78 |
| Q15 | L2 | 얼룩소 콘텐츠 전략 | 82 | 78 | 25 |
| Q16 | L3 | 핵심 주제 3가지 | 65 | 72 | 78 |
| Q17 | L3 | AI 연구→강의→실무 | 68 | 82 | 79 |
| Q18 | L3 | 정치 글쓰기·AI 연결 | 62 | 35 | 68 |
✗ = 검색거부(검색 0회). 점수 0~100, 환각은 0~1(낮을수록 좋음).
어디서 왔나 — 8편의 원전
| 개념 | 논문 · 저자 · 연도 | 핵심 기여 | 우리 적용 (정직) |
|---|---|---|---|
| RAG | Lewis 외, 20202005.11401 | LLM(기억)+외부 검색(인덱스) 결합. RAG 원전. | /search=검색(R). 생성(G)은 opt-in |
| BM25 | Robertson & Zaragoza, 2009 | TF 포화·길이정규화·IDF 확률 랭킹. | Sparse = FTS5 trigram(BM25 계열) |
| Dense | Karpukhin 외, 20202004.04906 | 의미 벡터 검색. BM25 대비 +9~19%p. | 로컬 MiniLM-L12-v2 (학습방식은 다름) |
| HNSW | Malkov & Yashunin, 20181603.09320 | 계층 그래프 근사 최근접(ANN). | 개념만 — 미사용(규모 작음) |
| RRF | Cormack 외, 2009 | 점수 아닌 순위로 Σ1/(k+rank) 융합. | 4채널 핵심 1/(60+rank) — 코드 1:1 ✅ |
| Cross-encoder | Nogueira & Cho, 20191901.04085 | (질문,문서) 동시 입력 재정렬. MRR@10 +27%. | 리랭커 bge-reranker-base |
| GraphRAG | Edge 외(MS), 20242404.16130 | LLM 추출→커뮤니티 요약→Local/Global. | 계열. LLM추출·요약·Map-Reduce=placeholder → MS 동등 ❌ |
| RAGAS | Es 외, 20232309.15217 | 정답 없이 faithfulness 자동평가. | 외부 미도입 — 자체 judge가 같은 정신 |
FTS5 trigram · Dense = paraphrase-multilingual-MiniLM-L12-v2 · 리랭커 = BAAI/bge-reranker-base · RRF k=60 4채널. HNSW/faiss = 미사용.보강 결과 & 한 장 요약
- →임베딩 A/B (BGE-M3) → 라이브 적용 완료. 우리 24문항 벤치 정답률 18 → 23/24 (소형 MiniLM → 다국어에 강한 bge-m3 + 채널 합산 방식 개선). 라이브 검색에 이미 켜져 동작 중.
- →gold-set 재검토 → 위에서 예고한 그대로 Q07·Q08·Q21은 검색이 옳고 우리가 적어둔 정답이 좁았던 것(교정). 단 Q09 한 개는 교정해도 못 잡은 진짜 약점으로 정직하게 남겼습니다(정답 끼워맞춰 24 만들기 ❌).
- →과적합 아님 증명 → 24문항과 겹치지 않는 48개 독립 질문으로도 측정 → 9→15 동반 상승(하나도 안 깎임). "벤치 하나로 전체 시스템 못 잡는다"를 광범위 검증으로 해소.
gr_goldfix_24q.json·broadval-48q.
(a) 자동 튜닝 (autoresearch)
한 변수만 바꿔 좋아지면 keep·나빠지면 버리는 자동 루프(autoresearch)를 119라운드 돌렸습니다. 결과: 현 가중치가 이미 최적 — baseline(hit@5 10/24·MRR 0.307)을 못 넘었어요. 합성점수만 높고 hit@5를 잃은 후보는 거부 = 정직한 negative result.
(b) LLM 답변합성
코드 완료 · 기본 OFF(opt-in). provider를 교체 가능한 구조로 — claude -p는 6/15부터 별도 과금이라 폐기, codex_cli 주력. 어느 경로가 막혀도 기본 검색은 멀쩡.
진짜 다음 레버 (벤치가 가리킨 것)
| 레버 | 왜 |
|---|---|
| 임베딩 A/B (BGE-M3 1순위) | L1 관계·교차언어 약점의 후보 (단 "모델만으로 해결"은 과장 — 12장) |
| gold-set 재검토 | Q08·Q21처럼 검색은 좋은데 gold가 좁아 'miss'로 찍힌 건 벤치를 고치는 문제 |
| frontmatter/alias 색인 | Q24 같은 진짜 갭(교차언어·메타데이터-only)을 잡는 retrieval 보강 |
| 답변합성 켜기 · 기본 sonnet | 검색 Top-5를 자연어 답으로(게이트). 모델 기본값 = sonnet |
한 장 요약
우리 /search = GraphRAG 계열 — AI(/knowledge-manager)가 만든 [[링크]]·태그를 규칙으로 옮긴 지식 그래프 + 4채널 하이브리드. MS의 인덱스-시점 LLM 추출·요약·Map-Reduce는 placeholder → "동등"은 과장, "계열"은 사실.
하이브리드 = 의미(dense)+키워드(BM25/FTS5)+질문분해+엔티티를 RRF(순위 융합)로 합치고 cross-encoder로 재정렬해 Top-5. HNSW는 우리 규모엔 불필요.
벤치(24문항·3모델): 검색은 직접조회(L0) 강·관계(L1) 약. 답변은 sonnet sweet spot(정확·안전), opus는 깊은 종합만·검색 약하면 날조.
도구 선택: 우리 규모엔 SQLite/FTS5 + MiniLM 유지가 합리적. 바꿀 가치는 저장소가 아니라 임베딩 모델 A/B(BGE-M3).
메타 교훈: 지식을 정리하는 AI 파이프라인(/knowledge-manager)이 남긴 링크·태그가 그대로 검색 그래프의 엣지가 된다 — 정리와 검색이 한 흐름 / 평가엔 ground-truth 앵커가 필수(모델은 모르면 자신있게 지어낸다).
근거: 벤치 aktofu-bench-table.md · 논문 domain-konan-papers.md · 스택분석 sonseokhee-stack-rationale-alternatives.md · live vault_graph.db 2026-06-07 KST
여기까지 — 그리고 다음
검색의 속을 다 열어봤습니다. 의미 + 단어 + 그래프를 섞어 순위를 매기고, 리랭커로 마지막 정밀 심사를 한 뒤 Top-5를 내놓는다 — 이게 우리 /search입니다.
→ Part 3: 이 검색을 봇 에이전트들이 실제로 어떻게 쓰는가 — vault·Discord·여러 봇이 한 몸처럼 도는 원리.