# RAG dans la pratique: Construction Real-Applications mondiales

À l'intérieur [Partie 1](/blog/rag-primer) et [Partie 2](/blog/rag-architecture) de cette série, nous avons examiné le RAGM SK1 ses origines , les principes fondamentaux

<datetime class="hidden">2025-11-22T10:00</datetime>

<!-- category -- AI, RAG, Machine Learning, Semantic Search, LLM, AI-Article -->
# Introduction

**Navigation de la série 📖** Il s'agit d'une partie 3 de la série RAGM SK1

- [Partie 1: Origines et fondements](/blog/rag-primer) - Historique, motivationM SK2 et concepts de base
- [Partie 2: Architecture et intérieurs](/blog/rag-architecture) - Étude technique approfondie de la façon dont fonctionne le RAG
- **Partie 3: RAG dans la pratique** (le présent articleM SK1 - Établissement de systèmes réels, défis,, et techniques avancées
- [Partie 4a: ONNX & Mise en œuvre de la garantie](/blog/semantic-search-with-onnx-and-qdrant) - CPU- fondation de recherche sémantique conviviale
- [Partie 4b: Recherche sémantique en action](/blog/semantic-search-in-action) - Typeahead , recherche hybride, et UI
- [Pièce 5: Recherche hybride & AutoM SK2Indexage](/blog/rag-hybrid-search-and-indexing) - Modèles d'intégration de la production
- [Partie 6: GraphRAG](/blog/graphrag-knowledge-graphs-for-rag) - Graphiques de connaissances pour le corpus

Si vous n’avez pas lu les parties 1 et 2,, je recommande de commencer par la comprendre.

- Qu'est-ce que le RAG et pourquoi il est important (Part 1)
- L'histoire de la recherche par mots clés à l'interprétation sémantique (Part 1)
- Pipeline RAG complet: indexation, extrait de , génération M SK3 partie 2)
- Internaux de LLM: tokens, cache KVM SK2 fenêtres contextuelles MSC3Part 2)
- RAG vs fine-tuning et autres approches M SK1Parte 1)

Cet article suppose que vous comprenez ces éléments fondamentaux et se concentre sur **implémentation, optimizationM SK1 et modèles réels- monde**.

[TOC]

# 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:**

1. Chaque post de blog est intégré lorsque publié
2. Lors de la visualisation d’un post,, nous retirons son emplacement à Qdrant
3. Trouver les 5 embeddings post les plus similaires
4. 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:**

```csharp
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 :

1. Insérer mon projet actuel
2. Recherche les articles précédents pour des contenus connexes sur l'authentification, ASPM SK1NET, sécurité
3. Suggère des fragments de code pertinents que j'ai utilisés avant
4. Auto-génère des liens internes aux postes connexes
5. Maintien de la cohérence avec mon style d'écriture

**Gazoduc RAG complet:**

```csharp
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.

```csharp
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

```python
# 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)

```csharp
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**

```csharp
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.

```csharp
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**

```csharp
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**

```csharp
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**

```csharp
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.

```csharp
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.

```csharp
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.

```csharp
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 .

```csharp
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.

```csharp
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.

```csharp
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:**

```mermaid
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:**

```csharp
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:**

1. **Étendue de mémoire infinie** - Les conversations depuis des années sont aussi accessibles que hier
2. **Recherche sémantique** - "GeorgeM SK2s spécifications" trouve la conversation même si elle est conservée sous forme d’articles de préscription.
3. **Liens entre les entités** - Toutes les conversations mentionnant "GeorgeM SK2 sont reliées
4. **Préservation de la vie privée-** - Peut mettre en œuvre l’isolement de la mémoire des utilisateurs par -
5. **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:**

1. **Protection des renseignements personnels**: Les conversations anciennes doivent être correctement isolées par utilisateur
2. **Dégradation de la pertinence**: Tous les anciens renseignements ne restent pas pertinents
3. **Coûts d'entreposage**: Il faut une stratégie de synthèse pour l’échelle
4. **Cohérence**: Maintien des résumés alignés sur les originaux
5. **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.

```csharp
// 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:

1. Faire fonctionner Qdrant dans Docker
2. Indiquer vos documents
3. 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.

```csharp
// 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.

1. **Récupérer** information pertinente provenant d'une base de connaissances utilisant une recherche sémantique
2. **Augmentation** la prompte LLM's avec ce contexte récupéré
3. **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

# Conclusion: De la théorie à la production

Vous'avez maintenant terminé la série RAG de trois piècesM SK1:

**[Partie 1: Origines et fondements](/blog/rag-primer)**

- Qu'est-ce que le RAG et pourquoi il est important
- Historique de la recherche par mots-clés à la compréhension sémantique
- RAG vs fine-tuning, long contextM SK2 et autres approches

**[Partie 2: Architecture et intérieurs](/blog/rag-architecture)**

- Pipeline RAG complet: indexationRecupération de ,génération du ,
- LLM internes: tokens, cache KVM SK2 fenêtres contextuelles
- Détails de mise en œuvre technique avec le code C#

**Partie 3: RAG dans la pratique (le présent article)**

- Real-world implementations (Related Posts , Semantic SearchMSC3 Writing AssistantM SK4
- Difficultés et solutions communes (chunkingM SK1embedding quality , hallucination)
- Techniques avancées (HyDE,Self-interrogatoireM SK2Requête ,MultipleMST4RechercheM ST5Compression contextuelleM st6LongMst7Memorie à termeMstr8
- Quand ne pas utiliser RAG
- Guide de démarrage

**Vous avez maintenant le tableau complet:** De la compréhension des origines du RAG' à l’élaboration de systèmes de production avec des optimisations avancées.

# Qu’est-ce que's Suivant?

Maintenant que vous comprenez le RAG de la théorie à la pratique, les articles qui suivent vous montreront comment construire des systèmes RAG completsM SK1 production- prêts en 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](https://arxiv.org/abs/2005.11401) - Le papier RAG original
- [Récupération de passages denses pour Open-Domain Question Answering](https://arxiv.org/abs/2004.04906) - RMR fondation de récupérationM SK2
- [Attention est tout ce dont vous avez besoin](https://arxiv.org/abs/1706.03762) - Transformers

**Outils et cadres:**

- [Qdrant](https://qdrant.tech/) - Base de données Vector que j'utilise
- [Le temps de fonctionnement d'ONNX](https://onnxruntime.ai/) - Pour les emplacements locaux
- [LLamaSharp](https://github.com/SciSharp/LLamaSharp) - Pour l'inférence locale LLM
- [Transformers de phrase](https://www.sbert.net/) - Modèles d'emplacement

**Lire plus:**

- [Anthropiques: Récupération contextuelle](https://www.anthropic.com/index/contextual-retrieval) - Techniques avancées de RAG
- [Comment fonctionne la traduction en machine neuronale](/blog/how-neural-machine-translation-works) - Comprendre les modèles d’AI derrière les emplacements

**Cette série RAG:**

- [Partie 1: Origines et fondements](/blog/rag-primer) - Historique, motivationM SK2 concepts fondamentaux
- [Partie 2: Architecture et intérieurs](/blog/rag-architecture) - plongée technique profonde
- **Partie 3: RAG dans la pratique** (le présent articleM SK1 - Établissement de systèmes réels

Bonne construction!