본문으로 건너뛰기
Kreath Archive
TechProjectsBooksAbout
TechProjectsBooksAbout

내비게이션

  • Tech
  • Projects
  • Books
  • About
  • Tags

카테고리

  • AI / ML
  • 웹 개발
  • 프로그래밍
  • 개발 도구

연결

  • GitHub
  • Email
  • RSS
© 2026 Kreath Archive. All rights reserved.Built with Next.js + MDX
홈TechProjectsBooksAbout
//
  1. 홈
  2. 테크
  3. 4장: 쿼리 이해와 확장
2026년 2월 9일·AI / ML·

4장: 쿼리 이해와 확장

쿼리 분류, 의도 인식, 엔티티 인식부터 LLM 기반 쿼리 확장, HyDE(가상 문서 생성), 다국어 처리까지 쿼리 이해 파이프라인을 다룹니다.

16분678자11개 섹션
searchai
공유
ai-search4 / 11
1234567891011
이전3장: 검색 품질 메트릭과 평가다음5장: 랭킹 모델 — Bi-encoder와 Cross-encoder

학습 목표

  • 쿼리 분류와 의도 인식의 원리를 이해합니다.
  • 동의어 확장과 쿼리 재작성 기법을 학습합니다.
  • LLM 기반 쿼리 확장의 구현 방법을 파악합니다.
  • HyDE(Hypothetical Document Embeddings)의 작동 원리를 이해합니다.
  • 다국어 환경에서의 쿼리 처리 전략을 학습합니다.

쿼리 이해의 중요성

사용자가 검색창에 입력하는 쿼리는 대부분 짧고 모호합니다. "파이썬 에러"라는 쿼리만으로는 어떤 종류의 에러인지, 해결법을 원하는지, 에러 원인을 알고 싶은지 파악하기 어렵습니다. 쿼리 이해(Query Understanding)는 이러한 짧은 입력에서 사용자의 실제 정보 요구를 파악하고, 검색 품질을 높이기 위해 쿼리를 보강하는 과정입니다.


쿼리 분류

**쿼리 분류(Query Classification)**는 검색어의 유형을 자동으로 판별하는 작업입니다. 쿼리 유형에 따라 검색 전략을 다르게 적용할 수 있습니다.

쿼리 유형

  • 정보형(Informational): "시맨틱 검색이란" — 정보를 찾는 쿼리
  • 탐색형(Navigational): "Elasticsearch 공식 문서" — 특정 페이지로 이동하려는 쿼리
  • 트랜잭션형(Transactional): "Elasticsearch 라이선스 구매" — 행동을 수행하려는 쿼리
query_classifier.py
python
from transformers import pipeline
 
classifier = pipeline(
    "zero-shot-classification",
    model="facebook/bart-large-mnli",
)
 
def classify_query(query: str) -> dict:
    """쿼리 유형 분류 (제로샷)"""
    labels = ["정보 검색", "페이지 탐색", "행동 수행"]
    result = classifier(query, candidate_labels=labels)
    return {
        "query": query,
        "type": result["labels"][0],
        "confidence": result["scores"][0],
    }
 
# 예시
print(classify_query("벡터 데이터베이스 비교"))
# -> type: "정보 검색", confidence: 0.87

의도 인식과 엔티티 인식

의도 인식

쿼리 분류보다 세밀한 수준에서 사용자의 구체적 의도를 파악합니다. 예를 들어 기술 블로그 검색에서 "Django ORM N+1"이라는 쿼리의 의도는 "문제 해결"일 가능성이 높고, "Django vs FastAPI"는 "비교 분석"이 의도일 수 있습니다.

intent_recognition.py
python
INTENT_TEMPLATES = {
    "문제 해결": ["에러", "오류", "안됨", "실패", "문제", "해결", "fix", "error", "bug"],
    "비교 분석": ["vs", "비교", "차이", "장단점", "대안"],
    "개념 학습": ["이란", "개념", "원리", "기초", "입문", "tutorial"],
    "구현 방법": ["방법", "구현", "만들기", "설정", "설치", "how to"],
    "성능 최적화": ["최적화", "성능", "속도", "튜닝", "optimization"],
}
 
def detect_intent(query: str) -> list[tuple[str, float]]:
    """규칙 기반 의도 인식"""
    query_lower = query.lower()
    scores = []
    for intent, keywords in INTENT_TEMPLATES.items():
        matches = sum(1 for kw in keywords if kw in query_lower)
        if matches > 0:
            scores.append((intent, matches / len(keywords)))
    return sorted(scores, key=lambda x: x[1], reverse=True)

엔티티 인식

쿼리에서 기술 용어, 프레임워크 이름, 프로그래밍 언어 등의 **엔티티(Entity)**를 추출합니다. 이는 필터링이나 패싯 검색에 활용됩니다.

tech_entity_recognition.py
python
TECH_ENTITIES = {
    "language": ["python", "javascript", "typescript", "java", "go", "rust"],
    "framework": ["django", "fastapi", "react", "next.js", "spring"],
    "database": ["postgresql", "mysql", "mongodb", "elasticsearch", "redis"],
    "concept": ["rest api", "graphql", "microservice", "docker", "kubernetes"],
}
 
def extract_tech_entities(query: str) -> dict[str, list[str]]:
    """쿼리에서 기술 엔티티 추출"""
    query_lower = query.lower()
    found = {}
    for category, entities in TECH_ENTITIES.items():
        matches = [e for e in entities if e in query_lower]
        if matches:
            found[category] = matches
    return found
 
# 예시
print(extract_tech_entities("FastAPI로 REST API 구현하기"))
# -> {"framework": ["fastapi"], "concept": ["rest api"]}

동의어 확장

**동의어 확장(Synonym Expansion)**은 검색어와 의미가 같거나 유사한 단어를 추가하여 검색 범위를 넓히는 기법입니다. 특히 한국어 환경에서는 영어 용어와 한국어 번역이 혼용되므로 동의어 사전이 필수적입니다.

synonym_expansion.py
python
SYNONYM_MAP = {
    "머신러닝": ["기계학습", "machine learning", "ML"],
    "딥러닝": ["심층학습", "deep learning", "DL"],
    "인공지능": ["AI", "artificial intelligence"],
    "시맨틱 검색": ["의미 검색", "semantic search"],
    "벡터 데이터베이스": ["벡터 DB", "vector database", "vector store"],
    "자연어 처리": ["NLP", "natural language processing"],
}
 
def expand_with_synonyms(query: str) -> list[str]:
    """동의어 확장된 쿼리 목록 반환"""
    expanded = [query]
    for term, synonyms in SYNONYM_MAP.items():
        if term in query:
            for syn in synonyms:
                expanded.append(query.replace(term, syn))
    return expanded
 
# "시맨틱 검색 구현" -> ["시맨틱 검색 구현", "의미 검색 구현", "semantic search 구현"]
Warning

동의어 확장은 검색 범위를 넓히지만, 과도한 확장은 정확도를 떨어뜨릴 수 있습니다. 동의어 사전은 도메인에 맞게 신중히 구축하고, 확장 결과의 품질을 정기적으로 검증해야 합니다.


LLM 기반 쿼리 확장

대규모 언어 모델(LLM)을 활용하면 규칙 기반 접근의 한계를 넘어 더 유연한 쿼리 확장이 가능합니다.

llm_query_expansion.py
python
from openai import OpenAI
 
client = OpenAI()
 
def expand_query_with_llm(query: str, num_expansions: int = 3) -> list[str]:
    """LLM 기반 쿼리 확장"""
    prompt = f"""다음 검색 쿼리와 의미가 같지만 다른 표현으로 된 검색어를 {num_expansions}개 생성하세요.
각 검색어는 한 줄에 하나씩 작성하세요.
 
원본 쿼리: {query}
 
확장 검색어:"""
 
    response = client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7,
        max_tokens=200,
    )
 
    expanded = response.choices[0].message.content.strip().split("\n")
    expanded = [q.strip().lstrip("0123456789.-) ") for q in expanded if q.strip()]
    return [query] + expanded[:num_expansions]
 
# "Elasticsearch 검색 속도 개선"
# -> ["Elasticsearch 검색 속도 개선",
#     "Elasticsearch 쿼리 성능 최적화",
#     "ES 검색 응답 시간 줄이기",
#     "Elasticsearch 검색 튜닝 방법"]

HyDE — 가상 문서 임베딩

**HyDE(Hypothetical Document Embeddings)**는 쿼리 확장의 혁신적인 접근법입니다. 사용자 쿼리에 대한 "가상의 답변 문서"를 LLM으로 생성한 뒤, 그 문서의 임베딩을 검색에 사용합니다.

HyDE의 작동 원리

핵심 아이디어는 간단합니다. 짧은 쿼리보다 긴 문서의 임베딩이 문서 인덱스의 임베딩과 더 유사한 분포를 가집니다. LLM이 생성한 가상 문서가 사실적으로 정확하지 않더라도, 그 임베딩은 관련 문서의 임베딩과 가까울 수 있습니다.

hyde_implementation.py
python
from openai import OpenAI
from sentence_transformers import SentenceTransformer
import numpy as np
 
llm_client = OpenAI()
embed_model = SentenceTransformer("intfloat/multilingual-e5-large")
 
def generate_hypothetical_document(query: str) -> str:
    """쿼리에 대한 가상 답변 문서 생성"""
    prompt = f"""다음 질문에 대해 기술 블로그 글처럼 상세한 답변을 작성하세요.
300자 내외로 작성하세요.
 
질문: {query}
 
답변:"""
 
    response = llm_client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[{"role": "user", "content": prompt}],
        temperature=0.7,
    )
    return response.choices[0].message.content
 
def hyde_search(query: str, index, k: int = 10):
    """HyDE 기반 검색"""
    # 1. 가상 문서 생성
    hypo_doc = generate_hypothetical_document(query)
 
    # 2. 가상 문서 임베딩
    hypo_embedding = embed_model.encode(
        f"passage: {hypo_doc}", normalize_embeddings=True
    )
 
    # 3. 원본 쿼리 임베딩
    query_embedding = embed_model.encode(
        f"query: {query}", normalize_embeddings=True
    )
 
    # 4. 두 임베딩의 평균 (선택적)
    combined = (hypo_embedding + query_embedding) / 2
    combined = combined / np.linalg.norm(combined)
 
    # 5. 벡터 검색
    return index.search(combined, k=k)
Tip

HyDE는 특히 짧고 모호한 쿼리에서 효과적입니다. 다만 LLM 호출 비용과 지연 시간이 추가되므로, 모든 쿼리에 적용하기보다는 검색 결과가 부족한 경우에 폴백(fallback)으로 사용하는 전략이 현실적입니다.


쿼리 재작성

**쿼리 재작성(Query Rewriting)**은 원본 쿼리를 검색에 더 적합한 형태로 변환하는 기법입니다. 오타 교정, 약어 확장, 불필요한 단어 제거 등이 포함됩니다.

query_rewriting.py
python
import re
 
class QueryRewriter:
    """규칙 기반 쿼리 재작성"""
 
    ABBREVIATIONS = {
        "ES": "Elasticsearch",
        "K8s": "Kubernetes",
        "DB": "Database",
        "ML": "Machine Learning",
        "DL": "Deep Learning",
        "NLP": "Natural Language Processing",
    }
 
    STOPWORDS = {"을", "를", "이", "가", "은", "는", "에서", "으로", "하는", "방법"}
 
    def rewrite(self, query: str) -> str:
        query = self._expand_abbreviations(query)
        query = self._remove_stopwords(query)
        query = self._normalize_whitespace(query)
        return query
 
    def _expand_abbreviations(self, query: str) -> str:
        for abbr, full in self.ABBREVIATIONS.items():
            pattern = rf"\b{re.escape(abbr)}\b"
            query = re.sub(pattern, f"{full}({abbr})", query, flags=re.IGNORECASE)
        return query
 
    def _remove_stopwords(self, query: str) -> str:
        tokens = query.split()
        return " ".join(t for t in tokens if t not in self.STOPWORDS)
 
    def _normalize_whitespace(self, query: str) -> str:
        return " ".join(query.split())
 
rewriter = QueryRewriter()
print(rewriter.rewrite("ES 검색 속도를 높이는 방법"))
# -> "Elasticsearch(ES) 검색 속도 높이는"

다국어 쿼리 처리

한국어 검색 시스템에서는 영어와 한국어가 혼용되는 경우가 빈번합니다. "React 컴포넌트 최적화"처럼 영어 기술 용어와 한국어 설명이 함께 사용됩니다.

다국어 처리 전략

  1. 다국어 임베딩 모델 사용: multilingual-e5-large, bge-m3 등 다국어를 지원하는 모델은 언어가 다르더라도 의미적으로 유사한 텍스트를 가까운 벡터로 매핑합니다.

  2. 교차 언어 확장: 한국어 쿼리에 영어 동의어를 추가하거나, 그 반대를 적용합니다.

  3. 언어 감지 후 분기: 쿼리의 주 언어를 감지하여 해당 언어에 최적화된 처리를 적용합니다.

multilingual_query.py
python
def cross_lingual_expand(query: str, synonyms: dict) -> list[str]:
    """교차 언어 쿼리 확장"""
    expanded = [query]
    for term, translations in synonyms.items():
        if term.lower() in query.lower():
            for trans in translations:
                expanded.append(query.lower().replace(term.lower(), trans))
    return expanded
 
synonyms = {
    "검색": ["search", "retrieval"],
    "임베딩": ["embedding", "벡터 표현"],
    "ranking": ["랭킹", "순위"],
}
 
results = cross_lingual_expand("시맨틱 검색 임베딩 방법", synonyms)
# ["시맨틱 검색 임베딩 방법", "시맨틱 search 임베딩 방법", ...]

개인화된 쿼리 확장

사용자의 이전 검색 이력과 관심사를 반영하여 쿼리를 확장할 수 있습니다. 같은 "최적화"라는 쿼리라도, 백엔드 개발자에게는 "데이터베이스 쿼리 최적화"로, 프론트엔드 개발자에게는 "React 렌더링 최적화"로 확장하는 것이 자연스럽습니다. 이 주제는 9장(검색 개인화)에서 자세히 다룹니다.


정리

이번 장에서는 검색의 시작점인 사용자 쿼리를 이해하고 보강하는 다양한 기법을 살펴보았습니다. 쿼리 분류와 의도 인식으로 사용자가 무엇을 원하는지 파악하고, 동의어 확장과 LLM 기반 확장으로 검색 범위를 넓히며, HyDE로 짧은 쿼리의 의미를 풍부하게 만드는 방법을 학습했습니다. 다국어 환경에서의 교차 언어 처리 전략도 확인했습니다.

다음 장에서는 검색 결과의 최종 순위를 결정하는 랭킹 모델을 깊이 있게 다룹니다. Bi-encoder와 Cross-encoder의 구조적 차이, Elastic Rerank의 DeBERTa v3 모델, 점수 퓨전(RRF), 학습 랭킹(LTR)까지 살펴보겠습니다.

이 글이 도움이 되셨나요?

관련 주제 더 보기

#search#ai

관련 글

AI / ML

5장: 랭킹 모델 — Bi-encoder와 Cross-encoder

Bi-encoder와 Cross-encoder의 구조적 차이, Elastic Rerank의 DeBERTa v3 모델, 점수 퓨전(RRF), 학습 랭킹(LTR)을 심층적으로 다룹니다.

2026년 2월 11일·14분
AI / ML

3장: 검색 품질 메트릭과 평가

Precision, Recall, NDCG, MRR, MAP 등 검색 품질 메트릭의 원리와 계산법, 오프라인/온라인 평가 방법론, A/B 테스트와 평가 데이터셋 구축을 다룹니다.

2026년 2월 7일·15분
AI / ML

6장: Elasticsearch AI 검색 통합

Elasticsearch의 kNN 검색, Inference API, semantic_text 필드, ELSER, Elastic Rerank, 하이브리드 검색(RRF)을 실습과 함께 다룹니다.

2026년 2월 13일·12분
이전 글3장: 검색 품질 메트릭과 평가
다음 글5장: 랭킹 모델 — Bi-encoder와 Cross-encoder

댓글

목차

약 16분 남음
  • 학습 목표
  • 쿼리 이해의 중요성
  • 쿼리 분류
    • 쿼리 유형
  • 의도 인식과 엔티티 인식
    • 의도 인식
    • 엔티티 인식
  • 동의어 확장
  • LLM 기반 쿼리 확장
  • HyDE — 가상 문서 임베딩
    • HyDE의 작동 원리
  • 쿼리 재작성
  • 다국어 쿼리 처리
    • 다국어 처리 전략
  • 개인화된 쿼리 확장
  • 정리