RAG i praktiken (Svenska (Swedish))

RAG i praktiken

Saturday, 22 November 2025

//

19 minute read

I Del 1 av denna RAG-serie, har vi omfattat fundamentalerna av Retrieval, -, upphöjt generation, —, vad det är, M SK3, hur det fungerar och den underliggande tekniken. (, embeddinger, MSC6, vektordatabaser,,, LLM-infrastrukturer och ).. Nu är det dags att implementera kunskapen i praktiken... Denna artikel visar er hur man bygger riktiga RAG-system med fungerande C-koder, MSL11, MLS12, lösa vanliga problem, MNL13 och använda avancerade tekniker från nya forskninger.

Inleiding

Om du inte har läst Del 1: RAG förklarad, Jag föreslår att börja där för att förstå de grundläggande koncepten

  • Vad är RAG och varför det är viktigt
  • Hur inbäddar och vektorsökningar fungerar
  • LLM-internaler (tokens, KV-cache, , kontextfenster,)
  • RAG vs fin-tunning och andra angrepp

Detta artikel antar att du förstår dessa grunder och fokuserar på implementering, optimering , och verkliga patterner.

Real-World RAG-applikationer på den här bloggen

I' har byggt flera RAGs - kraftfulla egenskaper på den här bloggen

Varje bloggpost kan visa "Antagna Poster" med hjälp av semantisk likhet

Hur det fungerar:

  1. Varje bloggpost blir inbäddad när den publiceras
  2. När vi tittar på en post, får vi fram dess inbäddar från Qdrant
  3. Hitta de 5 liknande inbäddarna
  4. Vertoon som "Antagliga meddelanden"

Varför är det bättre än etiketter?

  • Tags kräver manuell kategorisering
  • Semantiska sökningar hittar konceptuellt besläktade Beiträge även utan att matcha tag
  • "Docker Compose" och "Kontainer orchestrationM SK3 poster är semantiskt besläktade

Kod snippet:

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. Search for semantic blog

Sökboxen på den här bloggen använder sig av RAG-stilens semantiska sök (men utan generationsdelen ♫ - ♫ är det ♫

User experience:

  • söka efter " Konfiguration av en databas " | | → | hittar poster om PostgreSQL
  • söka efter "deployment
  • Ingen behov av exakta nyckelmatcher

Implementering: I' ska omfatta byggandet av detta i en kommande artikel om vektordatabaser .

3. advokaten GPT

I' bygger ett komplettt RAG-system för att hjälpa mig skriva nya bloggar

Använd fallet: När jag börjar skriva om " att lägga till autentisering till ASP . NET Core

  1. Inbäddar min nuvarande ritning
  2. Forskade tidigare Beiträge för liknande innehåll om autentisering, ASP
  3. Föreslår relevanta kodsnippet jag har använt förut
  4. Auto-genererar inre länkar till besläktade postituser
  5. Håller konsistens med min skrivstil

Pipeline full RAG

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;
}

Det här är RAG i aktion - sökning ( semantiskt sökande | | ) | + | upphöjning ♫ ( | inlägsning av kontexten | LLM föreslår |

vanliga RAG-utmaningar och lösningar

Att bygga produktions RAG-system är inte triviellt. Här är utmaningarna jag har ställt mig inför och hur man löser dem.

Utmaningen 1: Spelningsstrategi

Problemet: Hur ska man dela upp dokumente?

Lösningen: Hybridt chunking baserat på dokumentstrukturen.

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;
    }
}

De bästa metoderna:

  • Att respektera dokumentstrukturen (kolor , punkter)
  • Lägg till överlappande rader mellan stycken (50-100 ord)
  • Bevara kodblock intaktade
  • Inbegrip sektioners opskrifte i varje chunk för sammanhanget

Utmaningen 2: Embedding-kvalitet

Problemet: Generiska inbäddade modeller kan inte fånga domein

Lösningar:

Option 1: Fin-tune embeddings (advanced)

# 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: Hybrida inbäddar (koppela flera modeller

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: Lägg till metadatafiltering

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" } }
        }
    }
);

Utmaningen 3: Kontextfenster-Management

Problemet: LLMs har tokenbegränsningar. Hur sätter du in queryn + kontexten | + | prompten i fönstret

Lösningen: Dynamisk kontextselektion och sammanfattning.

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);
}

Utmaningen 4: Hallucinationer trots RAG

Problemet: Även i sammanhanget ignorerar LLM ibland det och hallucinerar

Lösningar:

1. Vinniga ingenjörskonst

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. Efteråt -genereringsvalidation

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. Iterativ raffinering

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.";
}

Utmaningen 5: Att hålla indexet uppåt -till -Date

Problemet: När du lägger till nya dokument, måste vektordatabasen vara aktuell.

Lösningen: Automatiserad indexeringskanal.

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
                    );
                }
            }
        }
    }
}

avancerade RAG-tekniker

Låt oss undersöka skärning med hjälp av RAG-tekniker från den senaste forskningen

1. Hypotetiska dokument-Embeddings (HyDE)

Problemet: Användarfragen är ofta korta och dåligt format.. Dokumenten är detaljerade och bra.

Lösningen: Generera ett hypotetiskt idealdokument som skulle svara på frågan.

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);
}

Varför det fungerar: Den hypotetiska svaret använder samma språk och struktur som faktiska dokument

2. Själv

Problemet: Användarfragen blandar ofta semantisk sök med metadatafilter.

exempel: "Det senaste posterna om DockerM SK2 | = semantiskt ("Docker") ♫ + filtern ( datum |> ♫

Lösningen: Använd LLM för att analysera sökningen till semantiska + metadatafilter .

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-Kräv RAG

Problemet: En enda fråga kan missa relevanta dokument på grund av frasen.

Lösningen: Generera flera variationer av sökningen

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. Kontextuell kompression

Problemet: De återvunna chunks innehåller irrelevant information. Att skicka ut alla avfall-tokens.

Lösningen: Använd en mindre LLM för att komprimera uppfunnen kontext till bara relevanta delar.

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. Iterativt att få fram data (MultiM SK2Hop RAG)

Problemet: komplexa frågor kräver information från flera källor som behöver kopplas ihop.

exempel: " Vilken databas använder bloggen och hur implementeras semantiska sökningar

  • Hop 1: Hitta vilken databas som används (PostgreSQL)
  • Hop 2: Hitta hur semantiskt sökande fungerar med den databasen

Lösningen: Iterativt extraherande och syntetisering.

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. långt-Term Kontextminne med RAG + sammanfattning

Problemet: Hur bygger man AI-system som minns konversationer från månader eller år sedan?

Lösningen: Kombinera RAG med fortskridande sammanfattning för att skapa persistent

Detta är tillvägagångssättet som används i DiSE (Inriktad syntetisk evolution) - en avancerad system I'm byggnad som använder RAG-baserad kontextminnet för att upprätthålla delade konversationshistorierna oändligt

Ett exempel scenario:

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

Hur det fungerar:

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

Implementeringsmetoden:

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")
                }
            );
        }
    }
}

Varför är detta kraftfullt:

  1. Oändlig minnesspann - Konversationer från förr är lika tillgängliga som igår
  2. Semantiska sökningar - "George' specifikationernaM SK3 hittar konversationen även om den är lagrad som
  3. Enhetens länkning - Alla konversationer som nämner "George" är sammankopplade
  4. Privacy-bevarande - Kan implementera varje userminne isolering
  5. Kosten-effektiv - Summarisering förhindrar data lagningsexplosioner samtidigt som man upprätthåller sökbarhet

Ett verkligt exempel från DiSE

DiSE använder denna metod för att komma ihåg

  • Invocationer av verktyg från månader sedan ( vad som fungerade , vad det misslyckades
  • Kodmönster som varit framgångsrika i tidigare projekt
  • Användar preferenser grundade i tidigare konversationer
  • Domainkunskaper samlats över tid

Detta skapar ett AI-system som genuint """, lär sig från varje interaktion och bygger institutionella minnen, , istället för att börja ny från varje sessie.

Utmaningarna att tänka på:

  1. Privatliv: Gamla konversationer måste vara korrekt isolerade per användare
  2. Relevans försämring: Inte alla gamla informationer är relevanta
  3. Storhetskostnader: Det behövs en sammanfattningsstrategi för skalan
  4. Konsistens: Att hålla sammanfattningar i ordning med original
  5. Precision: Balans mellan den senaste och den historiskt betydelsefulla kontexten

Den här tekniken omvandlar RAG från """, "" sök upp mina dokument "" "", till "" МSK2 "". Kom ihåg allt vi någonsin har diskuterat.

När inte använda RAG

RAG är inte alltid svaret.

1. allmäna kunskapsfrågor

  • "Vad är huvudstaden i Frankrike
  • RAG lägger till latens och kostnad utan nytta

2. Kreativt skrivande

  • Poesi
  • RAG begränsar kreativitet genom att basera sig på existerande text

3. Verkliga databehov

  • Börsekurser, Live-sportresultat
  • Mer tillfredsställt av API-integrationer, inte RAG

4. Matematisk resonemang

  • " räkna ut 15% spets på $83.47"
  • LLMs kan göra aritmetik.

5. Väldigt små kunskapsbaser

  • Om din kunskapsbas passar in i kontextfenstern, så inkludera bara allt
  • RAG överhead är inte värt det för 5-dokument

6. När du kontrollerar LLMs training

  • Om du trainingar en modell från början på dina data, skulle det kanske vara bättre att justera
  • RAG är bäst när du använder förgångna modeller som du inte kontrollerar

Att börja med RAG: En praktisk vägledning

Vill du bygga ditt eget RAG-system?

Vecka 1: Start Enkel

mål: få det grundläggande att hämta data utan 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

vecka 2: Lägg till vektordatabas

mål: Skala till riktiga dokumentsamlingar.

Nästa steg för att implementera

  1. Att köra Qdrant i Docker
  2. Indeksera dina dokument
  3. Implementera sökendet

I' ska beskriva detta i detalj i en kommande artikel om vektordatabaser .

vecka 3: Lägg till LLM-generation

mål: Completera RAG-sledning.

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

vecka 4: Optimera och engelska

  • Lägg till kassering ( spara på API-kostnaden )
  • Försämra killningsstrategin
  • Att lägga till metadatafiltering
  • Implementera hybrid sök
  • Lägg till citationsextraktion

På plats (Optionell)

När grunden fungerar migraerar de till den lokala slutsatsen (I ' Jag kommer att beskriva detta i kommande artiklarna

  • Lokal LLM-förklaring med GPU: s acceleration
  • CPU-vänliga inbäddar med ONNX Runtime

Slutsatsen

RAG (AttrivningM SK1Augmented Generation) är en kraftfull teknik för att göra LLMs mer precisa , upp, - till - datum, , och tillförlitliga genom att basera deras svar på faktiska dokument.

  1. Ta tillbaka relevant information från en kunskapsbas med hjälp av semantisk sök
  2. Tillväxt LLM's prompt med den hämtade kontexten
  3. Generera svar som är baserade på riktiga källor med citat

Nyckliga fördelar med RAG:

  • Alltid uppåt-tillM SK1datum (uppdatera bara din kunskapsbassa )
  • Begrundat i källor ( minskar hallucinationer )
  • Förklaring (kan citera källor)
  • kostnaderna-effektiva | ( | inga dyra omkvalificeringar
  • Privatsphäre-bevara (kan köra fullständigt lokaltM SK2
  • Domain-specifikt (använd dina egna dokument

När använda RAG:

  • Vetenskapsprocesser
  • Frequent uppdaterar information
  • Privata data
  • Behovet av citationer och kontrollbarhet
  • kostnad och enkelhet

När ska man undvika RAG:

  • Allgemeina kunskapsfrågor
  • Kreativt skrivande
  • Verklig data -tidsdata | ( | använda API istället
  • Väldigt små kunskapsbaser

fältet utvecklas snabbt med avancerade tekniker som HyDE, multi, query retrieval och kontextuell kompression, men kärnan är enkel, : ger LLM tillgång till rätt information vid rätt tidpunkt.

Starta enkelt, mäta resultat , och itera. RAG är en av de mest praktiska sätten att bygga tillförlitliga AI-system idag

Vad's Nästa?

Nu när ni förstår båda grunderna (Del 1) och praktiska tillämpningar av RAG

Kom snart:

  • CPU-Friendly Semantic Search - Att bygga semantiska sökningar med ONNX-integreringar som funkar på vilken VPS som helst utan GPU-behov
  • Self-Hosted Vector Databases - Hela uppsättningsguidet för Qdrant med Docker, inklusive indexeringM SK2 sökningMSC3 och optimeringsstrategier
  • Att bygga en RAG-skrivare - En hel serie om att skapa en AI-adjuster som använder din existerande innehåll som en kunskapsbas

De här artiklarna kommer ta dig från teorin till praktiken, med komplett arbetskod , utpostningsstrategier M SK2 och verkliga - världsoptimeringar baserat på att köra dessa system i produktion på den här bloggen

Stanna tuned for hands-on implementeringsguidaner som omvandlar detta RAG-kunskap till arbetssystem

resurser

Grunddokument:

verktyg och ramverk:

Weiterlesen:

lycklig byggnad!

Finding related posts...
logo

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