Sudachi, Milvus/Zilliz, AWS Bedrock으로 일본어 텍스트 검색 품질을 개선하는 방법
이 게시물은 원래 Qiita에 게시되었으며, 허가를 받아 번역 및 게시되었습니다.
소개
일본어 사용자를 위한 Retrieval-Augmented Generation (RAG) 시스템을 구축하기 시작했을 때, 일본어 텍스트를 다뤄본 사람이라면 누구나 익숙하게 느낄 만한 문제에 부딪혔습니다. 검색 정확도가 영어에서처럼 그렇게 단순하지 않다는 점입니다. 일본어에는 표기 변형, 장음, 혼용 문자, 표층형 차이 같은 특성이 있어, dense 검색과 sparse 검색 방법을 각각 단독으로 사용할 때 둘 다 쉽게 깨지곤 합니다.
Dense vector search는 문맥과 의미적 유사성을 이해하는 데 뛰어나지만, 정확한 일치가 필요할 때는 금세 한계를 드러냅니다. 예를 들어 모델 번호, 법률 조문 식별자, 내부 코드, 또는 “金商法第37条 (금융상품거래법 제37조)” 같은 매우 구체적인 엔터티가 그렇습니다. BM25 같은 키워드 기반 방법은 이런 경우를 잘 처리하지만, 입력에 사소한 철자 변형(“サーバー” vs. “サーバ”)이 포함되거나 같은 개념이 여러 형태로 표현될 수 있을 때는 따라가기 어렵습니다.
이를 해결하기 위해 저는 두 접근 방식의 장점을 결합한 hybrid search 파이프라인을 구축했습니다. 이 솔루션은 다음을 사용합니다.
Sudachi: 일관되지 않은 텍스트 전반에서 정규화와 안정적인 토큰화를 제공하는 일본어 tokenizer입니다.
Zilliz Cloud (완전 관리형 Milvus 서비스): dense vector, sparse vector, 그리고 자동 BM25 vector 생성까지 지원하는 고성능 vector database로, hybrid search 구현을 훨씬 쉽게 만들어 줍니다.
AWS Bedrock: 검색 파이프라인의 의미적 측면을 구성하는 고품질 dense embedding(Titan Embeddings v2)을 생성하는 데 사용됩니다.
이 게시물에서는 이러한 요소들을 어떻게 조합해 고정확도 일본어 hybrid search 시스템을 구축했는지 설명하겠습니다. 또한 직접 같은 workflow를 시도해 보고 여러분의 RAG 프로젝트에 맞게 조정할 수 있도록 실습 예제도 포함하겠습니다.
아키텍처 개요
이 글의 hybrid search 시스템은 단순하지만 효과적인 stack 위에 구축되어 있습니다. 각 구성 요소는 일본어 텍스트를 다룰 때 발생하는 특정 문제를 해결하며, 함께 의미적 이해와 정확 일치 정밀도의 균형을 이루는 검색 파이프라인을 형성합니다. 다음은 stack이 어떻게 구성되어 있고 각 부분이 왜 중요한지에 대한 설명입니다.
SudachiPy — Tokenizer / 형태소 분석
일본어 텍스트에는 종종 일관되지 않은 철자, 띄어쓰기 불규칙성, 표기 변형이 포함됩니다. 단순한 토큰화에 의존하는 대신, 저는 SudachiPy와 그 normalized_form() API를 사용해 이를 모두 정리합니다. 이를 통해 “サーバー”와 “サーバ”가 동일한 정규화된 token으로 매핑되며, 그렇지 않으면 누락되었을 문서들도 검색 결과에 나타나게 됩니다. 이 단일 단계만으로도 전반적인 recall이 크게 향상됩니다.
Zilliz Cloud (Managed Milvus): 고성능 Vector Database
Milvus는 43K+ GitHub stars와 대규모 contributor 생태계를 갖춘, 가장 널리 채택된 오픈소스 vector database입니다. Zilliz Cloud 는 동일한 Milvus core를 사용하지만, cluster 설정, autoscaling, performance tuning, backups, version upgrades 같은 운영 작업을 모두 제거하면서도 동일한 Milvus API를 제공합니다. 실제로 이는 제가 open-source Milvus로 로컬에서 빌드하고, production-grade 환경이 필요할 때 Zilliz Cloud에 정확히 같은 코드를 배포할 수 있다는 의미입니다.
많은 벡터 검색 프로젝트가 같은 벽에 부딪히기 때문에 이는 중요합니다: 프로토타입은 작동하지만, 확장하면 비용이 너무 비싸지거나 예측하기 어려워집니다. Azure AI Search 같은 완전 관리형 PaaS 검색 서비스나 독점 벡터 스토어는 성능 요구 사항을 충족하기 훨씬 전에 비용 병목이 되는 경우가 많습니다. Zilliz Cloud는 더 효율적인 경로를 제공합니다: 더 높은 처리량, 더 낮은 지연 시간, 데이터 레이아웃에 대한 더 많은 제어—규모가 커질 때 보통 나타나는 비용 증가 없이 말입니다.
이 아키텍처에서 Zilliz Cloud는 모든 임베딩 저장과 검색을 처리합니다. 지원하는 기능은 다음과 같습니다:
의미적 유사성을 위한 Dense vector search
키워드 기반 검색을 위한 Sparse vector search
Milvus v2.4부터 데이터베이스에는 원시 텍스트에서 BM25 sparse vector를 자동으로 생성하는 Function 기능도 포함되어 있습니다. 이는 운영 측면에서 큰 이점입니다. BM25를 클라이언트 측에서 계산하거나, 추가 인덱싱 파이프라인을 유지하거나, 여러 시스템 간에 메타데이터를 동기화할 필요가 없습니다. Dense embeddings부터 BM25, hybrid ranking까지 모든 것이 하나의 데이터베이스에 존재하므로 전체 검색 워크플로를 단순하고 빠르며 유지 관리하기 쉽게 유지할 수 있습니다.
AWS Bedrock (Titan Embeddings v2): 임베딩 모델
Dense vector embeddings에는 AWS Bedrock의 Titan Embeddings v2를 사용합니다. 여러 언어에서 성능이 좋고 일본어 텍스트도 안정적으로 처리하는데, 이는 짧은 쿼리, 긴 정책 문서, 제품 설명, FAQ 스타일 텍스트처럼 혼합 콘텐츠를 임베딩할 때 중요합니다.
Reciprocal Rank Fusion (RRF): 재랭킹 방법
Hybrid search는 dense search와 sparse search의 결과를 의미 있게 결합할 수 있을 때만 작동하며, 두 점수 공간은 근본적으로 다릅니다. RRF (Reciprocal Rank Fusion)는 원시 점수가 아니라 순위에 기반해 결과를 병합함으로써 이를 깔끔하게 해결합니다. 수동으로 조정한 가중치나 정규화 트릭 없이도 안정적이고 이해하기 쉬운 hybrid 결과를 생성합니다.
초보자 친화적인 Hybrid Search 튜토리얼
아키텍처 설명은 이 정도로 하고, 이제 실제로 실행할 수 있는 내용으로 들어가 보겠습니다. 필요한 모든 코드와 예제 데이터를 담은 GitHub repo를 준비해 두었으므로, 설정은 의도적으로 가볍게 구성했습니다. 무료 Zilliz Cloud 클러스터를 시작하고 AWS Bedrock API 키를 추가하면, 세 가지 검색 모드를 나란히 테스트할 수 있습니다:
Dense vector search
Sparse (BM25) full-text search
Hybrid search (RRF fusion)
전체 워크플로는 낮은 비용으로 실행됩니다—요금이 발생하는 것은 Bedrock 임베딩 호출뿐입니다.
Step 1: GitHub에서 Repository 클론하기
먼저 repository를 클론합니다:
git clone [https://github.com/Beginnersguide138/rag-with-sudachi.git](https://github.com/Beginnersguide138/rag-with-sudachi.git)
프로젝트 디렉터리로 이동하고 Python 환경을 설정합니다:
cd rag-with-sudachi
uv sync # uv를 사용하여 Python 의존성 설치
cp .env.example .env # 템플릿을 기반으로 환경 파일 생성
Step 2: Zilliz Cloud 설정하기 (Free Tier)
Zilliz Cloud는 AWS, GCP, Azure 등 모든 주요 클라우드 제공업체에서 실행됩니다. Zilliz website에서 직접 가입하거나 각 클라우드 marketplace를 통해 구독할 수 있습니다. 이 튜토리얼에서는 인프라를 건드리지 않고 완전 관리형 Milvus 클러스터를 빠르게 시작할 수 있는 방법이므로 AWS Marketplace 경로를 사용하겠습니다.
- AWS Marketplace의 Zilliz Cloud listing으로 이동해 “Try for free.”를 클릭합니다. 그러면 비용이 들지 않는 serverless Milvus cluster가 생성됩니다:
유료 플랜으로 자동 전환되지 않습니다
몇 가지 제한 사항이 있습니다(예: 제한된 모니터링 기능)
이 클러스터는 이 하이브리드 검색 튜토리얼에 충분하고도 남습니다
2. 구독이 완료된 후 Zilliz Cloud 콘솔을 엽니다:
새 Organization을 생성합니다(프로젝트를 위한 논리적 컨테이너일 뿐입니다).
이미 프로비저닝된 무료 serverless cluster가 표시됩니다.
Cluster Endpoint와 API Key를 가져옵니다. 코드에서 연결할 때 필요합니다.
3단계: 환경 변수 구성
Zilliz 및 Bedrock 자격 증명을 .env 파일에 붙여넣습니다:
# Zilliz Cloud connection
ZILLIZ_CLOUD_URI=https://your-cluster-id.serverless.region.cloud.zilliz.com
ZILLIZ_CLOUD_API_KEY=your-api-key-here
# AWS Bedrock short-term API key
AWS_BEARER_TOKEN_BEDROCK=bedrock-api-key-your-token-here
코드 참고: Bedrock 단기 토큰은 12시간마다 만료됩니다. 이는 의도된 동작입니다. 자격 증명 노출 시 피해 범위를 줄여 주며 로컬 개발에 이상적입니다.
4단계: Notebook을 실행하거나 Script를 실행합니다
VS Code에서 repository를 엽니다. 메인 튜토리얼은 다음 위치에 있습니다:
notebooks/hybrid_search_with_bm25.ipynb
Jupyter Notebook 대신 순수 Python 워크플로를 선호한다면 다음을 실행할 수 있습니다:
python run_hybrid_search.py
두 버전 모두:
Milvus schema를 빌드합니다
Sudachi 정규화를 적용합니다
dense 및 sparse vector를 삽입합니다
semantic, keyword, hybrid search 결과를 비교합니다
기술 세부 정보
1. 일본어 처리의 핵심: Sudachi를 사용한 텍스트 정규화
일본어 콘텐츠를 위한 검색 시스템에서는 정확도의 상당 부분이 인덱싱 중 텍스트가 어떻게 전처리되는지에 달려 있습니다. PDF에서 추출된 텍스트에는 일관되지 않은 공백, 표기 변형 또는 노이즈가 포함되는 경우가 많으며, 이는 종종 매칭 누락과 낮은 recall로 이어집니다.
이 문제를 해결하기 위해 이 구현은 Sudachi의 정규화 함수를 사용합니다. 이 과정은 인덱싱 전에 토큰을 표준화하여 검색 시스템이 서로 다른 철자와 표현을 동등한 것으로 처리할 수 있게 합니다.
코드: Sudachi 정규화 Wrapper
class SudachiAnalyzer:
def __init__(self):
self.tokenizer = dictionary.Dictionary(dict="core").create()
self.mode = tokenizer.Tokenizer.SplitMode.C
def analyze(self, text: str) -> str:
if not text:
return ""
tokens = self.tokenizer.tokenize(text, self.mode)
# Return as a space-separated string
return " ".join(\[t.normalized_form() for t in tokens if t.surface().strip()\])
analyzer = SudachiAnalyzer()
정규화가 중요한 이유
normalized_form()을 사용하면 다음과 같은 변형을 통합할 수 있습니다:
가타카나: 「サーバー」 ⇔ 「サーバ」
숫자 표기: 「第1条」 ⇔ 「第一条」
PDF 공백 노이즈: 「第 一 条」(不自然なスペース) ⇔ 「第一条」
정규화하지 않으면 이러한 변형은 다음으로 이어집니다:
BM25 매칭 누락
잘못된 sparse vector 토큰화
법적 구조를 가진 쿼리에 대한 낮은 recall
문서와 쿼리를 모두 정규화함으로써, 하이브리드 시스템은 매칭 확률을 크게 높입니다.
2. Zilliz Cloud(managed Milvus)의 Schema 설계
Zilliz Cloud의 핵심인 Milvus는 데이터베이스에서 BM25 기반 sparse vector를 자동으로 생성하는 Function 기능(v2.4 이상에서 사용 가능)을 제공합니다. 이를 통해 클라이언트 측에서 BM25 vector를 미리 계산할 필요가 없어집니다.
Schema 정의
# Create schema (auto ID disabled for explicit ID assignment)
schema = MilvusClient.create_schema(auto_id=False, enable_dynamic_field=True)
# Field definitions
schema.add_field(field_name="id", datatype=DataType.INT64, is_primary=True)
schema.add_field(
field_name="text",
datatype=DataType.VARCHAR,
max_length=65535,
enable_analyzer=True,
analyzer_params={
"tokenizer": "whitespace"
}, # Sudachi already provides whitespace-separated input
)
schema.add_field(
field_name="dense_vector", datatype=DataType.FLOAT_VECTOR, dim=1024
) # Titan Embeddings v2 outputs 1024 dimensions
schema.add_field(field_name="sparse_vector", datatype=DataType.SPARSE_FLOAT_VECTOR)
# Define the BM25 function
bm25_function = Function(
name="text_bm25_emb",
input_field_names=\["text"\],
output_field_names=\["sparse_vector"\],
function_type=FunctionType.BM25,
)
schema.add_function(bm25_function)
이 설계는 데이터 삽입 중에 희소 벡터를 명시적으로 전달할 필요를 없애 운영 복잡성을 크게 줄입니다.
BM25 벡터 생성을 Milvus 자체로 옮김으로써:
수집 파이프라인이 더 단순해집니다
명시적인 희소 벡터 계산이 필요하지 않습니다
추가 전처리 코드를 유지 관리하지 않아도 됩니다
확장이 훨씬 쉬워집니다
이는 운영 부담을 크게 줄입니다.
인덱스 설계 및 최적화 전략
# Index definitions
index_params = client.prepare_index_params()
index_params.add_index(
field_name="dense_vector", index_type="HNSW", metric_type="COSINE"
)
index_params.add_index(
field_name="sparse_vector",
index_type="SPARSE_INVERTED_INDEX",
metric_type="BM25",
params={"inverted_index_algo": "DAAT_MAXSCORE"},
)
밀집 벡터 인덱스: HNSW
HNSW (Hierarchical Navigable Small World)는 벡터 데이터베이스에서 널리 사용되는 그래프 기반 ANN 알고리즘입니다. 다음을 제공합니다:
고속 검색
높은 재현율
대규모 환경에서의 강력한 성능
Titan Embeddings는 정규화된 코사인 공간에서 작동하므로 COSINE이 유사도 지표로 사용됩니다.
희소 벡터 인덱스: MaxScore 최적화를 적용한 역색인
희소 벡터는 전통적인 역색인 구조를 사용합니다. 추가적인 DAAT_MAXSCORE 최적화는 다음을 제공합니다:
효율적인 순회를 위한 문서 단위 처리
상위 k 점수에 도달할 수 없는 문서의 조기 가지치기
정확도를 저하시키지 않는 계산량 감소
이는 BM25 검색을 훨씬 더 빠르게 만듭니다.
3. RRF를 사용한 하이브리드 검색 구현
밀집(의미 기반) 검색과 희소(키워드) 검색의 결과를 공정하게 병합하기 위해 시스템은 Reciprocal Rank Fusion (RRF)을 사용합니다. RRF는 견고하고 적용하기 쉬우며, 점수 유형 간 튜닝이나 정규화가 필요하지 않습니다.
하이브리드 검색 코드
from pymilvus import AnnSearchRequest, RRFRanker
def search_hybrid(client, collection_name, query_text, query_vector, top_k=5):
# Normalize and tokenize the query using Sudachi
query_processed = analyzer.analyze(query_text)
# Dense semantic search request
req_dense = AnnSearchRequest(
data=\[query_vector\],
anns_field="dense_vector",
param={"metric_type": "COSINE"},
limit=top_k * 2,
)
# Sparse BM25 keyword search request
req_sparse = AnnSearchRequest(
data=\[query_processed\],
anns_field="sparse_vector",
param={"metric_type": "BM25"},
limit=top_k * 2,
)
# Perform hybrid search using RRF
res = client.hybrid_search(
collection_name=collection_name,
reqs=\[req_dense, req_sparse\],
ranker=RRFRanker(), # Fuse rankings using RRF
limit=top_k,
output_fields=\["text", "original_text"\],
)
return res\[0\]
실제 검색 결과 비교
이 튜토리얼은 일본 금융청의 공개 문서를 사용하여 결과를 평가합니다. 노트북에서는 다음을 나란히 비교할 수 있습니다:
의미 기반 검색(밀집 벡터)
전체 텍스트 검색(희소 벡터)
하이브리드 검색(RRF를 통한 밀집 + 희소)
사례 연구: 키워드 비중이 높은 쿼리
쿼리: “指定ADR機関が存在しない場合の苦情処理措置”
참고: 이 쿼리는 “지정된 ADR 기관이 없을 때의 불만 처리 절차”를 의미합니다.
결과:
Dense 벡터 검색: 개념적으로 관련된 구절을 반환하는 경우가 많지만, 정확한 규제 조항을 표면화하는 데 어려움을 겪습니다.
Sparse BM25 검색: “designated ADR organization” 및 “complaint-handling measures”와 같은 용어가 포함된 문서를 올바르게 식별하고, 이를 가장 높은 순위로 매깁니다.
Hybrid 검색: BM25의 정밀한 매칭 능력과 dense 검색으로 검색된 추가 관련 컨텍스트를 결합합니다.
이는 사용자가 전문 용어로 쿼리할 때 dense-only 검색은 중요한 결과를 놓칠 위험이 있음을 보여줍니다. Hybrid 검색은 비즈니스 문서 검색에 필수적입니다.
요약 및 활용
이 글에서는 Sudachi 기반 정규화와 Zilliz Cloud (managed Milvus)를 결합한 실용적인 hybrid 검색 설정을 살펴보았습니다. 목표는 단순했습니다. 의미적 유사성과 정확한 매칭이 모두 중요한 일본어 텍스트에서 잘 작동하는 검색 파이프라인을 구축하는 것입니다. Dense 벡터, BM25 sparse 벡터, RRF 기반 융합을 결합함으로써 시스템은 정확하고, 실행하기 쉬우며, 실제 프로덕션 워크로드에 적응할 수 있습니다.
주요 이점
철자 변형에 강함: Sudachi의 정규화는 표기상의 차이, 띄어쓰기 문제, PDF 추출 노이즈를 완화하여 일본어 텍스트 검색에서 흔한 재현율 실패를 방지합니다.
낮은 운영 오버헤드: Milvus Functions는 데이터베이스 내부에서 BM25 sparse 벡터 생성을 처리합니다. 추가 전처리 작업, 외부 검색 서비스, 중복된 인덱싱 로직이 필요 없습니다.
높은 전반적 정확도: RRF는 복잡한 가중치 튜닝 없이 dense 및 sparse 검색을 결합합니다. 개념적 쿼리와 정확한 식별자를 모두 자연스럽게 처리하는 안정적인 hybrid 결과를 얻을 수 있습니다.
잠재적 사용 사례
이 hybrid 접근 방식은 사용자가 정밀하고 구조화된 쿼리와 개방형 언어 사이를 전환할 수 있는 시나리오에서 빛을 발합니다.
내부 정책 및 매뉴얼 검색: 모호하거나 탐색적인 쿼리도 처리하면서 정확한 참조(예: 조항 번호)를 지원합니다.
전자상거래 제품 검색: 유사도 기반 추천을 제공하면서 정확한 부품 번호 조회를 가능하게 합니다.
고객 지원 지식 베이스: 자연스러운 사용자 입력(“画面が真っ黒です”, “ログインできない”)을 해석하면서 오류 코드와 같은 구조화된 용어를 매칭합니다.
이 글에서 사용한 전체 소스 코드와 Jupyter Notebook은 다음 저장소에서 확인할 수 있습니다: GitHub: rag-with-sudachi
모든 것을 시험해 보는 비용은 매우 적습니다—AWS Bedrock 임베딩 호출에 대해서만 요금이 부과됩니다. Zilliz Cloud의 무료 serverless 티어만으로 전체 워크플로를 실행하기에 충분합니다.
프로덕션 시스템이나 내부 프로토타입을 위한 hybrid 검색을 검토하고 있다면, 이 예제는 훌륭한 출발점입니다.
계속 읽기

Introducing Zilliz CLI and Agent Skills for Zilliz Cloud
Manage your vector database from your terminal or AI coding agent. Zilliz CLI and Agent Skills work with Claude Code, Cursor, Codex, and Copilot.

Zilliz Cloud Now Available in AWS Europe (Ireland)
Zilliz Cloud launches in AWS eu-west-1 (Ireland) — bringing low-latency vector search, EU data residency, and full GDPR-ready infrastructure to European AI teams. Now live across 30 regions on five cloud providers.

Zilliz Named "Highest Performer" and "Easiest to Use" in G2's Summer 2025 Grid® Report for Vector Databases
Zilliz shines in G2's Summer 2025 Grid® Report as both "Highest Performer" and "Easiest to Use," solving the performance-usability dilemma.



