한국어 문서 AI 검색 정확도를 높이려면 모델을 바꾸기 전에 질문·문서 인코딩 설정과 원문 분할 상태부터 확인해야 한다. 검색어와 비슷한 문장은 나오는데 정작 답이 있는 문서는 빠진다면, 접두어 누락이나 입력 잘림이 원인일 수 있다.
이 글은 설정을 직접 점검하는 단계별 가이드다. 임베딩 검색의 기본 구조를 짚은 뒤, 같은 한국어 질문과 문서를 사용해 설정별 상위 결과를 비교하고, 마지막으로 모델별 프롬프트와 입력 길이를 확인한다. 2026년 기준으로 확인 가능한 공식 문서를 바탕으로 작성하되, 과거 벤치마크를 2026년 신규 실험 결과로 소개하지 않는다.
1. 한국어 임베딩 검색의 정확도는 질문과 문서의 역할 구분에서 시작한다
임베딩 검색은 질문과 문서를 숫자 벡터로 변환한 뒤, 벡터 사이의 유사도로 관련 문서를 찾는 방식이다. 같은 단어가 없어도 의미가 비슷한 내용을 찾을 수 있어, “암호를 잊었을 때”라는 질문으로 “비밀번호 재설정” 안내를 검색하는 데 활용할 수 있다.
다만 질문과 문서는 역할이 다르다. 질문은 필요한 정보를 짧게 표현하고, 문서는 그 답과 주변 설명을 담는다. 모델이 이 차이를 반영하도록 학습됐다면 검색 시에도 해당 모델의 질문용·문서용 입력 형식을 따라야 한다.
Sentence Transformers 공식 문서는 검색 작업에 encode_query()와 encode_document()를 권장한다. 전자는 등록된 query 프롬프트를 사용하고, 후자는 document·passage·corpus 순서로 사용 가능한 프롬프트를 찾는다. 별도 프롬프트나 역할별 처리가 없는 모델에서는 두 메서드가 일반 encode()와 같은 방식으로 동작할 수 있다. 근거: Sentence Transformers 공식 Usage 문서.
📌 핵심 요약
- 질문·문서 메서드를 나누고, 모델이 요구하는 프롬프트가 실제로 적용되는지 확인한다.
- 동일한 질문과 문서에서 설정 하나만 바꿔 상위 검색 결과를 비교한다.
- 유사도 점수보다 정답 문서의 순위를 확인하고, 답이 입력 길이 제한으로 잘렸는지 따로 점검한다.

2. encode_query·encode_document로 한국어 문서 검색 예제 구성하기
다음은 intfloat/multilingual-e5-large를 이용한 진단용 예제다. 문서는 코드 동작을 설명하기 위해 작성한 테스트 문장이며, 실제 서비스의 정책이나 수집 데이터가 아니다. 질문의 정답을 담은 문서를 D1으로 지정하고, 단어는 비슷하지만 해결 방법이 다른 문서를 함께 넣었다.
예제의 기준 환경은 Python 3.11, sentence-transformers 5.1.0이다. 최신 버전이라는 의미가 아니라 비교 환경을 고정하기 위한 선택이며, 아래 코드는 이 글에서 실행한 측정 결과가 아닌 재현용 절차다. 모델은 별도의 숫자형 버전 대신 Hugging Face 저장소의 커밋 SHA를 조회해 고정하고, 실제 설치된 라이브러리 버전도 출력한다. 배포 확인: PyPI sentence-transformers 5.1.0.
python -m pip install "sentence-transformers==5.1.0"
처음 실행할 때는 모델을 내려받을 네트워크 연결이 필요하다. CPU에서도 실행할 수 있지만 처리 시간과 메모리 사용량은 환경에 따라 달라진다. 아래 코드는 접두어를 원문에 직접 붙이지 않고, 메서드의 prompt 인자로 한 번만 적용한다.
import sys
from importlib.metadata import version
from huggingface_hub import model_info
from sentence_transformers import SentenceTransformer
MODEL_ID = "intfloat/multilingual-e5-large"
# 재실행 시에는 첫 실행에서 출력된 SHA 문자열로 교체한다.
REVISION = model_info(MODEL_ID).sha
print("Python:", sys.version.split()[0])
for package in [
"sentence-transformers", "transformers",
"torch", "huggingface-hub"
]:
print(package, version(package))
print("model:", MODEL_ID, "revision:", REVISION)
model = SentenceTransformer(
MODEL_ID, revision=REVISION, device="cpu"
)
model.max_seq_length = 512
print("registered prompts:", model.prompts)
query = "로그인 비밀번호를 잊었을 때 어떻게 바꾸나요?"
docs = [
("D1", "로그인 화면의 비밀번호 찾기를 선택하고 "
"본인 인증을 마치면 새 비밀번호를 설정할 수 있습니다."),
("D2", "로그인한 상태에서는 계정 설정에서 "
"현재 비밀번호를 입력하고 비밀번호를 변경합니다."),
("D3", "로그인 아이디를 잊었다면 가입 이메일로 "
"아이디 찾기를 진행합니다."),
("D4", "비밀번호는 다른 서비스와 다르게 설정하고 "
"타인에게 공유하지 않습니다."),
]
texts = [text for _, text in docs]
def search(label, query_prompt, document_prompt):
q = model.encode_query(
[query], prompt=query_prompt,
normalize_embeddings=True, convert_to_tensor=True
)
d = model.encode_document(
texts, prompt=document_prompt,
normalize_embeddings=True, convert_to_tensor=True
)
scores = (q @ d.T)[0]
order = scores.argsort(descending=True).tolist()
print("\n", label)
for rank, index in enumerate(order[:3], start=1):
print(rank, docs[index][0],
round(scores[index].item(), 4), docs[index][1])
answer_rank = order.index(0) + 1
print("D1 순위:", answer_rank)
print("Hit@3:", int(answer_rank <= 3))
search("접두어 생략 조건", "", "")
search("E5 권장 접두어 조건", "query: ", "passage: ")
실행 환경을 다시 만들 때는 출력 내용과 함께 python -m pip freeze 결과를 보관한다. 모델 이름이 같아도 저장소가 갱신될 수 있으므로, 이후 비교에서는 출력된 커밋 SHA를 REVISION에 직접 넣어야 한다. 라이브러리와 모델 리비전을 함께 기록해야 순위 변화의 원인을 좁힐 수 있다.
2. encode_query·encode_document로 한국어 … 실시간 확인하기
3. 같은 한국어 질문의 상위 검색 결과와 코사인 유사도 비교하기
코드를 실행하면 접두어 생략 조건과 E5 권장 조건의 상위 3개 문서 ID·점수·본문이 각각 출력된다. 확인할 핵심은 “점수가 얼마나 올랐는가”보다 비밀번호를 잊은 상황을 해결하는 D1이 몇 번째에 나오는가다. 두 조건에서 D1이 모두 1위라면 이 작은 예제만으로는 개선 효과를 입증할 수 없다.
D2는 비밀번호 변경이라는 단어가 일치하지만 현재 비밀번호를 알고 로그인한 상태를 전제로 한다. D2가 앞선다면 “잊었다”라는 조건을 충분히 구분하지 못한 사례로 기록할 수 있다. 이렇게 정답과 혼동 문서를 함께 확인하면 단순히 비슷한 문장이 검색되는 것과 답을 주는 문서가 검색되는 것을 구분하기 쉽다.
- 두 출력에서 D1의 순위와 상위 3개 문서 구성을 나란히 기록한다.
- 문서·질문·분할 방식은 그대로 두고 접두어 설정만 비교한다.
- 실제 검색 로그에서 질문을 추가하고, 질문마다 정답 문서 ID를 지정한다.
- 정답 포함 여부와 첫 정답 순위를 집계해 설정 변경 전후를 평가한다.
예제의 Hit@3은 정답 D1이 상위 3개에 있으면 1, 없으면 0이다. 여러 질문에 대해 평균을 내면 상위 3개 안에 정답이 포함된 질문의 비율을 구할 수 있다. 정답이 여러 개인 평가에서는 전체 정답 중 얼마나 검색했는지 보는 Recall@k와 구분해서 사용해야 한다.
L2 정규화한 벡터끼리의 내적은 코사인 유사도와 같다. E5 공식 모델 카드는 점수가 0.7~1.0 부근에 모일 수 있으며, 학습 온도 0.01에 따른 예상된 현상이라고 설명한다. 따라서 0.85를 정답 확률 85%로 해석하거나 모든 모델에 동일한 합격 기준으로 적용하면 안 된다. 근거: intfloat multilingual-e5-large 공식 모델 카드.
4. E5·BGE-M3·Qwen3의 프롬프트와 입력 길이 확인하기
질문·문서 메서드를 나눴다고 모델별 입력 규칙까지 자동으로 보장되는 것은 아니다. 로드된 model.prompts를 확인하고, 등록 내용이 없다면 공식 모델 카드에 맞춰 프롬프트를 명시해야 한다. 원문에 접두어를 붙인 상태에서 같은 프롬프트를 추가하면 중복 입력이 된다.
| 모델 | 검색 입력 규칙 | 공식 사양 |
|---|---|---|
| intfloat multilingual-e5-large | 한국어에도 질문 query: / 문서 passage: 적용 | 1,024차원, 최대 512토큰 |
| BAAI BGE-M3 | 검색 질문에 별도 지시문을 붙일 필요 없음 | 1,024차원, 최대 8,192토큰 |
| Qwen Qwen3-Embedding-0.6B | 질문에 검색 과제 지시문 적용, 문서는 지시문 없이 처리 | 최대 1,024차원, 32K 문맥 길이 |
사양 근거: intfloat 공식 카드, BAAI 공식 카드, Qwen 공식 카드. 실제 입력 한도는 라이브러리와 실행 설정도 함께 확인해야 한다.
Qwen 개발진은 지시문을 사용했을 때 다수의 후속 과제에서 통상 1~5% 개선됐다고 보고하며, 다국어 환경에서도 영어 지시문을 권장한다. 이는 개발진의 평가 결과로, 한국어 사내 문서에서도 같은 폭으로 개선된다는 보장은 아니다. Qwen3로 바꿀 때는 E5 접두어를 제거하고, 질문에 Instruct: 검색 과제 설명\nQuery: 질문 형태를 적용하는 방식으로 별도 평가한다. 근거: Qwen3-Embedding 공식 사용 예제와 지시문 평가 설명.
모델 선택의 참고 자료로 E5 공식 카드의 Mr. TyDi 한국어 MRR@10은 small 54.3, base 55.8, large 61.6이다. 이 수치는 첫 정답의 순위를 평가한 해당 벤치마크의 보고값이며, 일반적인 한국어 검색 정답률이나 2026년 신규 측정값이 아니다. 실제 도입 판단에서는 자체 질문·정답 문서로 측정한 결과가 더 직접적인 기준이 된다. 근거: intfloat 공식 Mr. TyDi 평가표.
4. E5·BGE-M3·Qwen3의 프롬프트와 입력 길이 확인하기
5. 검색 품질 점검표로 인코딩 오류와 문서 분할 문제 구분하기
짧은 정답 문장은 검색되는데 원문 전체를 넣으면 빠지는 경우, 먼저 입력 잘림을 의심할 수 있다. 특히 E5의 512토큰은 512글자가 아니다. 한국어 글자 수와 토큰 수는 일치하지 않으므로 모델의 토크나이저로 확인해야 한다.
앞의 예제에 다음 코드를 추가하면 접두어와 특수 토큰을 포함한 길이를 확인할 수 있다. 실제 문서 조각으로 texts를 바꿔 실행하고, 한도를 넘는 조각에 정답 문장이 포함돼 있는지 살핀다.
for (doc_id, _), text in zip(docs, texts):
ids = model.tokenizer(
"passage: " + text,
add_special_tokens=True,
truncation=False
)["input_ids"]
print(doc_id, "tokens:", len(ids),
"limit:", model.max_seq_length)
- 인코딩 설정: 질문·문서 모델 ID와 리비전이 같은지, 접두어가 빠지거나 두 번 들어가지 않았는지 확인한다.
- 벡터 일관성: 정규화 방식과 검색 인덱스의 거리 함수를 확인한다. 문서 프롬프트나 모델을 바꿨다면 기존 문서 벡터도 다시 만든다.
- 원문 추출: PDF 표의 열 순서, 제목 누락, 반복 머리말처럼 텍스트 추출 과정에서 생긴 오류를 확인한다.
- 문서 분할: 질문의 답과 적용 조건이 서로 다른 조각으로 갈라졌는지, 답이 토큰 제한 뒤에 위치하는지 확인한다.
- 검색 이후 처리: 권한·날짜 필터가 정답을 제외하는지, 같은 문서의 중복 조각이 상위 결과를 채우는지 확인한다.
실무적으로는 정답 문장만 넣은 실험과 원문 조각을 넣은 실험을 나누는 것이 진단에 유리하다. 정답 문장도 찾지 못하면 인코딩·모델 적합성을 먼저 살피고, 짧은 문장은 찾지만 긴 조각에서 실패하면 분할·문맥 희석·잘림을 우선 점검한다. 이 구분은 원인을 좁히는 방법이며, 한 번의 결과만으로 원인을 확정하는 규칙은 아니다.
5. 검색 품질 점검표로 인코딩 오류와 문서 분할 문제 구분하기
6. 한국어 AI 검색 개선은 자체 평가와 하이브리드 검색으로 이어진다
2026년 검색 시스템을 설계할 때 참고할 흐름은 긴 문맥 지원, 과제 지시문, 여러 검색 방식의 결합이다. BAAI의 BGE-M3는 밀집·희소·다중 벡터 검색을 지원하며, 공식 문서는 하이브리드 검색 뒤 재순위화를 권장한다. 다만 일반적인 Sentence Transformers 밀집 벡터 예제만 실행한다고 세 검색 방식이 모두 활성화되는 것은 아니다. 근거: BAAI BGE-M3 공식 검색 파이프라인 안내.
검색 품질 개선의 핵심은 실패 유형에 맞춰 다음 수단을 고르는 데 있다. 제품 코드·문서 번호처럼 정확한 문자열이 중요하면 키워드 검색을 함께 평가하고, 정답이 후보 목록에는 있지만 순위가 낮다면 재순위화를 검토한다. 정답이 후보 단계부터 빠진다면 문서 분할이나 후보 검색 범위를 먼저 개선해야 한다.
가장 먼저 할 일은 실제 질문과 정답 문서 ID를 묶어 작은 평가 세트를 만드는 것이다. 이후 인코딩 설정을 고정하고, 분할 방식과 검색 방식을 하나씩 바꾸면서 정답 순위와 처리 시간을 함께 기록한다. 이 순서가 갖춰져야 모델 교체의 효과와 단순 설정 수정의 효과를 구분할 수 있다.