Comment améliorer la qualité de la recherche pour le texte japonais avec Sudachi, Milvus/Zilliz et AWS Bedrock
Cet article a été initialement publié sur Qiita et est traduit et publié ici avec autorisation.
Introduction
Lorsque j’ai commencé à construire des systèmes de Retrieval-Augmented Generation (RAG) pour des utilisateurs japonais, je me suis heurté à un problème qui semblera probablement familier à quiconque a travaillé avec du texte japonais : la précision de la recherche n’est tout simplement pas aussi directe qu’en anglais. La langue présente des particularités — variations orthographiques, voyelles longues, écritures mixtes, différences de formes de surface — qui mettent régulièrement en échec les méthodes de recherche dense comme sparse lorsqu’elles sont utilisées isolément.
La recherche vectorielle dense est excellente pour comprendre le contexte et la similarité sémantique, mais elle s’effondre rapidement lorsqu’il faut des correspondances exactes — numéros de modèle, identifiants d’articles juridiques, codes internes ou entités très spécifiques comme « 金商法第37条 (article 37 de la loi sur les instruments financiers et les changes). » Les méthodes basées sur les mots-clés comme BM25 gèrent bien ces cas, mais elles ne suivent pas lorsque l’entrée contient de légères variations orthographiques (« サーバー » vs. « サーバ ») ou lorsque la même idée peut être exprimée sous plusieurs formes.
Pour contourner cela, j’ai construit un pipeline de recherche hybride qui combine les forces des deux approches. La solution utilise :
Sudachi : un tokenizer japonais qui fournit une normalisation et une tokenisation stable sur des textes incohérents.
Zilliz Cloud (le service Milvus entièrement managé) : une base de données vectorielle haute performance qui prend en charge les vecteurs denses, les vecteurs sparse et même la génération automatique de vecteurs BM25, ce qui rend la recherche hybride beaucoup plus facile à implémenter.
AWS Bedrock : utilisé pour générer des embeddings denses de haute qualité (Titan Embeddings v2), qui constituent le versant sémantique du pipeline de recherche.
Dans cet article, je vais expliquer comment j’ai assemblé ces éléments pour construire un système de recherche hybride japonais de haute précision. J’inclurai également un exemple pratique afin que vous puissiez essayer le même workflow vous-même et l’adapter à vos propres projets RAG.
Vue d’ensemble de l’architecture
Le système de recherche hybride présenté dans cet article repose sur une stack simple mais efficace. Chaque composant résout un problème spécifique qui se pose lorsqu’on traite du texte japonais, et ensemble ils forment un pipeline de recherche qui équilibre compréhension sémantique et précision des correspondances exactes. Voici le détail de la stack et pourquoi chaque partie compte.
SudachiPy — Tokenizer / analyse morphologique
Le texte japonais contient souvent des orthographes incohérentes, des irrégularités d’espacement et des variations de notation. Au lieu de m’appuyer sur une tokenisation naïve, j’utilise SudachiPy et son API normalized_form() pour tout nettoyer. Cela garantit que « サーバー » et « サーバ » correspondent au même token normalisé, et que les documents qui seraient autrement manqués apparaissent tout de même dans les résultats de recherche. Cette seule étape améliore considérablement le rappel dans l’ensemble.
Zilliz Cloud (Milvus managé) : une base de données vectorielle haute performance
Milvus est la base de données vectorielle open source la plus largement adoptée, avec plus de 43 000 étoiles GitHub et un vaste écosystème de contributeurs. Zilliz Cloud utilise le même noyau Milvus, mais supprime tout le travail opérationnel — configuration de cluster, autoscaling, optimisation des performances, sauvegardes, mises à niveau de versions — tout en exposant la même API Milvus. En pratique, cela signifie que je peux construire localement avec Milvus open source et déployer exactement le même code sur Zilliz Cloud lorsque j’ai besoin d’un environnement de niveau production.
C’est important, car de nombreux projets de recherche vectorielle se heurtent au même obstacle : le prototype fonctionne, mais son passage à l’échelle devient trop coûteux ou trop imprévisible. Les services de recherche PaaS entièrement gérés comme Azure AI Search ou les bases vectorielles propriétaires deviennent souvent des goulots d’étranglement en matière de coûts bien avant que les exigences de performance ne soient atteintes. Zilliz Cloud offre une voie plus efficace : un débit plus élevé, une latence plus faible et davantage de contrôle sur l’organisation des données, sans l’augmentation progressive des coûts qui apparaît généralement à grande échelle.
Dans cette architecture, Zilliz Cloud gère tout le stockage et la récupération des embeddings. Il prend en charge :
La recherche vectorielle dense pour la similarité sémantique
La recherche vectorielle sparse pour la récupération basée sur les mots-clés
À partir de Milvus v2.4, la base de données inclut également une fonctionnalité Function qui génère automatiquement des vecteurs sparse BM25 à partir de texte brut. C’est un grand avantage opérationnel. Je n’ai pas besoin de calculer BM25 côté client, de maintenir des pipelines d’indexation supplémentaires ni de synchroniser les métadonnées entre plusieurs systèmes. Tout — des embeddings denses à BM25 en passant par le classement hybride — réside dans une seule base de données, ce qui rend l’ensemble du flux de récupération simple, rapide et facile à maintenir.
AWS Bedrock (Titan Embeddings v2) : Le modèle d’embedding
Pour les embeddings vectoriels denses, j’utilise Titan Embeddings v2 d’AWS Bedrock. Il offre de bonnes performances dans plusieurs langues et gère le texte japonais de manière fiable, ce qui est important lorsque vous encodez du contenu mixte comme des requêtes courtes, de longs documents de politique, des descriptions de produits et du texte de type FAQ.
Reciprocal Rank Fusion (RRF) : La méthode de reclassement
La recherche hybride ne fonctionne que si vous pouvez combiner de manière pertinente les résultats de la recherche dense et de la recherche sparse, et les deux espaces de scores sont fondamentalement différents. RRF (Reciprocal Rank Fusion) résout cela proprement en fusionnant les résultats selon leur rang plutôt que selon les scores bruts. Il produit des résultats hybrides stables et faciles à interpréter, sans pondération réglée manuellement ni astuces de normalisation.
Tutoriel de recherche hybride adapté aux débutants
Maintenant que l’architecture est présentée, passons à quelque chose que vous pouvez réellement exécuter. J’ai préparé un dépôt GitHub avec tout le code et les données d’exemple dont vous avez besoin, donc la configuration est volontairement légère. Une fois que vous avez lancé un cluster Zilliz Cloud gratuit et ajouté votre clé d’API AWS Bedrock, vous pourrez tester trois modes de récupération côte à côte :
Recherche vectorielle dense
Recherche plein texte sparse (BM25)
Recherche hybride (fusion RRF)
L’ensemble du flux de travail fonctionne à faible coût : seuls les appels d’embedding Bedrock entraînent des frais.
Étape 1 : Cloner le dépôt depuis GitHub
Tout d’abord, clonez le dépôt :
git clone [https://github.com/Beginnersguide138/rag-with-sudachi.git](https://github.com/Beginnersguide138/rag-with-sudachi.git)
Accédez au répertoire du projet et configurez l’environnement Python :
cd rag-with-sudachi
uv sync # Install Python dependencies using uv
cp .env.example .env # Create an environment file based on the template
Étape 2 : Configurer Zilliz Cloud (offre gratuite)
Zilliz Cloud fonctionne sur tous les principaux fournisseurs de cloud — AWS, GCP et Azure. Vous pouvez vous inscrire directement sur le site web de Zilliz ou vous abonner via les marketplaces cloud correspondantes. Dans ce tutoriel, j’utiliserai le parcours AWS Marketplace, car c’est un moyen rapide de lancer un cluster Milvus entièrement géré sans toucher à l’infrastructure.
- Accédez à la page Zilliz Cloud sur AWS Marketplace et cliquez sur « Try for free. » Cela crée un cluster Milvus serverless sans frais :
Il ne sera jamais automatiquement converti en forfait payant
Présente certaines limitations (par exemple, des fonctionnalités de surveillance restreintes)
Le cluster est largement suffisant pour ce tutoriel de recherche hybride
2. Ouvrez la console Zilliz Cloud une fois l’abonnement terminé :
Créez une nouvelle Organization (simplement un conteneur logique pour vos projets).
Vous verrez un cluster serverless gratuit déjà provisionné.
Récupérez le Cluster Endpoint et l’API Key—vous en aurez besoin pour vous connecter depuis votre code.
Étape 3 : Configurer les variables d’environnement
Collez vos identifiants Zilliz et Bedrock dans le fichier .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
Note sur le code : les jetons à court terme Bedrock expirent toutes les 12 heures. C’est intentionnel : ils réduisent le rayon d’impact d’une exposition des identifiants et sont idéaux pour le développement local.
Étape 4 : Lancer le notebook ou exécuter le script
Ouvrez le dépôt dans VS Code. Le tutoriel principal se trouve dans :
notebooks/hybrid_search_with_bm25.ipynb
Si vous préférez un flux de travail Python pur plutôt que Jupyter Notebook, vous pouvez exécuter :
python run_hybrid_search.py
Les deux versions :
Construisent le schéma Milvus
Appliquent la normalisation Sudachi
Insèrent des vecteurs denses et clairsemés
Comparent les résultats de la recherche sémantique, par mots-clés et hybride
Détails techniques
1. La clé du traitement du japonais : la normalisation du texte avec Sudachi
Dans les systèmes de recherche pour le contenu en japonais, une grande partie de la précision dépend de la manière dont le texte est prétraité pendant l’indexation. Le texte extrait de PDF contient souvent des espacements incohérents, des variations orthographiques ou du bruit, ce qui entraîne fréquemment des correspondances manquées et un rappel plus faible.
Pour résoudre ce problème, cette implémentation utilise Sudachi’s normalization function. Ce processus standardise les tokens avant l’indexation afin que le système de recherche puisse traiter différentes orthographes et représentations comme équivalentes.
Code : wrapper de normalisation Sudachi
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()
Pourquoi la normalisation est importante
L’utilisation de normalized_form() unifie des variations telles que :
Katakana : 「サーバー」 ⇔ 「サーバ」
Notation numérique : 「第1条」 ⇔ 「第一条」
Bruit d’espacement dans les PDF : 「第 一 条」(不自然なスペース) ⇔ 「第一条」
Sans normalisation, ces variations entraînent :
Des correspondances BM25 manquées
Une tokenisation incorrecte des vecteurs clairsemés
Un rappel plus faible pour les requêtes juridiquement structurées
En normalisant à la fois les documents et les requêtes, le système hybride augmente considérablement la probabilité de correspondance.
2. Conception du schéma dans Zilliz Cloud (Milvus géré)
Milvus, le cœur de Zilliz Cloud, fournit une fonctionnalité Function (disponible à partir de la v2.4) qui génère automatiquement des vecteurs clairsemés basés sur BM25 dans la base de données. Cela évite d’avoir à précalculer les vecteurs BM25 côté client.
Définition du schéma
# 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)
Cette conception élimine la nécessité de transmettre explicitement des vecteurs clairsemés lors de l’insertion des données, réduisant considérablement la complexité opérationnelle.
En déplaçant la génération des vecteurs BM25 directement dans Milvus :
Le pipeline d’ingestion devient plus simple
Aucun calcul explicite de vecteur clairsemé n’est requis
Vous évitez de maintenir du code de prétraitement supplémentaire
La mise à l’échelle devient beaucoup plus facile
Cela réduit considérablement la charge opérationnelle.
Conception de l’index et stratégie d’optimisation
# 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"},
)
Index de vecteurs denses : HNSW
HNSW (Hierarchical Navigable Small World) est un algorithme ANN basé sur des graphes largement utilisé dans les bases de données vectorielles. Il offre :
Une récupération à grande vitesse
Un rappel élevé
De solides performances à grande échelle
COSINE est utilisé comme métrique de similarité parce que Titan Embeddings fonctionne dans un espace cosinus normalisé.
Index de vecteurs clairsemés : index inversé avec optimisation MaxScore
Les vecteurs clairsemés utilisent une structure traditionnelle d’index inversé. L’optimisation supplémentaire DAAT_MAXSCORE fournit :
Un traitement document par document pour une traversée efficace
Un élagage précoce des documents qui ne peuvent pas atteindre les scores top-k
Une réduction du calcul sans compromettre la précision
Cela conduit à une recherche BM25 nettement plus rapide.
3. Mise en œuvre de la recherche hybride avec RRF
Pour fusionner équitablement les résultats des recherches dense (sémantique) et clairsemée (par mots-clés), le système utilise Reciprocal Rank Fusion (RRF). RRF est robuste, facile à appliquer et ne nécessite aucun réglage ni normalisation entre les types de scores.
Code de recherche hybride
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\]
Comparaison des résultats de recherche réels
Le tutoriel évalue les résultats à l’aide de documents accessibles au public de l’Agence des services financiers du Japon. Le notebook permet une comparaison côte à côte de :
Recherche sémantique (vecteur dense)
Recherche en texte intégral (vecteur clairsemé)
Recherche hybride (dense + clairsemée via RRF)
Étude de cas : requête riche en mots-clés
Requête : “指定ADR機関が存在しない場合の苦情処理措置”
Remarque : la requête signifie « Une procédure de traitement des réclamations lorsqu’aucune organisation ADR désignée n’existe. »
Résultats :
Recherche par vecteurs denses : renvoie souvent des passages conceptuellement liés, mais peine à faire remonter les clauses réglementaires exactes.
Recherche sparse BM25 : identifie correctement les documents contenant des termes tels que « designated ADR organization » et « complaint-handling measures », en les classant en tête.
Recherche hybride : combine la capacité de correspondance précise de BM25 avec un contexte pertinent supplémentaire récupéré par la recherche dense.
Cela montre que la recherche uniquement dense risque de passer à côté de résultats critiques lorsque les utilisateurs effectuent des requêtes avec une terminologie spécialisée. La recherche hybride est essentielle pour la récupération de documents métier.
Résumé et applications
Dans cet article, nous avons parcouru une configuration pratique de recherche hybride qui associe la normalisation basée sur Sudachi à Zilliz Cloud (Milvus managé). L’objectif était simple : construire un pipeline de récupération performant sur le texte japonais, où la similarité sémantique et la correspondance exacte comptent toutes deux. En combinant des vecteurs denses, des vecteurs sparse BM25 et une fusion basée sur RRF, le système reste précis, facile à exploiter et adaptable aux charges de travail de production réelles.
Principaux avantages
Robuste face aux variations orthographiques : la normalisation de Sudachi atténue les différences orthographiques, les problèmes d’espacement et le bruit d’extraction PDF, évitant ainsi les échecs courants de rappel dans la recherche de texte japonais.
Faible charge opérationnelle : les fonctions Milvus gèrent la génération des vecteurs sparse BM25 à l’intérieur de la base de données. Aucun job de prétraitement supplémentaire, aucun service de recherche externe et aucune logique d’indexation dupliquée.
Grande précision globale : RRF combine la récupération dense et sparse sans réglage complexe des pondérations. Vous obtenez des résultats hybrides stables qui gèrent avec élégance à la fois les requêtes conceptuelles et les identifiants exacts.
Cas d’utilisation potentiels
Cette approche hybride est particulièrement efficace dans les scénarios où les utilisateurs peuvent passer de requêtes précises et structurées à un langage ouvert :
Recherche dans les politiques internes et les manuels : prend en charge les références exactes (par exemple, les numéros d’article) tout en gérant également les requêtes vagues ou exploratoires.
Recherche de produits e-commerce : permet la recherche précise par numéro de pièce tout en offrant des recommandations basées sur la similarité.
Bases de connaissances du support client : fait correspondre des termes structurés comme les codes d’erreur tout en interprétant les entrées utilisateur naturelles (« 画面が真っ黒です », « ログインできない »).
Le code source complet et le Jupyter Notebook utilisés dans cet article sont disponibles dans le dépôt suivant : GitHub: rag-with-sudachi
Le coût pour tout essayer est minime : seuls les appels d’embedding AWS Bedrock sont facturés. Le niveau serverless gratuit de Zilliz Cloud suffit pour exécuter l’ensemble du workflow.
Si vous explorez la recherche hybride pour des systèmes de production ou des prototypes internes, cet exemple constitue un excellent point de départ.
Continuer à lire

Vector Lakebase: End the AI Data Silo
Learn how Vector Lakebase unifies vector search, data lakes, and AI data operations so teams can serve RAG and agents without copy-and-sync pipelines.

My Wife Wanted Dior. I Spent $600 on Claude Code to Vibe-Code a 2M-Line Database Instead.
Write tests, not code reviews. How a test-first workflow with 6 parallel Claude Code sessions turns a 2M-line C++ codebase into a daily shipping pipeline.

Vector Databases vs. Key-Value Databases
Use a vector database for AI-powered similarity search; use a key-value database for high-throughput, low-latency simple data lookups.



