RAG dans la pratique: Construction Real-Applications mondiales (Français (French))

RAG dans la pratique: Construction Real-Applications mondiales

Saturday, 22 November 2025

//

22 minute read

À 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:

  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:

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:

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:

  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.

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

// 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

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:

Outils et cadres:

Lire plus:

Bonne construction!

Finding related posts...
logo

© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.