
Comment créer une base de connaissances efficace pour les modèles d’IA
seulement aussi fort que leur base de connaissances. Une base de connaissances précise et organisée améliore à la fois la vitesse et la précision des modèles, domaines dans lesquels les modèles actuels échouent souvent. En fait, une étude récente montre que d’importants Les chatbots IA ont tort pour presque une requête sur deux.
Dans cet article, j’expliquerai comment vous pouvez créer une base de connaissances fiable avec des étapes détaillées et des erreurs à éviter.
6 étapes pour construire une base de connaissances efficace

Adopter une approche systématique pour créer une base de connaissances vous aide à en créer une qui soit standardisée, évolutive et explicite. Tout nouveau développeur peut facilement ajouter ou mettre à jour la base de connaissances au fil du temps pour la maintenir à jour et fiable.
Pour vous assurer d’y parvenir, vous pouvez suivre ces six étapes chaque fois que vous commencez à créer une base de connaissances :
1. Collecter des données
Une des principales idées fausses concernant la collecte de données pour une base de connaissances est de supposer que plus c’est mieux. Cela vous fait tomber dans le problème classique du « déchets entrants, déchets sortants ».
Donnez la priorité à la valeur plutôt qu’au volume et collectez toutes les données pertinentes pour votre modèle. Cela pourrait prendre la forme de :
- Contenu factuel et didacticiel couvrant les faits et les procédures
- Contenu de résolution de problèmes sous forme de texte instructif ou de vidéos
- Données historiques montrant les problèmes passés ou le journal d’exécution
- Données en temps réel couvrant l’état du système en direct ou les flux d’actualités récents
- Données de domaine pour le modèle afin d’obtenir plus de contexte
Il est important de comprendre que votre système n’a pas besoin de toutes les informations. Par exemple, si vous créez un chatbot de support client, votre modèle peut n’avoir besoin que de contenu factuel et didacticiel expliquant la politique et les procédures de l’entreprise. Cela garantit que votre modèle n’invente pas de réponse invalide ou hors de portée et s’en tient à ce qui lui est fourni.
Conseil: Il existe une tendance croissante à alimenter les données générées par l’IA tout en construisant une base de connaissances sur de nouveaux modèles d’IA. Je pense que cette pratique est un peu une arme à double tranchant. Il offre de la vitesse, mais vous devez vérifier la fiabilité et le fluff de la sortie. Optimisez toujours le contenu pour des réponses claires et vérifiez le résultat avant de l’ajouter à la base de connaissances.
2. Nettoyer et segmenter les données en morceaux
Une fois que les données brutes sont prêtes, vous pouvez d’abord les nettoyer. Le processus de nettoyage comprend généralement :
- Suppression du contenu en double et obsolète
- Suppression des détails non pertinents tels que les en-têtes, les pieds de page et les numéros de page
- Standardiser le contenu, tant au niveau du format que du contenu (terminologie cohérente)
Ces données nettoyées sont ensuite divisées en morceaux logiques, où chaque morceau contient une idée ou un sujet clair.
Chaque morceau se voit également attribuer des métadonnées qui fournissent un contexte rapide sur son contenu. Ces métadonnées aident les modèles d’IA à parcourir les bases de connaissances plus rapidement et à atteindre rapidement les éléments contenant des détails pertinents.
Vous pouvez également définir un accès basé sur les rôles par blocs pour garantir que les rôles ont accès aux informations de ce bloc. Même si de nombreux rôles peuvent avoir accès à un modèle, tout le monde ne peut pas accéder à toutes les données. Le chunking est l’endroit où vous pouvez définir la sécurité et le contrôle d’accès au sein du modèle.
Conseil: Une bonne pratique que je suis toujours consiste à regrouper les données en fonction des requêtes des utilisateurs plutôt que de la structure du document. Par exemple, vous disposez d’un document sur la gestion des connexions et des accès. Vous pouvez le regrouper en fonction de questions courantes des utilisateurs telles que « Comment changer le mot de passe ? », « Quelle est la politique de mot de passe ? », etc. Vous pouvez ensuite valider ces fragments en les testant par rapport à des requêtes réelles. Un ensemble sûr peut comprendre 10 à 12 questions.
3. Organiser et indexer les données
Les morceaux de texte sont convertis en nombres appelés vecteurs à l’aide d’un modèle d’intégration comme OpenAI v3-Large, BGE-M3, etc.
Les modèles d’IA peuvent parcourir des vecteurs plus rapidement qu’un énorme bloc de texte. Après la vectorisation, les métadonnées attachées au chunk sont ensuite attachées au vecteur. Le morceau final ressemblera à ceci :
[ Vector (numbers) ] + [ Original text ] + [ Metadata ]
4. Choisissez une plateforme pour stocker les données
Vous pouvez stocker cette sortie vectorielle dans une base de données vectorielle telle que Pinecone, Milvus ou Weaviate pour la récupération. Vous pouvez télécharger les données vectorielles en écrivant un simple code python.
import math
import time
import json
from dataclasses import dataclass, field
from typing import Any
import numpy as np
# Vector Normalization + Metadata
def normalize_l2(vector: list[float]) -> list[float]:
"""
Return an L2-normalized copy of `vec`.
Many vector stores use dot-product similarity. If you normalize vectors to
unit length, dot-product becomes equivalent to cosine similarity.
"""
arr = np.array(vector, dtype=np.float32)
norm = np.linalg.norm(arr)
if norm == 0:
return vector
return (arr / norm).tolist()
def prepare_record(
doc_id: str,
embedding: list[float],
text: str,
source: str,
extra_metadata: dict[str, Any] | None = None,
) -> dict:
"""
Prepare a single record for vector DB upsert.
Metadata serves two purposes:
- Filtering: narrow down search to a subset
"""
metadata = {
"source": source,
"text_preview": text[:500],
"char_count": len(text),
}
if extra_metadata:
metadata.update(extra_metadata)
return {
"id": doc_id,
"values": normalize_l2(embedding),
"metadata": metadata,
}
# Vector Quantization
# Scalar Quantization / SQ
def scalar_quantization(input_vec) -> dict:
"""
This funtion demonstrates
how to compress float32 input_vec to uint8
"""
input_arr = np.array(input_vec, dtype=np.float32)
min, max = input_arr.min(), input_arr.max()
range = (max - min)
if range == 0:
quantized = np.zeros_like(arr, dtype=np.uint8)
else:
quantized = ((input_arr - min) / range * 255).astype(np.uint8)
return {
"quantized": quantized.tolist(),
"min": float(min),
"max": float(max),
}
def scalar_dequantization(record: dict) -> list[float]:
"""
You can Reconstruct the original vector
by approximate float32 vector from uint8.
"""
arr = np.array(record["quantized"], dtype=np.float32)
return (arr / 255 * (record["max"] - record["min"]) + record["min"]).tolist()
# Product Quantization / PQ
def train_product_quantizer( vectors, num_subvectors: int = 8, num_centroids: int = 256, max_iterations: int = 20) -> list:
"""
This function demonstrates
split vector into subvectors, cluster each independently
"""
from sklearn.cluster import KMeans
dim = vectors.shape[1]
assert dim % num_subvectors == 0, "dim must be divisible by num_subvectors"
sub_dim = dim // num_subvectors
codebooks = []
for i in range(num_subvectors):
sub_vectors = vectors[:, i * sub_dim : (i + 1) * sub_dim]
kmeans = KMeans(n_clusters=num_centroids, max_iter=max_iterations, n_init=1)
kmeans.fit(sub_vectors)
codebooks.append(kmeans.cluster_centers_)
return codebooks
def pq_encode(vector: np.ndarray, codebooks: list[np.ndarray]) -> list[int]:
"""
Encode a single vector into PQ codes (one uint8 per subvector)
"""
num_subvectors = len(codebooks)
sub_dim = len(vector) // num_subvectors
codes = []
for i, codebook in enumerate(codebooks):
sub_vec = vector[i * sub_dim : (i + 1) * sub_dim]
distances = np.linalg.norm(codebook - sub_vec, axis=1)
codes.append(int(np.argmin(distances)))
return codes
def pq_decode(codes: list[int], codebooks: list[np.ndarray]) -> np.ndarray:
"""
Reconstruct approximate vector from PQ codes
"""
return np.concatenate(
[codebook[code] for code, codebook in zip(codes, codebooks)]
)
Conseil: Pour augmenter la vitesse de téléchargement, je suggère d’utiliser l’option d’insertion par lots. Vous pouvez également normaliser les vecteurs (les rendre tous de mêmes tailles) pendant la phase de téléchargement. Après normalisation, quantifiez-le (compressez-le) pour optimiser le stockage. Cette étape supplémentaire de normalisation et de quantification accélère la récupération ultérieure.
5. Optimiser la récupération
Pour activer la récupération à partir de la base de données vectorielles, vous pouvez utiliser des frameworks d’orchestration tels que LlamaIndex et LangChain.
LlamaIndex peut parcourir la base de données vectorielle plus rapidement et accéder au morceau exact où se trouve le contenu lié à la requête de l’utilisateur.
LangChain récupère ensuite les données du morceau et les transforme selon la requête de l’utilisateur. Par exemple, résumer un texte ou rédiger un e-mail à partir de celui-ci.
"""
Hybrid Retrieval: Take benefits from both keyword search and vector similarity
Where each approach shines:
- Keywords: looks for exact matches, but will miss searches with synonym
- Embeddings: has advantage of capturing the meaning, but there is possibility of missing exact keyword
Hybrid is a combination of both to get the best of each.
"""
import math
from collections import defaultdict
from dataclasses import dataclass
import numpy as np
@dataclass
class Document:
id: str
text: str
embedding: list[float]
class BestMatching25Index:
def __init__(self, k1: float = 1.5, b: float = 0.75):
# Here k1 is the term frequency saturation limit
# and b is length of normalization
self.k1 = k1
self.b = b
self.doc_lengths: dict[str, int] = {}
self.avg_doc_length: float = 0
self.doc_freqs: dict[str, int] = {}
self.term_freqs: dict[str, dict[str, int]] = {}
self.corpus_size: int = 0
def _tokenize(self, text: str) -> list[str]:
return text.lower().split()
def index(self, documents: list[Document]) -> None:
self.corpus_size = len(documents)
for doc in documents:
tokens = self._tokenize(doc.text)
self.doc_lengths[doc.id] = len(tokens)
self.term_freqs[doc.id] = {}
seen_terms: set[str] = set()
for token in tokens:
self.term_freqs[doc.id][token] = self.term_freqs[doc.id].get(token, 0) + 1
if token not in seen_terms:
self.doc_freqs[token] = self.doc_freqs.get(token, 0) + 1
seen_terms.add(token)
self.avg_doc_length = sum(self.doc_lengths.values()) / self.corpus_size
def score(self, query: str, doc_id: str) -> float:
query_terms = self._tokenize(query)
doc_len = self.doc_lengths[doc_id]
score = 0.0
for term in query_terms:
if term not in self.doc_freqs or term not in self.term_freqs.get(doc_id, {}):
continue
tf = self.term_freqs[doc_id][term]
df = self.doc_freqs[term]
idf = math.log((self.corpus_size - df + 0.5) / (df + 0.5) + 1)
tf_norm = (tf * (self.k1 + 1)) / (
tf + self.k1 * (1 - self.b + self.b * doc_len / self.avg_doc_length)
)
score += idf * tf_norm
return score
def search(self, query: str, top_k: int = 10) -> list[tuple[str, float]]:
scores = [
(doc_id, self.score(query, doc_id))
for doc_id in self.doc_lengths
]
scores.sort(key=lambda x: x[1], reverse=True)
return scores[:top_k]
class VectorIndex:
"""This class implements the smart search using the hybrid search.
The index function normalize and stores the document
search implements a cosine similarity search
hybrid_search_weighted merges BM25 index and vector index using weighted average
Reciprocal_rank_fusion Combines the results in an efficient way
"""
def __init__(self):
self.documents: dict[str, np.ndarray] = {}
def index(self, documents: list[Document]) -> None:
for doc in documents:
arr = np.array(doc.embedding, dtype=np.float32)
norm = np.linalg.norm(arr)
self.documents[doc.id] = arr / norm if norm > 0 else arr
def search(self, query_embedding: list[float], top_k: int = 10) -> list[tuple[str, float]]:
q = np.array(query_embedding, dtype=np.float32)
q = q / np.linalg.norm(q)
scores = [
(doc_id, float(np.dot(q, emb)))
for doc_id, emb in self.documents.items()
]
scores.sort(key=lambda x: x[1], reverse=True)
return scores[:top_k]
def hybrid_search_weighted(
query: str,
query_embedding: list[float],
bm25_index: BestMatching25Index,
vector_index: VectorIndex,
alpha: float = 0.5,
top_k: int = 10,
) -> list[dict]:
"""Combine keyword and vector scores with a tunable weight.
alpha = 1.0 → pure vector search
alpha = 0.0 → pure keyword search
alpha = 0.5 → equal weight (good starting point)
"""
keyword_results = bm25_index.search(query, top_k=top_k * 2)
vector_results = vector_index.search(query_embedding, top_k=top_k * 2)
# Normalize (min-max) each score list to [0, 1]
def normalize_scores(results: list[tuple[str, float]]) -> dict[str, float]:
if not results:
return {}
scores = [s for _, s in results]
min_s, max_s = min(scores), max(scores)
rng = max_s - min_s
if rng == 0:
return {doc_id: 1.0 for doc_id, _ in results}
return {doc_id: (s - min_s) / rng for doc_id, s in results}
keyword_scores = normalize_scores(keyword_results)
vector_scores = normalize_scores(vector_results)
# Merge
all_doc_ids = set(keyword_scores) | set(vector_scores)
combined = []
for doc_id in all_doc_ids:
ks = keyword_scores.get(doc_id, 0.0)
vs = vector_scores.get(doc_id, 0.0)
combined.append({
"id": doc_id,
"score": alpha * vs + (1 - alpha) * ks,
"keyword_score": ks,
"vector_score": vs,
})
combined.sort(key=lambda x: x["score"], reverse=True)
return combined[:top_k]
def reciprocal_rank_fusion(
*ranked_lists: list[tuple[str, float]],
k: int = 60,
top_n: int = 10,
) -> list[dict]:
"""
Merge multiple ranked lists, uses RRF (Reciprocal Rank Fusion)
RRF score = sum over all lists of: 1 / (k + rank)
Why RRF over weighted combination?
- No score normalization needed (works on ranks, not raw scores)
- No alpha tuning needed
- Robust across different score distributions
- Used by Elasticsearch, Pinecone, Weaviate under the hood
"""
rrf_scores: dict[str, float] = defaultdict(float)
doc_details: dict[str, dict] = {}
for list_idx, ranked_list in enumerate(ranked_lists):
for rank, (doc_id, raw_score) in enumerate(ranked_list, start=1):
rrf_scores[doc_id] += 1.0 / (k + rank)
if doc_id not in doc_details:
doc_details[doc_id] = {}
doc_details[doc_id][f"list_{list_idx}_rank"] = rank
doc_details[doc_id][f"list_{list_idx}_score"] = raw_score
results = []
for doc_id, rrf_score in rrf_scores.items():
results.append({
"id": doc_id,
"rrf_score": round(rrf_score, 6),
**doc_details[doc_id],
})
results.sort(key=lambda x: x["rrf_score"], reverse=True)
return results[:top_n]
def hybrid_search_rrf(
query: str,
query_embedding: list[float],
bm25_index: BestMatching25Index,
vector_index: VectorIndex,
top_k: int = 10,
) -> list[dict]:
keyword_results = bm25_index.search(query, top_k=top_k * 2)
vector_results = vector_index.search(query_embedding, top_k=top_k * 2)
return reciprocal_rank_fusion(keyword_results, vector_results, top_n=top_k)
Conseil: Je recommande la récupération hybride basée à la fois sur des mots-clés et des intégrations pour une récupération rapide. La récupération de mots-clés est idéale pour les termes exacts (« Politique de mot de passe »). Les intégrations sont meilleures pour les correspondances conceptuelles ou basées sur le sens. LlamaIndex est excellent en matière de récupération hybride, où il peut rechercher des termes exacts et un contexte autour de la question.
6. Établir une routine de mise à jour et d’actualisation automatique
La dernière étape consiste à vous assurer que la base de connaissances est toujours à jour. Pour cela, vous pouvez mettre en œuvre l’oubli sélectif. Il s’agit du processus d’écrasement ou de suppression de données obsolètes et redondantes pour maintenir la précision du modèle.
Comment trouver les données à supprimer ? Il existe des plateformes d’évaluation et d’observabilité pour vous aider. Vous pouvez planifier des règles/requêtes de test dans le framework DeepEval qui vérifient régulièrement si votre modèle d’IA est précis. Si les réponses sont incorrectes, la plateforme TruLens vous aide à atteindre la partie exacte d’où cette réponse a été sélectionnée.
"""
Knowledge Base Quality Monitoring
Knowledge base health with the help of automated checks:
1. Retrieval quality — is it finding the right documents?
2. Freshness detection — Are documents stale or embeddings drifting?
3. Unified pipeline — Scheduled monitoring with alerts
"""
import time
import json
import logging
from datetime import datetime, timedelta
from dataclasses import dataclass, field
from typing import Any, Callable
import numpy as np
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger("kb_monitor")
def setup_deepeval_metrics():
"""Define retrieval quality metrics using DeepEval.
DeepEval provides LLM-evaluated metrics — it uses a judge LLM to score
whether retrieved context actually helps answer the question.
"""
from deepeval.metrics import (
AnswerRelevancyMetric,
FaithfulnessMetric,
ContextualPrecisionMetric,
ContextualRecallMetric,
)
from deepeval.test_case import LLMTestCase
metrics = {
# Does the answer address the question?
"relevancy": AnswerRelevancyMetric(threshold=0.7),
# Is the answer grounded in the retrieved context (no hallucination)?
"faithfulness": FaithfulnessMetric(threshold=0.7),
# Are the top-ranked retrieved docs actually relevant?
"context_precision": ContextualPrecisionMetric(threshold=0.7),
# Did we retrieve all the docs needed to answer?
"context_recall": ContextualRecallMetric(threshold=0.7),
}
return metrics, LLMTestCase
def evaluate_retrieval_quality(
rag_pipeline: Callable,
test_cases: list[dict],
) -> list[dict]:
"""Run a set of test queries through your RAG pipeline and score them.
Each test case should have:
- query: the user question
- expected_answer: ground truth answer (for recall/relevancy)
"""
from deepeval import evaluate
from deepeval.test_case import LLMTestCase
from deepeval.metrics import (
AnswerRelevancyMetric,
FaithfulnessMetric,
ContextualPrecisionMetric,
ContextualRecallMetric,
)
results = []
for tc in test_cases:
# Run your actual RAG pipeline
response = rag_pipeline(tc["query"])
test_case = LLMTestCase(
input=tc["query"],
actual_output=response["answer"],
expected_output=tc["expected_answer"],
retrieval_context=response["retrieved_contexts"],
)
metrics = [
AnswerRelevancyMetric(threshold=0.7),
FaithfulnessMetric(threshold=0.7),
ContextualPrecisionMetric(threshold=0.7),
ContextualRecallMetric(threshold=0.7),
]
for metric in metrics:
metric.measure(test_case)
results.append({
"query": tc["query"],
"scores": {m.__class__.__name__: m.score for m in metrics},
"passed": all(m.is_successful() for m in metrics),
})
return results
def setup_trulens_monitoring(rag_pipeline: Callable, app_name: str = "my_kb"):
"""Wrap your RAG pipeline with TruLens for continuous feedback logging.
TruLens records every query + response + retrieved context, then
runs feedback functions asynchronously to score each interaction.
"""
from trulens.core import TruSession, Feedback, Select
from trulens.providers.openai import OpenAI as TruLensOpenAI
from trulens.apps.custom import TruCustomApp, instrument
session = TruSession()
# Feedback provider (uses an LLM to judge quality)
provider = TruLensOpenAI()
feedbacks = [
# Is the response relevant to the query?
Feedback(provider.relevance)
.on_input()
.on_output(),
# Is the response grounded in retrieved context?
Feedback(provider.groundedness_measure_with_cot_reasons)
.on(Select.RecordCalls.retrieve.rets)
.on_output(),
# Is the retrieved context relevant to the query?
Feedback(provider.context_relevance)
.on_input()
.on(Select.RecordCalls.retrieve.rets),
]
# Wrap your pipeline — every call is now logged and scored
@instrument
class InstrumentedRAG:
def __init__(self, pipeline):
self._pipeline = pipeline
@instrument
def retrieve(self, query: str) -> list[str]:
result = self._pipeline(query)
return result["retrieved_contexts"]
@instrument
def query(self, query: str) -> str:
result = self._pipeline(query)
return result["answer"]
instrumented = InstrumentedRAG(rag_pipeline)
tru_app = TruCustomApp(
instrumented,
app_name=app_name,
feedbacks=feedbacks,
)
return tru_app, session
def get_trulens_dashboard_url(session) -> str:
"""Launch the TruLens dashboard to visualize quality over time."""
session.run_dashboard(port=8501)
return "http://localhost:8501"
@dataclass
class DocumentFreshness:
doc_id: str
last_updated: datetime
last_embedded: datetime
source_hash: str # hash of source content at embedding time
class FreshnessMonitor:
"""Detect stale documents and embedding drift."""
def __init__(self, staleness_threshold_days: int = 30):
self.threshold = timedelta(days=staleness_threshold_days)
self.freshness_records: dict[str, DocumentFreshness] = {}
def register(self, doc_id: str, source_hash: str) -> None:
now = datetime.utcnow()
self.freshness_records[doc_id] = DocumentFreshness(
doc_id=doc_id,
last_updated=now,
last_embedded=now,
source_hash=source_hash,
)
def check_staleness(self) -> dict:
"""Find documents that haven't been re-embedded recently."""
now = datetime.utcnow()
stale, fresh = [], []
for doc_id, record in self.freshness_records.items():
age = now - record.last_embedded
if age > self.threshold:
stale.append({"id": doc_id, "days_stale": age.days})
else:
fresh.append(doc_id)
return {
"total": len(self.freshness_records),
"fresh": len(fresh),
"stale": len(stale),
"stale_documents": stale,
}
def check_content_drift(
self, doc_id: str, current_source_hash: str
) -> bool:
"""Check if source content changed since last embedding."""
record = self.freshness_records.get(doc_id)
if not record:
return True # unknown doc, treat as drifted
return record.source_hash != current_source_hash
def detect_embedding_drift(
old_embeddings: dict[str, list[float]],
new_embeddings: dict[str, list[float]],
drift_threshold: float = 0.1,
) -> dict:
"""Compare old vs new embeddings for the same documents.
If your embedding model gets updated (or you switch models),
existing vectors may no longer be compatible. This detects that.
"""
drifted = []
common_ids = set(old_embeddings) & set(new_embeddings)
for doc_id in common_ids:
old = np.array(old_embeddings[doc_id])
new = np.array(new_embeddings[doc_id])
# cosine distance: 0 = identical, 2 = opposite
cos_sim = np.dot(old, new) / (np.linalg.norm(old) * np.linalg.norm(new))
cos_dist = 1 - cos_sim
if cos_dist > drift_threshold:
drifted.append({
"id": doc_id,
"cosine_distance": round(float(cos_dist), 4),
})
return {
"documents_compared": len(common_ids),
"drifted": len(drifted),
"drift_threshold": drift_threshold,
"drifted_documents": sorted(drifted, key=lambda x: x["cosine_distance"], reverse=True),
}
L’utilisation de DeepEval en combinaison avec TruLens automatise les tests périodiques de votre base de connaissances.
Principaux défis dans la construction d’une base de connaissances (+ solutions)
Voici les problèmes courants que j’ai rencontrés avec la base de connaissances :
1. Augmentation des erreurs de qualité des données
Les modèles d’IA construits au fil des années, même par des entreprises réputées et dotées d’équipes solides, sont hallucinants. Le célèbre Accident du chatbot d’Air Canada est un exemple où le modèle promettait un remboursement à un client contre une politique qui n’a jamais existé.
Même si tous les ingénieurs tentent de mettre du contenu pertinent dans la base de connaissances, le résultat présente encore des problèmes. D’après mon expérience, le manque d’expertise dans le domaine crée des erreurs dans l’identification de ce qui est pertinent. Retirez le chapeau technique et portez une casquette de domaine pour identifier les informations obsolètes, contradictoires et non pertinentes dans votre base de connaissances.
2. Lenteur à la récupération
Un modèle d’IA fournissant simplement la bonne réponse ne suffit pas. Les utilisateurs détestent le chargement ou le décalage et veulent des réponses en un clin d’œil, au moins depuis une machine.
Les développeurs restent souvent bloqués sur les fonctionnalités et ne priorisent pas la partie optimisation, qui est totalement non négociable. Utilisez les conseils suivants pour résoudre le problème de lenteur courant :
- Suivez les index HNSW (Hierarchical Navigable Small World) ou FIV au lieu des index plats, car ils regroupent les sujets pertinents pour une récupération rapide.
- Effectuez une quantification (en réduisant les vecteurs convertis des requêtes afin qu’ils occupent moins de mémoire) ou une division récursive des caractères (en les divisant en extraits) des requêtes afin qu’elles occupent moins de mémoire
- Conservez votre base de données et votre service d’IA dans la même région cloud pour un accès plus rapide.
3. Mauvaise évolutivité
Pour accélérer la mise en œuvre, les développeurs prennent souvent de mauvaises décisions de conception qui affectent l’évolutivité à long terme. L’un de ces problèmes consiste à suivre une architecture monolithique dans laquelle tout le stockage des données et le traitement des requêtes s’effectuent dans un seul cluster étroitement couplé. À mesure que l’utilisation du modèle augmente, l’utilisation du CPU/RAM augmente à travers le monde. entier cluster pour chaque requête. Je suggère le partitionnement horizontal (divisant les données en plusieurs petits serveurs) pour gérer efficacement l’évolutivité.
Un autre problème est l’augmentation des coûts avec l’échelle, ce qui se produit généralement si vous ne quantifiez ou ne compressez pas les vecteurs pour optimiser le stockage. Les développeurs manquent l’étape de quantification pour accéder plus rapidement au modèle. L’inconvénient n’est pas visible au départ, mais bientôt la lenteur et l’augmentation des factures de cloud montrent l’écart.
Une base de connaissances n’est pas un vidage de données mais un actif organisé
Construire une base de connaissances n’est pas un projet ponctuel. C’est un actif évolutif qui nécessite une optimisation régulière. La structure que vous créez aujourd’hui révélera des lacunes demain. Chaque requête échouée est un retour et chaque récupération réussie valide vos choix de conception.
Je suggère de commencer petit, en choisissant les dix questions les plus courantes pour le modèle, en créant une documentation claire pour celles-ci, puis en testant si votre modèle peut réellement donner les bonnes réponses au bon moment. Une fois que vous commencez à obtenir le résultat attendu, vous pouvez répéter le processus pour élargir la base de connaissances.
La différence entre un modèle qui devine et un modèle qui sait se résume à ce travail de conservation délibéré. L’affinement continu rend la recherche suivante plus facile et les résultats plus fiables.



