This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
RAG dans la pratique: Construction Real-Applications mondiales
Saturday, 22 November 2025
À l'intérieur Partie 1 de cette série RAG, nous avons examiné les principes fondamentaux de Retrieval -Génergie accrue —qui est ce qu'il s'agit M SK3comme il fonctionne MSC4 et la technologie sous-jacente
Introduction
Si vous n’avez pas lu Partie 1: RAG Expliqué, Je recommande d’y commencer pour comprendre les concepts fondamentaux.
- Qu'est-ce que le RAG et pourquoi il est important
- Comment fonctionnent les embeddings et la recherche par vecteur
- Internaux de LLM (tokens, cache KVM SK2 fenêtres contextuelles )
- RAG vs fine-tuning et autres approches
Cet article suppose que vous comprenez ces principes fondamentaux et se concentre sur implémentation, optimizationM SK1 et modèles réels- monde.
Real-World RAG Applications sur ce blog
I' j’ai construit plusieurs RAG- fonctions puissantes sur ce blogM SK2 Permettez-moi de vous montrer des exemples concretsMSC3
1. Postes connexes Recommandation
Chaque post de blog peut montrer "Postes connexesM SK1 utilisant la similitude sémantique.
Comment il fonctionne:
- Chaque post de blog est intégré lorsque publié
- Lors de la visualisation d’un post,, nous retirons son emplacement à Qdrant
- Trouver les 5 embeddings post les plus similaires
- Display as "Postes connexes"
Pourquoi le ' est-il meilleur que les tags?
- Les balises nécessitent une catégorisation manuelle
- La recherche sémantique permet de trouver des articles conceptuellement liés même sans les balises correspondantes
- Les postes "Docker Compose" et "Container orchestrationM SK3 sont liés sémantiquement
Partie de code:
public async Task<List<SearchResult>> GetRelatedPostsAsync(
string currentPostSlug,
string language,
int limit = 5)
{
// Get the current post's embedding
var currentPost = await _vectorStore.GetByIdAsync(currentPostSlug);
if (currentPost == null)
return new List<SearchResult>();
// Find similar posts
var similarPosts = await _vectorStore.SearchAsync(
currentPost.Embedding,
limit: limit + 1, // +1 because result includes the current post
filter: new Filter
{
Must =
{
new Condition
{
Field = "language",
Match = new Match { Keyword = language }
}
},
MustNot =
{
new Condition
{
Field = "slug",
Match = new Match { Keyword = currentPostSlug }
}
}
}
);
return similarPosts.Take(limit).ToList();
}
2. Recherche sur le blog sémantique
La case de recherche sur ce blog utilise RAG-style de recherche sémantique (même si sans la partie de génération - c'est seulement le retrievalM SK4
Expérience de l'utilisateur:
- Recherche pour "configuration d'une base de données" → trouve des articles sur les migrations PostgreSQL
- Recherche pour "déploiementM SK1 → trouve des articles sur Docker, hostingMST4 CIMSC5CD
- Il n'est pas nécessaire de trouver des mots-clés exacts.
Mise en œuvre: I' Je vais aborder la construction de cet article dans un prochain article sur les bases de données vectorielles.
3. Le " Avocat GPT" Adjoint à l'écriture
I'je mets en place un système RAG complet qui m’aidera à écrire de nouveaux articles sur mon blog.
Utiliser le cas: Lorsque je commence à écrire sur "addition d'authentification à ASPM SK1NET Core", le système :
- Insérer mon projet actuel
- Recherche les articles précédents pour des contenus connexes sur l'authentification, ASPM SK1NET, sécurité
- Suggère des fragments de code pertinents que j'ai utilisés avant
- Auto-génère des liens internes aux postes connexes
- Maintien de la cohérence avec mon style d'écriture
Gazoduc RAG complet:
public async Task<WritingAssistanceResponse> GetSuggestionsAsync(
string currentDraft,
string topic)
{
// 1. Embed the current draft
var draftEmbedding = await _embeddingService.GenerateEmbeddingAsync(
currentDraft
);
// 2. Retrieve related past content
var relatedPosts = await _vectorStore.SearchAsync(
draftEmbedding,
limit: 5
);
// 3. Build context for LLM
var prompt = BuildWritingAssistancePrompt(
currentDraft,
topic,
relatedPosts
);
// 4. Generate suggestions using local LLM
var suggestions = await _llmService.GenerateAsync(prompt);
// 5. Extract and format citations
var response = ExtractCitations(suggestions, relatedPosts);
return response;
}
Il s'agit d'un RAG en action - récupération ( recherche sémantique) + augmentation MSC4ajout de contexteMSC5 ♫+ génération
Défis et solutions courants de RAG
Établir des systèmes de production RAG n’est pas trivial' . Voici les défis que j’ai rencontrés et comment les résoudre.
Défi 1: Stratégie de partenariat
Problème: Comment répartir les documents? Trop petit = perte de contexteM SK2 trop grand = information irrégulièreMSC4
Solution: Groupage hybride basé sur la structure du document.
public class SmartChunker
{
public List<Chunk> ChunkDocument(string markdown, string sourceId)
{
var chunks = new List<Chunk>();
// Parse markdown into sections
var document = Markdown.Parse(markdown);
var sections = ExtractSections(document);
foreach (var section in sections)
{
var wordCount = CountWords(section.Content);
if (wordCount < MinChunkSize)
{
// Merge small sections
MergeWithPrevious(chunks, section);
}
else if (wordCount > MaxChunkSize)
{
// Split large sections
var subChunks = SplitSection(section);
chunks.AddRange(subChunks);
}
else
{
// Just right
chunks.Add(CreateChunk(section, sourceId));
}
}
return chunks;
}
}
Pratiques exemplaires:
- Respecter la structure du document (entêtesM SK1paragraphes)
- Ajouter le chevauchement entre les chunks (50-100 mots)
- Préserver les blocs de code intacts
- Inclure les titres de section dans chaque chunk pour le contexte
Défi 2: Qualité de l'emplacement
Problème: Les modèles génériques d'embedding ne peuvent pas saisir le domaine-semantique spécifique.
Solutions:
Option 1: Fine-tune embeddings (advancedM SK1
# Using sentence-transformers in Python
from sentence_transformers import SentenceTransformer, InputExample, losses
model = SentenceTransformer('all-MiniLM-L6-v2')
# Create training examples from your domain
train_examples = [
InputExample(texts=['Docker Compose', 'container orchestration'], label=0.9),
InputExample(texts=['Entity Framework', 'ORM database'], label=0.9),
InputExample(texts=['Docker', 'apple fruit'], label=0.1)
]
# Fine-tune
train_dataloader = DataLoader(train_examples, shuffle=True, batch_size=16)
train_loss = losses.CosineSimilarityLoss(model)
model.fit(train_objectives=[(train_dataloader, train_loss)], epochs=1)
Option 2: Emplacements hybrides (combinaison de modèles multiples)
public async Task<float[]> GenerateHybridEmbeddingAsync(string text)
{
var semantic = await _semanticModel.GenerateEmbeddingAsync(text);
var keyword = await _keywordModel.GenerateEmbeddingAsync(text);
// Concatenate or weighted average
return CombineEmbeddings(semantic, keyword);
}
Option 3: Ajouter le filtrage des métadonnées
var results = await _vectorStore.SearchAsync(
queryEmbedding,
limit: 10,
filter: new Filter
{
Must =
{
new Condition { Field = "category", Match = new Match { Keyword = "ASP.NET" } },
new Condition { Field = "date", Range = new Range { Gte = "2024-01-01" } }
}
}
);
Défi 3: Gestion des fenêtres contextuelles
Problème: Les LLM ont des limites de token. Comment insérer la demande + le contexte + l'énoncé dans la fenêtreM SK3
Solution: Choix et résumé du contexte dynamiques.
public string BuildContextAwarePrompt(
string query,
List<SearchResult> retrievedDocs,
int maxTokens = 4096)
{
var promptTemplate = GetPromptTemplate();
var queryTokens = CountTokens(query);
var templateTokens = CountTokens(promptTemplate);
// Reserve tokens for: prompt + query + response
var availableForContext = maxTokens - queryTokens - templateTokens - 500; // 500 for response
// Add context until we hit limit
var selectedContext = new List<SearchResult>();
var currentTokens = 0;
foreach (var doc in retrievedDocs.OrderByDescending(d => d.Score))
{
var docTokens = CountTokens(doc.Text);
if (currentTokens + docTokens <= availableForContext)
{
selectedContext.Add(doc);
currentTokens += docTokens;
}
else
{
// Try summarizing the doc if it's important
if (doc.Score > 0.85)
{
var summary = await SummarizeAsync(doc.Text, maxTokens: 200);
var summaryTokens = CountTokens(summary);
if (currentTokens + summaryTokens <= availableForContext)
{
selectedContext.Add(new SearchResult
{
Text = summary,
Title = doc.Title,
Score = doc.Score
});
currentTokens += summaryTokens;
}
}
}
}
return FormatPrompt(query, selectedContext);
}
Défi 4: Hallucination malgré le RAG
Problème: Même dans le contexte, les LLM l’ignorent parfois et hallucinaient.
Solutions:
1. Ingénierie rapideM SK1
var systemPrompt = @"
You are a technical assistant.
CRITICAL RULES:
1. ONLY use information from the provided CONTEXT sections
2. If the context doesn't contain the answer, say 'I don't have enough information in the provided context to answer that'
3. DO NOT use your training data to supplement answers
4. Always cite the source using [1], [2] notation
5. If you're unsure, say so
CONTEXT:
{context}
QUESTION: {query}
ANSWER (following all rules above):
";
2. Posts-validation de la générationM SK2
public async Task<bool> ValidateResponseAgainstContext(
string response,
List<SearchResult> context)
{
// Check if response contains claims not in context
var responseSentences = SplitIntoSentences(response);
foreach (var sentence in responseSentences)
{
var isSupported = await IsClaimSupportedByContext(sentence, context);
if (!isSupported)
{
_logger.LogWarning("Hallucination detected: {Sentence}", sentence);
return false;
}
}
return true;
}
3. Raffinement itératifM SK1
public async Task<string> GenerateWithValidationAsync(
string query,
List<SearchResult> context,
int maxAttempts = 3)
{
for (int attempt = 0; attempt < maxAttempts; attempt++)
{
var response = await _llm.GenerateAsync(
BuildPrompt(query, context)
);
var isValid = await ValidateResponseAgainstContext(response, context);
if (isValid)
return response;
// Refine prompt for next attempt
query = $"{query}\n\nPrevious attempt hallucinated. Stick strictly to the context.";
}
return "I couldn't generate a reliable answer. Please rephrase your question.";
}
Défi 5: Maintenir l’indice à la hausse-àM SK2Date
Problème: Au fur et à mesure que vous ajoutez de nouveaux documents,, la base de données vectorielle doit rester en vigueur.
Solution: Pipeline d'indexation automatique.
public class BlogIndexingBackgroundService : BackgroundService
{
private readonly IVectorStoreService _vectorStore;
private readonly IMarkdownService _markdownService;
private readonly ILogger<BlogIndexingBackgroundService> _logger;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
try
{
await IndexNewPostsAsync(stoppingToken);
// Check for updates every hour
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in indexing service");
}
}
}
private async Task IndexNewPostsAsync(CancellationToken ct)
{
var allPosts = await _markdownService.GetAllPostsAsync();
foreach (var post in allPosts)
{
var existingDoc = await _vectorStore.GetByIdAsync(post.Slug);
// Check if content changed
var currentHash = ComputeHash(post.Content);
if (existingDoc == null || existingDoc.ContentHash != currentHash)
{
_logger.LogInformation("Indexing updated post: {Title}", post.Title);
var chunks = _chunker.ChunkDocument(post.Content, post.Slug);
foreach (var chunk in chunks)
{
var embedding = await _embeddingService.GenerateEmbeddingAsync(chunk.Text);
await _vectorStore.UpsertAsync(
id: $"{post.Slug}_{chunk.Index}",
embedding: embedding,
metadata: new Dictionary<string, object>
{
["slug"] = post.Slug,
["title"] = post.Title,
["chunk_index"] = chunk.Index,
["content_hash"] = currentHash
},
ct: ct
);
}
}
}
}
}
Techniques avancées de RAG
Let's explorer les techniques de coupe du RAG à partir des recherches récentes .
1. Embeddings hypothétiques de documents (HyDE)
Problème: Les demandes d’utilisateur sont souvent courtes et mal formées. Les chunks de documents sont détaillés et bien conçusM SK1 écrits . Cette incompatibilité nuit à la récupération.
Solution: Générer un document idéal hypothétique qui répondrait à la demande, insérer queM SK1 puis rechercher.
public async Task<List<SearchResult>> HyDESearchAsync(string query)
{
// Generate hypothetical answer (even if hallucinated)
var hypotheticalAnswer = await _llm.GenerateAsync($@"
Write a detailed, technical paragraph that would perfectly answer this question:
Question: {query}
Paragraph:"
);
// Embed the hypothetical answer
var embedding = await _embeddingService.GenerateEmbeddingAsync(
hypotheticalAnswer
);
// Search using this embedding
return await _vectorStore.SearchAsync(embedding);
}
Pourquoi cela fonctionne: La réponse hypothétique utilise un langage et une structure similaires aux documents réels, amélioration de la recherche.
2. Automne
Problème: Les questions des utilisateurs mélangent souvent la recherche sémantique avec les filtres de métadonnées.
Exemple: M SK1Postes récents sur Docker" ≥= sémantiqueMSC4Docker ")
Solution: Utiliser LLM pour analyser la demande en termes sémantiques + filtres de métadonnées.
public async Task<SearchQuery> ParseSelfQueryAsync(string naturalLanguageQuery)
{
var parsingPrompt = $@"
Parse this search query into:
1. Semantic search query (what the user is looking for)
2. Metadata filters (category, date range, etc.)
User Query: {naturalLanguageQuery}
Output JSON:
{{
""semantic_query"": ""the core concept"",
""filters"": {{
""category"": ""...",
""date_after"": ""..."",
""date_before"": ""...""
}}
}}
";
var jsonResponse = await _llm.GenerateAsync(parsingPrompt);
var parsed = JsonSerializer.Deserialize<SearchQuery>(jsonResponse);
return parsed;
}
// Use parsed query
var parsedQuery = await ParseSelfQueryAsync("Recent ASP.NET posts about authentication");
// semantic_query: "authentication"
// filters: { category: "ASP.NET", date_after: "2024-01-01" }
var results = await _vectorStore.SearchAsync(
embedding: await _embeddingService.GenerateEmbeddingAsync(parsedQuery.SemanticQuery),
filter: BuildFilter(parsedQuery.Filters)
);
3. Multi-Query RAG
Problème: Une seule demande pourrait manquer des documents pertinents en raison du libellé suivant :.
Solution: Générer des variations multiples de la recherche, avec tous les résultatsM SK1 combinaison des résultats .
public async Task<List<SearchResult>> MultiQuerySearchAsync(string query)
{
// Generate query variations
var variations = await _llm.GenerateAsync($@"
Generate 3 different ways to phrase this search query:
Original: {query}
Variations (one per line):
");
var queries = variations.Split('\n', StringSplitOptions.RemoveEmptyEntries)
.Prepend(query) // Include original
.ToList();
// Search with all variations
var allResults = new List<SearchResult>();
foreach (var q in queries)
{
var embedding = await _embeddingService.GenerateEmbeddingAsync(q);
var results = await _vectorStore.SearchAsync(embedding, limit: 10);
allResults.AddRange(results);
}
// Deduplicate and merge scores
var merged = allResults
.GroupBy(r => r.Id)
.Select(g => new SearchResult
{
Id = g.Key,
Text = g.First().Text,
Title = g.First().Title,
Score = g.Max(r => r.Score) // Take best score
})
.OrderByDescending(r => r.Score)
.ToList();
return merged;
}
4. Compression contextuelle
Problème: Les chunks récupérés contiennent des informations irrelevantes. Envoyant tous les tokens de déchets.
Solution: Utiliser un LLM plus petit pour comprimer le contexte récupéré à seulement les parties pertinentes.
public async Task<string> CompressContextAsync(
string query,
List<SearchResult> retrievedDocs)
{
var compressed = new List<string>();
foreach (var doc in retrievedDocs)
{
var compressionPrompt = $@"
Extract only the sentences from this document that are relevant to answering the question.
Question: {query}
Document:
{doc.Text}
Relevant excerpts (maintain original wording):
";
var relevantExcerpt = await _smallLLM.GenerateAsync(compressionPrompt);
if (!string.IsNullOrWhiteSpace(relevantExcerpt))
{
compressed.Add($"From '{doc.Title}':\n{relevantExcerpt}");
}
}
return string.Join("\n\n", compressed);
}
5. Récupération itérative (MultiM SK2Hop RAG)
Problème: Les questions complexes nécessitent des informations provenant de multiples sources qui doivent être reliées.
Exemple: M SK1Quelle base de données le blog utilise et comment la recherche sémantique est-elle mise en œuvre?
- Hop 1: Trouver quelle base de données est utilisée (PostgreSQL)
- Hop 2: Trouver comment la recherche sémantique fonctionne avec cette base de données
Solution: Récupération et synthèse itératives.
public async Task<string> MultiHopRAGAsync(string complexQuery, int maxHops = 3)
{
var currentQuery = complexQuery;
var allContext = new List<SearchResult>();
for (int hop = 0; hop < maxHops; hop++)
{
// Retrieve for current query
var results = await SearchAsync(currentQuery, limit: 5);
allContext.AddRange(results);
// Check if we have enough information
var synthesisPrompt = $@"
Original question: {complexQuery}
Context so far:
{FormatContext(allContext)}
Can you answer the original question with this context?
If yes, provide the answer.
If no, what additional information do you need? (be specific)
";
var synthesis = await _llm.GenerateAsync(synthesisPrompt);
if (synthesis.Contains("yes", StringComparison.OrdinalIgnoreCase))
{
// We have enough information
return ExtractAnswer(synthesis);
}
// Extract what we need for next hop
currentQuery = ExtractNextQuery(synthesis);
}
// Final synthesis with all gathered context
return await GenerateFinalAnswerAsync(complexQuery, allContext);
}
6. LongM SK1Memorie du contexte de terminaux avec RAG + Sommaire
Problème: Comment bâtir des systèmes d’AI qui se souviendront de conversations tenues il y a plusieurs mois ou des années?
Solution: Combiner RAG avec la sommation progressive pour créer une mémoire persistante, rechercheable.
Il s’agit de l’approche utilisée dans DiSE (Evolution synthétique dirigée) - un système avancé I'm bâtiment qui utilise la mémoire contextuelle basée sur RAG - pour maintenir l'historique de conversation partagée indéfiniment
Exemple de scénario:
User (Today): "Remember George's specs?"
AI: "Yes, you discussed George's prescription requirements in our conversation
from 5 years ago (2019-03-15). He needed progressive lenses with..."
Comment il fonctionne:
flowchart TB
A[User Message] --> B[Store in RAG Memory]
B --> C[Extract Key Entities & Topics]
C --> D[Link to Past Conversations]
E[Periodic Summarization] --> F[Summarize Old Conversations]
F --> G[Store Summary with High-Level Tags]
G --> H[Keep Original for Retrieval]
I[Future Query: 'George's specs'] --> J[Semantic Search in RAG]
J --> K[Find: 2019 conversation]
K --> L[Retrieve Original Context]
L --> M[LLM generates response with 5-year-old context!]
style B stroke:#f9f,stroke-width:3px
style J stroke:#bbf,stroke-width:3px
Approche de mise en œuvre:
public class LongTermConversationalMemory
{
private readonly IVectorStoreService _vectorStore;
private readonly IEmbeddingService _embeddingService;
public async Task StoreConversationAsync(
string conversationId,
string userId,
List<ConversationTurn> turns,
DateTime timestamp)
{
// Extract key entities and topics
var entities = await ExtractEntitiesAsync(turns);
var topics = await ExtractTopicsAsync(turns);
// Create searchable representation
var conversationText = string.Join("\n", turns.Select(t =>
$"{t.Speaker}: {t.Message}"));
// Generate embedding
var embedding = await _embeddingService.GenerateEmbeddingAsync(
conversationText);
// Store in RAG with rich metadata
await _vectorStore.IndexDocumentAsync(
id: $"conv_{conversationId}",
embedding: embedding,
metadata: new Dictionary<string, object>
{
["user_id"] = userId,
["timestamp"] = timestamp.ToString("O"),
["entities"] = entities, // ["George", "specs", "prescription"]
["topics"] = topics, // ["healthcare", "eyewear"]
["full_text"] = conversationText,
["turn_count"] = turns.Count
}
);
}
public async Task<List<PastContext>> RetrieveRelevantPastAsync(
string currentQuery,
string userId,
int limit = 5)
{
// Embed the current query
var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(
currentQuery);
// Search past conversations
var results = await _vectorStore.SearchAsync(
queryEmbedding,
limit: limit,
filter: new Filter
{
Must =
{
new Condition { Field = "user_id", Match = new Match { Keyword = userId } }
}
}
);
return results.Select(r => new PastContext
{
ConversationId = r.Id,
Timestamp = DateTime.Parse(r.Metadata["timestamp"].ToString()),
Entities = (List<string>)r.Metadata["entities"],
FullText = r.Metadata["full_text"].ToString(),
Relevance = r.Score
}).ToList();
}
// Periodic summarization to keep memory manageable
public async Task SummarizeOldConversationsAsync(DateTime olderThan)
{
var oldConversations = await _vectorStore.FindByDateRangeAsync(
endDate: olderThan);
foreach (var conv in oldConversations)
{
// Generate summary using LLM
var summary = await _llm.GenerateAsync($@"
Summarize this conversation, preserving key facts and entities:
{conv.FullText}
Summary:");
// Update document with summary while keeping original
await _vectorStore.UpdateAsync(
id: conv.Id,
additionalMetadata: new Dictionary<string, object>
{
["summary"] = summary,
["summarized_at"] = DateTime.UtcNow.ToString("O")
}
);
}
}
}
Pourquoi cela est puissant:
- Étendue de mémoire infinie - Les conversations depuis des années sont aussi accessibles que hier
- Recherche sémantique - "GeorgeM SK2s spécifications" trouve la conversation même si elle est conservée sous forme d’articles de préscription.
- Liens entre les entités - Toutes les conversations mentionnant "GeorgeM SK2 sont reliées
- Préservation de la vie privée- - Peut mettre en œuvre l’isolement de la mémoire des utilisateurs par -
- Coût-efficacité - La sommisation évite l'explosion de stockage tout en maintenant la recherche
Exemple réelle de DiSE-world
DiSE utilise cette méthode pour se rappeler:
- Invocations d'outils depuis quelques mois (ce qui a fonctionné, ce qui a échouéM SK2
- Patterns de code qui ont été couronnées de succès dans des projets antérieurs
- Préférences des utilisateurs a été établie dans des conversations antérieures
- Connaissance du domaine accumulé au fil du temps
Cela crée un système d’AI qui apprend véritablement " de chaque interaction et construit une mémoire institutionnelle , plutôt que de commencer chaque session à nouveau.
Les défis à relever:
- Protection des renseignements personnels: Les conversations anciennes doivent être correctement isolées par utilisateur
- Dégradation de la pertinence: Tous les anciens renseignements ne restent pas pertinents
- Coûts d'entreposage: Il faut une stratégie de synthèse pour l’échelle
- Cohérence: Maintien des résumés alignés sur les originaux
- Précision de récupération: Équilibrage entre le contexte récent et celui historiquement important
Cette technique transforme le RAG de "cherche mes documents" en "remmère tout ce que nous avons déjà discutéMSC3M SK4 - un jeuMST6changer pour des assistants AI à long termeSST7
Quand ne pas utiliser RAG
Le RAG n’est pas toujours la réponse.
1. Questions de connaissance générale
- "Qu'est-ce que la capitale de France?
- RAG ajoute la latence et le coût sans aucun avantage
2. Écrit créatif
- Poésie, fiction, brainstorming
- Le RAG restreint la créativité en se fondant sur le texte existant
3. Dépenses de données réelles
- Prix des actions, scores de sports en direct
- Amélioration des services par les intégrations API, non RAG
4. Rationalisation mathématique
- "Calculer 15% de l'extrémité sur MSC2
- LLMs peuvent faire l'arithmetique; RAG ne fonctionne pas't aide
5. Très petites bases de connaissances
- Si votre base de connaissances s’inscrit dans la fenêtre contextuelle,, incluez-le tout.
- Les frais généraux de RAG ne sont pas valables pour les documents '
6. Lorsque vous contrôlez l’entraînement du LLM
- Si vous 'traînez un modèle à partir de zéro sur vos données , fineM SK2le réglage pourrait être meilleur
- Le RAG est le meilleur lorsque vous utilisez des modèles préformés qui ne sont pas contrôlés par '
Comment commencer par RAG: Guide pratique
Vous voulez bâtir votre propre système RAG? Voici'est un pasM SK2par -approche par étapesMSC4
Semaine 1: Début simple
Objectif: Obtenir une récupération de base sans LLM.
// 1. Choose an embedding service (start with API for simplicity)
var openAI = new OpenAIClient(apiKey);
// 2. Embed a few test documents
var docs = new[]
{
"Docker is a containerization platform",
"Kubernetes orchestrates containers",
"Entity Framework is an ORM for .NET"
};
var embeddings = new List<float[]>();
foreach (var doc in docs)
{
var response = await openAI.GetEmbeddingsAsync(
new EmbeddingsOptions("text-embedding-3-small", new[] { doc })
);
embeddings.Add(response.Value.Data[0].Embedding.ToArray());
}
// 3. Implement basic search (in-memory for now)
var query = "container orchestration";
var queryEmbedding = await GetEmbeddingAsync(query);
var results = embeddings
.Select((emb, idx) => new
{
Text = docs[idx],
Score = CosineSimilarity(queryEmbedding, emb)
})
.OrderByDescending(r => r.Score)
.ToList();
// 4. Verify search works
foreach (var result in results)
{
Console.WriteLine($"{result.Score:F3}: {result.Text}");
}
// Expected: Kubernetes scores highest
Semaine 2: Ajouter une base de données de vecteurs
Objectif: Élargir à des collections de documents réelles.
Prochaines étapes de la mise en œuvre:
- Faire fonctionner Qdrant dans Docker
- Indiquer vos documents
- Mise en œuvre du point final de la recherche
I' Je vais le traiter en détail dans un article à venir sur les bases de données vectorielles .
Semaine 3: Ajouter la génération de LLM
Objectif: Compléter le pipeline RAG.
// 1. Retrieve context
var context = await SearchAsync(query, limit: 3);
// 2. Build prompt
var prompt = $@"
Answer the question using this context:
{FormatContext(context)}
Question: {query}
Answer:";
// 3. Generate (start with API)
var response = await openAI.GetChatCompletionsAsync(new ChatCompletionsOptions
{
Messages =
{
new ChatMessage(ChatRole.System, "You are a helpful assistant."),
new ChatMessage(ChatRole.User, prompt)
},
Temperature = 0.7f,
MaxTokens = 500
});
return response.Value.Choices[0].Message.Content;
Semaine 4: Optimiser et Polish
- Ajouter le caching (économiser sur les coûts de l’APIM SK1
- Améliorer la stratégie de regroupement
- Ajouter le filtrage des métadonnées
- Mise en œuvre de la recherche hybride
- Ajouter une extraction de citation
Envoyer local (Optionnel)
Une fois que les bases fonctionneront, , migreront vers l’inférence locale (IM SK2 Je vous en parlerai dans les prochains articles
- Inférence locale LLM avec accélération GPU
- CPU- embeddings conviviales avec ONNX Runtime
Conclusion
RAG (RetrievalM SK1Augmented Generation ) est une technique puissante pour rendre les LLM plus exacts, jusqu’à la date de - à - et crédibles en fondant leurs réponses sur des documents réels.
- Récupérer information pertinente provenant d'une base de connaissances utilisant une recherche sémantique
- Augmentation la prompte LLM's avec ce contexte récupéré
- Générer les réponses qui sont fondées sur des sources réelles avec des citations
Principaux avantages du RAG:
- Soyez toujours à jour-àM SK1date (juste actualiser votre base de connaissances )
- Sources fondées (réduira l'allucinationM SK1
- Expliquer ( peut citer des sources)
- Coût-efficace M SK1sans frais de recyclage)
- Protection des renseignements personnels-conservation M SK1se peut exécuter entièrement localement)
- Domaine-spécifique M SK1utiliser vos propres documents)
Quand utiliser RAG:
- Connaissances-tâches intensives
- Mise à jour fréquente de l'information
- Données privées/données propres
- Nécessité de citations et vérifiabilité
- La question du coût et de la simplicité
Quand éviter RAG:
- Questions de connaissance générale
- Écrire créatif
- Données réelles-données de temps M SK1utiliser des APIs en lieu et place)
- Très petites bases de connaissances
Le domaine évolue rapidement grâce à des techniques avancées comme HyDE, multi - recherche de demandesM SK2 et compression contextuelle,, mais le concept fondamental demeure simpleMSC4 donner aux LLM l’accès à l’information appropriée au moment opportunMNK5
Démarrer simple, résultats de mesureM SK1 et iterer. RAG est l’une des façons les plus pratiques d’édifier des systèmes d’AI fiables aujourd’hui
Qu’est-ce que's Suivant?
Maintenant que vous comprenez les deux éléments fondamentaux (Partie 1) et applications pratiques du RAG, dans les prochains articles IM SK2je vous montrerai comment construire des systèmes RAG completsMSC3de productionMNK4dans le cadre de la C#:
Arrivée prochaine:
- CPU-Suite sémantique conviviale - Élaborer une recherche sémantique avec des embeddings ONNX qui fonctionnent sur tout VPS sans GPU
- Self-Databases de vecteurs hébergées - Guide complet de mise en place pour Qdrant avec Docker , incluant l’indexationM SK2 recherche, et stratégies d’optimisation
- Établissement d'un assistant de rédaction du RAG - Une série complète sur la création d’un assistant de rédaction autonome AI- qui utilise votre contenu existant comme base de connaissances
Ces articles vous mèneront de la théorie à la pratique.
Suivre les instructions de mise en œuvre qui transforment ces connaissances RAG en systèmes de travail!
Ressources
Documents de base:
- Récupération-Generation améliorée des connaissances-Taxes importantes de PNL - Le papier RAG original
- Récupération de passages denses pour Open-Domain Question Answering - RMR fondation de récupérationM SK2
- Attention est tout ce dont vous avez besoin - Transformers
Outils et cadres:
- Qdrant - Base de données Vector que j'utilise
- Le temps de fonctionnement d'ONNX - Pour les emplacements locaux
- LLamaSharp - Pour l'inférence locale LLM
- Transformers de phrase - Modèles d'emplacement
Lire plus:
- Anthropiques: Récupération contextuelle - Techniques avancées de RAG
- Comment fonctionne la traduction en machine neuronale - Comprendre les modèles d’AI derrière les emplacements
- RAG Expliqué: Partie 1 - Le préliminaire fondamental
Bonne construction!