two-thirteen
LLM을 붙이기 전에 시험지부터: EXAONE과 임베딩으로 블로그 검색 고쳐 보기
LLM을 붙이기 전에 시험지부터: EXAONE과 임베딩으로 블로그 검색 고쳐 보기
2026-11-01 LLM Engineering

LLM을 붙이기 전에 시험지부터: EXAONE과 임베딩으로 블로그 검색 고쳐 보기

온톨로지 검색이 못 찾던 질문을 LLM으로 고칠 수 있을지, 질문 32개짜리 평가 세트로 먼저 측정했습니다. 키워드 상한 하나의 효과, 로컬 EXAONE 3.5로 독자 검색어를 만든 빌드 단계 보강, bge-m3 임베딩 하이브리드 검색의 효과를 요소별 수치로 비교합니다.

"감은 잘 안 오는데요"

지난 글에서 데이터베이스 없이 온톨로지로 검색을 만들었습니다. 그래도 아쉬운 질문이 남았습니다. 검색어의 단어가 글에도, 사전에도 없으면 아무것도 나오지 않았습니다.

자연스럽게 LLM 이야기가 나왔습니다. 국산 모델인 EXAONE을 붙이면 한국어 검색이 좋아질까. 솔직히 감이 오지 않았습니다. 좋아졌다고 느끼는 것과 실제로 좋아진 것은 다릅니다.

그래서 모델보다 시험지를 먼저 만들었습니다.

질문 32개짜리 시험지

독자가 칠 법한 질문 32개를 적고, 질문마다 정답 글을 정했습니다. 일부러 글에 쓰인 표현을 피했습니다.

{"q": "크롤링하다 램이 계속 늘어남", "expect": ["example-post", "cloud-run-batch-pipeline-troubleshooting"]},
{"q": "AI가 지표를 잘못 계산함", "expect": ["next26-knowledge-catalog-business-context"]}

헤드리스 Chrome으로 실제 페이지를 열고, 브라우저 안의 검색 함수에 질문을 넣어 순위를 받습니다. 지표는 세 가지를 봤습니다.

  • hit@1: 1위가 정답인 비율
  • hit@3: 3위 안에 정답이 있는 비율
  • MRR: 첫 정답 순위의 역수 평균. 2위면 0.5, 3위면 0.33점입니다.

당시 운영 중이던 검색은 hit@1 68.8%, hit@3 87.5%, MRR 0.782였습니다. 셋 중 하나는 1위를 틀린다는 뜻입니다.

한계도 분명히 적어 둡니다. 질문을 제가 만들었습니다. 실제 독자의 검색어보다 후하게 나올 수 있습니다.

LLM보다 먼저 찾은 범인

틀린 질문을 하나씩 보다가 이상한 걸 발견했습니다. "프롬프트 인젝션 방어"를 쳤는데 보안 글이 아예 나오지 않았습니다. 그 글 본문에는 분명히 '인젝션'이 있습니다.

색인을 열어 보니 '인젝션'이 빠져 있었습니다. 글마다 점수 상위 100개 단어만 남기고 있었는데, 한 번만 나온 '인젝션'이 그 아래로 밀린 겁니다. 색인 크기를 줄이려고 정한 숫자였습니다.

상한을 200개로 올렸습니다. 그것만으로 hit@1이 68.8%에서 75.0%로 올랐습니다. LLM은 아직 한 줄도 쓰지 않았습니다.

EXAONE에게 독자 흉내를 시키기

EXAONE은 글을 생성하는 모델입니다. 검색어와 글이 얼마나 가까운지 재는 일은 원래 임베딩 모델의 몫입니다. 그래서 EXAONE에는 다른 일을 맡겼습니다. 글을 읽고 "이 글을 찾는 사람이 칠 법한 검색어"를 만들게 했습니다.

이 작업은 방문자가 검색할 때가 아니라 글을 발행할 때 한 번만 돕니다. 맥미니(16GB)에 Ollama로 EXAONE 3.5 7.8B를 띄웠고, 글 한 편에 1분 안팎이 걸렸습니다. 운영 비용도, 검색 지연도 없습니다.

첫 결과는 실망스러웠습니다.

Cloud Run 배치 타임아웃 해결 방법
BeautifulSoup 메모리 누수 방지
Cloud Run OOM 오류 해결

글의 제목과 용어를 다시 늘어놓았을 뿐이었습니다. 이미 색인에 있는 단어라 검색에 보탤 게 없었습니다.

프롬프트에 조건을 하나 넣었습니다. 검색어의 대부분은 제품 이름과 제목 단어를 쓰지 말고, 처음 문제를 겪는 사람이 증상으로 묻는 말투로 쓰라고 했습니다. 결과가 확 달라졌습니다.

배치 작업 갑자기 멈춤
메모리 계속 증가 원인
에이전트 권한 추적 어려움
여러 에이전트 공용 계정 문제
실제로 쓴 프롬프트의 규칙 부분
- queries: 이 글이 답해 주는 검색어 12개. 그중 8개 이상은 제품 이름과 제목의 단어를 쓰지 말고,
  이 문제를 처음 겪는 사람이 증상이나 목적으로 묻는 구어체로 쓰세요
  (예: "배치가 중간에 죽음", "램이 계속 늘어남", "AI가 숫자를 틀리게 계산"). 2~8단어.
- keywords: 글의 핵심 개념을 다르게 부르는 말 20개. 동의어, 한국어/영어 표기, 약어, 상위·하위 용어. 1~3단어.
- 글에 없는 사실을 지어내지 마세요. JSON만 출력하세요.

결과는 YAML 파일로 저장하고, 빌드는 그 파일만 읽습니다. 사람이 열어 보고 이상한 표현을 지울 수 있습니다. 본문이 바뀐 글만 다시 만들고, 사내 IP나 토큰처럼 보이는 표현은 보안 검사에서 버립니다. 이 표현으로만 찾은 결과에는 '(AI 보강)'이라고 표시합니다. 근거가 사람이 쓴 글이 아니라는 걸 숨기지 않으려는 것입니다.

중간에 한 번 막히기도 했습니다. JSON만 출력하게 강제하는 모드에서 로컬 모델이 가끔 500 오류를 냈습니다. 일반 모드로 한 번 더 받아 직접 파싱하는 것으로 넘어갔습니다.

보강을 넣고 다시 쟀습니다. hit@1이 75.0%에서 81.3%로 올랐습니다.

라이선스도 짚어 둡니다. EXAONE 3.5는 비상업 라이선스입니다. 광고 없는 개인 블로그에서 검색어를 만드는 정도라 문제가 적다고 봤지만, 수익화하면 다시 따져야 합니다.

단어가 하나도 안 맞을 때

보강을 해도 "램이 계속 늘어남"은 여전히 0건이었습니다. 단어 기반 검색의 한계입니다. 이 문제는 임베딩 모델이 맡습니다.

모델은 bge-m3를 골랐습니다. 100개가 넘는 언어를 다루는 다국어 모델이라 한국어 검색에 쓸 만하고, MIT 라이선스입니다. 글은 개요, 소제목 섹션, EXAONE이 만든 독자 검색어로 잘라 92조각을 임베딩했습니다. 1,024차원 벡터를 int8로 줄이니 전체가 131KB였습니다.

검색어 벡터는 방문자가 검색할 때 만들어야 합니다. 그래서 Cloudflare Pages Function을 하나 두고, Workers AI의 같은 모델(@cf/baai/bge-m3)을 부르게 했습니다. 응답은 0.2~0.4초입니다. 화면은 단어 결과를 먼저 보여주고, 입력이 멈추면 의미 점수로 순위를 다시 매깁니다.

여기서 걱정이 하나 있었습니다. 글 벡터는 로컬 Ollama로, 검색어 벡터는 Workers AI로 만듭니다. 같은 모델이라도 실행 환경이 다르면 결과가 어긋날 수 있습니다. 같은 문장을 양쪽에 넣어 비교해 보니 코사인 유사도가 0.99999였습니다. 사실상 같은 벡터입니다.

두 점수는 반씩 섞습니다.

// lex: 단어·온톨로지 점수(최고점 대비 0~1), cos: 가장 가까운 조각과의 코사인
const sem = clamp((cos - 0.40) / (0.65 - 0.40), 0, 1);
const score = 0.5 * lex + 0.5 * sem;
// 단어로는 못 찾았어도 sem이 0.45 이상이면 결과에 넣는다

이제 "램이 계속 늘어남"은 메모리 누수 글 두 편을 1·2위로 찾습니다. 찾은 이유에는 "의미가 가까운 부분 · '조용히 차오르던 메모리, 범인은 BeautifulSoup이었다'"처럼 어느 섹션이 가까웠는지 나옵니다.

숫자로 본 결과

요소를 하나씩 더해 가며 같은 시험지로 쟀습니다. 이 연재 두 편을 올리기 전, 글 12편 기준입니다.

구성 hit@1 hit@3 MRR
기존 검색 68.8% 87.5% 0.782
키워드 상한 100→200 75.0% 87.5% 0.820
+ EXAONE 보강 81.3% 90.6% 0.868
+ bge-m3 하이브리드 81.3% 96.9% 0.891

EXAONE 보강은 1위 정확도를 올렸습니다. 임베딩은 1위보다 3위 안을 채우는 데 효과가 컸습니다. 단어로는 놓쳤던 정답을 상위권으로 끌어올리지만, 1위를 바꿀 만큼 확신하지는 못하는 경우가 많았습니다.

가장 싼 개선이 가장 먼저였다는 점도 남겨 둡니다. 숫자 하나를 바꾼 효과가 LLM 보강과 비슷했습니다.

아직 못 찾는 질문

"누가 무슨 작업 했는지 추적"은 지금도 정답을 못 찾습니다. 정답은 에이전트 거버넌스 글입니다. 그 글은 같은 이야기를 '감사 로그'라는 말로 합니다. 단어 검색은 '작업'이라는 흔한 단어에 끌려가고, 의미 검색도 다른 글을 더 가깝다고 봅니다.

이런 질문은 시험지에 계속 남겨 둘 생각입니다. 실제로 실패한 검색어를 모아 시험지에 더하고, 무언가를 바꿀 때마다 전후 숫자를 비교하는 것. LLM을 붙이는 것보다 그 습관이 검색을 더 많이 고쳤습니다.