# RAG Αρχιτεκτονική και Εσωτερικά: Πώς Λειτουργεί Πραγματικά

Το [Μέρος 1](/blog/rag-primer)Κατανοείτε την έννοια υψηλού επιπέδου: να ανακτήσει τις σχετικές πληροφορίες, στη συνέχεια να το χρησιμοποιήσετε για να δημιουργήσετε απαντήσεις. Τώρα βουτάμε βαθιά στην τεχνική αρχιτεκτονική;Ακριβώς πώς λειτουργούν τα συστήματα RAG κάτω από το καπό, από στρατηγικές κοπής σε LLM εσωτερικά, όπως μάρκες και KV caches.

<datetime class="hidden">2025-11-22T09:30</datetime>

<!-- category -- AI, RAG, Machine Learning, Semantic Search, LLM, AI-Article -->
# Εισαγωγή

**Πλοήγηση σειράς:** Αυτό είναι το μέρος 2 της σειράς RAG:

- [Μέρος 1: Προέλευση και Θεμελιώδη Στοιχεία](/blog/rag-primer) - Ιστορία, κίνητρα και βασικές έννοιες
- **Μέρος δεύτερο: Αρχιτεκτονική και Εσωτερικά** (αυτό το άρθρο) - Τεχνική βαθιά κατάδυση στον τρόπο λειτουργίας των ΚΓΠΕ
- [Μέρος 3: Οι ΚΓΠΕ στην πράξη](/blog/rag-practical-applications) - Κατασκευή πραγματικών συστημάτων, προκλήσεων και προηγμένων τεχνικών
- [Μέρος 4α: Εφαρμογή ONNX & Qdrant](/blog/semantic-search-with-onnx-and-qdrant) - ΚΜΕ-φιλικό θεμέλιο σημασιολογικής αναζήτησης
- [Μέρος 4β: Σημαντική Αναζήτηση σε Δράση](/blog/semantic-search-in-action) - Typenaward, υβριδική αναζήτηση, και UI
- [Μέρος 5: Υβριδική Αναζήτηση & Auto-Indexing](/blog/rag-hybrid-search-and-indexing) - Τρόποι ενσωμάτωσης στην παραγωγή
- [Μέρος 6: GraphRAG](/blog/graphrag-knowledge-graphs-for-rag) - Γραφήματα γνώσης για την κατανόηση του επιπέδου του σώματος

Αν δεν έχετε διαβάσει το Μέρος 1, προτείνω να ξεκινήσετε από εκεί για να καταλάβετε:

- Τι είναι το RAG και γιατί έχει σημασία
- Η ιστορία από την αναζήτηση λέξεων-κλειδιών στη σημασιολογική κατανόηση
- Ο παρών κανονισμός αρχίζει να ισχύει την εικοστή ημέρα από τη δημοσίευσή του στην Επίσημη Εφημερίδα της Ευρωπαϊκής Ένωσης.

Αυτό το άρθρο υποθέτει ότι καταλαβαίνετε αυτά τα βασικά και επικεντρώνεται σε **τεχνική αρχιτεκτονική, λεπτομέρειες εφαρμογής και εσωτερικά LLM**.

[TOC]

# Πώς λειτουργεί το RAG: Η Πλήρης Εικόνα

Ας αναλύσουμε ακριβώς τι συμβαίνει σε ένα σύστημα RAG, από τη στιγμή που θα προσθέσετε ένα έγγραφο στο πότε ένας χρήστης παίρνει μια απάντηση.

## Φάση 1: Ευρεσιτεχνία (Προετοιμασία της Βάσης Γνώσης)

Πριν RAG μπορεί να ανακτήσει οτιδήποτε, θα πρέπει να αξιολογήσετε τη βάση γνώσεων σας. Αυτή είναι μια διαδικασία μιας φοράς (αν και μπορείτε να προσθέσετε νέα έγγραφα αργότερα).

```mermaid
flowchart TB
    A[Source Documents] -->|1. Extract Text| B[Text Extraction]
    B -->|2. Split into Chunks| C[Chunking Service]
    C -->|3. Generate Embeddings| D[Embedding Model]
    D -->|4. Store Vectors| E[Vector Database]

    B -.Metadata.-> E

    subgraph "Example: Blog Post"
        F["Understanding Docker: A containerization platform..."]
    end

    subgraph "Chunks"
        G["Chunk 1: Title + Intro"]
        H["Chunk 2: Benefits Section"]
    end

    subgraph "Embeddings"
        I["0.234, 0.891, 0.567, ..."]
        J["0.445, 0.123, 0.789, ..."]
    end

    F --> G
    F --> H
    G --> I
    H --> J

    style D stroke:#f9f,stroke-width:2px
    style E stroke:#bbf,stroke-width:2px
```

### Βήμα 1: Εξαγωγή κειμένου

Απόσπασμα απλού κειμένου από τα αρχικά σας έγγραφα.

- Markdown αρχεία (όπως αναρτήσεις blog μου)
- PDF (για τεκμηρίωση)
- HTML (για ξύσιμο ιστού)
- Αρχεία βάσης δεδομένων
- Emails, αρχεία καταγραφής συνομιλίας, κλπ.

**Παράδειγμα από το blog μου:**

```csharp
// From MarkdownRenderingService
public string ExtractPlainText(string markdown)
{
    // Remove code blocks
    var withoutCode = Regex.Replace(markdown, @"```[\s\S]*?```", "");

    // Convert markdown to plain text
    var document = Markdown.Parse(withoutCode);
    var plainText = document.ToPlainText();

    return plainText.Trim();
}
```

### Βήμα 2: Τσάνκινγκ

Δεν μπορείτε απλά να χωρίσετε στα όρια των παραγράφων - χρειάζεστε σημασιολογικά συνεκτικά κομμάτια.

**Γιατί το πνιγμό έχει σημασία:**

- LLMs έχουν συμβολικά όρια (παράθυρα κειμένου)
- Μικρότερα κομμάτια = ακριβέστερη ανάκτηση
- Αλλά τα κομμάτια πρέπει να περιέχουν αρκετά συμφραζόμενα για να έχουν νόημα

**Κακό κουδούνισμα:**

```
Chunk 1: "Docker is a containerization platform. It allows you"
Chunk 2: "to package applications with their dependencies. This"
Chunk 3: "ensures consistency across environments."
```

**Καλό κουδούνισμα:**

```
Chunk 1: "Docker is a containerization platform. It allows you to package applications with their dependencies. This ensures consistency across environments."

Chunk 2: "Benefits of Docker:
- Isolation: Each container runs in its own environment
- Portability: Containers run anywhere Docker is installed
- Efficiency: Lightweight compared to virtual machines"
```

**Παράδειγμα από τη σημασιολογική μου εφαρμογή αναζήτησης:**

```csharp
public class TextChunker
{
    private const int TargetChunkSize = 500; // ~500 words
    private const int ChunkOverlap = 50;     // 50 words overlap

    public List<Chunk> ChunkDocument(string text, string sourceId)
    {
        var chunks = new List<Chunk>();

        // Split on section boundaries first (## headers in markdown)
        var sections = SplitOnHeaders(text);

        foreach (var section in sections)
        {
            // If section is small enough, keep it whole
            if (section.WordCount < TargetChunkSize)
            {
                chunks.Add(new Chunk
                {
                    Text = section.Text,
                    SourceId = sourceId,
                    SectionHeader = section.Header
                });
            }
            else
            {
                // Split large sections on sentence boundaries
                var subChunks = SplitOnSentences(section.Text, TargetChunkSize, ChunkOverlap);
                chunks.AddRange(subChunks.Select(c => new Chunk
                {
                    Text = c,
                    SourceId = sourceId,
                    SectionHeader = section.Header
                }));
            }
        }

        return chunks;
    }
}
```

**Κοινές στρατηγικές πνιγμού:**

- **Σταθερό μέγεθος**: Απλό αλλά σπάει σημασιολογικά όρια
- **Με βάση την ποινή**: Σέβεται τη γραμματική αλλά μπορεί να είναι πολύ μικρή
- **Με βάση την παράγραφο**: Φυσικό αλλά μεταβλητό μέγεθος
- **Με βάση το τμήμα**: Καλύτερο για δομημένο περιεχόμενο (προτίμησή μου)
- **Συρόμενο παράθυρο με επικάλυψη**: Εξασφαλίζει ότι δεν χάνεται κανένα πλαίσιο στα όρια

### Βήμα 3: Δημιουργία ενσωμάτωσης

Μια ενσωμάτωση είναι ένα διάνυσμα (συστοιχία αριθμών) που αντιπροσωπεύει την έννοια του κειμένου.

**Βασική έννοια:** Παρόμοιες έννοιες → παρόμοιοι φορείς

```
"Docker container" → [0.234, -0.891, 0.567, ..., 0.123]
"containerization platform" → [0.221, -0.903, 0.534, ..., 0.119]
"apple fruit" → [0.891, 0.234, -0.567, ..., -0.789]
```

Οι δύο πρώτοι φορείς θα είναι "κοντά" στο διανυσματικό χώρο (υψηλή ομοιότητα συνημίτονου), ενώ ο τρίτος είναι μακριά.

**Πώς δημιουργούνται οι παρεμβολές:**
Μοντέρνα μοντέλα ενσωμάτωσης είναι νευρικά δίκτυα εκπαιδευμένα σε μαζικά σύνολα κειμένου για να μάθουν σημασιολογικές σχέσεις.

- **Όλα-MiniLM-L6-v2**: 384 διαστάσεις, γρήγορη, καλή ποιότητα (τι χρησιμοποιώ σε αυτό το blog)
- **text-embedding-3-small** (OpenAI): 1536 διαστάσεις, πολύ υψηλής ποιότητας
- **Βάση BGE**: 768 διαστάσεις, state-of-the-art open source

**Παράδειγμα από την υπηρεσία προσθήκης ONNX μου:**

```csharp
public async Task<float[]> GenerateEmbeddingAsync(string text)
{
    // Tokenize the input text
    var tokens = Tokenize(text);

    // Create input tensors for ONNX model
    var inputIds = CreateInputTensor(tokens);
    var attentionMask = CreateAttentionMaskTensor(tokens.Length);
    var tokenTypeIds = CreateTokenTypeIdsTensor(tokens.Length);

    // Run ONNX inference
    var inputs = new List<NamedOnnxValue>
    {
        NamedOnnxValue.CreateFromTensor("input_ids", inputIds),
        NamedOnnxValue.CreateFromTensor("attention_mask", attentionMask),
        NamedOnnxValue.CreateFromTensor("token_type_ids", tokenTypeIds)
    };

    using var results = _session.Run(inputs);

    // Extract the output (sentence embedding)
    var output = results.First().AsTensor<float>();
    var embedding = output.ToArray();

    // L2 normalize the vector for cosine similarity
    return NormalizeVector(embedding);
}
```

**Γιατί η ομαλοποίηση έχει σημασία:** Μετά την ομαλοποίηση L2, η ομοιότητα συνημίτονου γίνεται ένα απλό προϊόν κουκκίδων, κάνοντας την αναζήτηση πολύ γρηγορότερα.

### Βήμα 4: Κατάστημα σε βάση δεδομένων Vector

Σε αντίθεση με τις παραδοσιακές βάσεις δεδομένων που χρησιμοποιούν ερωτήματα SQL, οι διανυσματικές βάσεις δεδομένων χρησιμοποιούν αναζήτηση ομοιότητας.

**Βασικές πράξεις:**

- **Επάνω**: Προσθήκη ή ενημέρωση φορέα με μεταδεδομένα
- **Αναζήτηση**: Βρείτε K πιο παρόμοια διανυσματικά μέσα σε ένα διάνυσμα ερώτημα
- **Φίλτρο**: Συνδυάστε διανυσματική αναζήτηση με φίλτρα μεταδεδομένα

**Παράδειγμα εφαρμογής Qdrant:**

```csharp
public async Task IndexDocumentAsync(
    string id,
    float[] embedding,
    Dictionary<string, object> metadata)
{
    var point = new PointStruct
    {
        Id = new PointId { Uuid = id },
        Vectors = embedding,
        Payload =
        {
            ["title"] = metadata["title"],
            ["source"] = metadata["source"],
            ["chunk_index"] = metadata["chunk_index"],
            ["created_at"] = DateTime.UtcNow.ToString("O")
        }
    };

    await _client.UpsertAsync(
        collectionName: "blog_posts",
        points: new[] { point }
    );
}
```

**Δημοφιλείς βάσεις δεδομένων διάνυσμα:**

- **QdrantCity name (optional, probably does not need a translation)**: Γρήγορη, αυτο-ξεχωριστή, εξαιρετική υποστήριξη C# (η επιλογή μου)
- **pgvector**: Επέκταση PostgreSQL (μεγάλη αν χρησιμοποιείτε ήδη Postgres)
- **Πινκόνη**: Managed service (ακριβό αλλά καλό)
- **Υγρασία**: Πλούσιο χαρακτηριστικό, καλό για σύνθετα σχήματα
- **ChromaDBCity name (optional, probably does not need a translation)**: Python-εστιασμένη, ελαφριά

Θα εξερευνήσουμε τη δημιουργία αυτών των βάσεων δεδομένων σε επερχόμενα άρθρα.

## Φάση 2: Ανάκτηση (Εύρεση σχετικών πληροφοριών)

Όταν ένας χρήστης κάνει μια ερώτηση, το σύστημα RAG πρέπει να βρει τις πιο σχετικές πληροφορίες από τη βάση γνώσεων.

```mermaid
flowchart LR
    A["User Query:<br/>'How do I use Docker Compose?'"] --> B[Generate Query Embedding]
    B --> C["Query Vector:<br/>[0.445, -0.123, ...]"]
    C --> D[Vector Search]
    D --> E[Vector Database]
    E --> F[Top K Similar Chunks]
    F --> G["Results:<br/>1. Docker Compose Basics 0.92<br/>2. Multi-Container Setup 0.87<br/>3. Service Configuration 0.83"]

    style B stroke:#f9f,stroke-width:3px
    style D stroke:#bbf,stroke-width:3px
```

### Βήμα 1: Δημιουργία προσθήκης ερωτημάτων

Η ερώτηση του χρήστη μετατρέπεται σε ένα διάνυσμα με τη χρήση του **το ίδιο μοντέλο ενσωμάτωσης** Αυτό είναι κρίσιμο - διαφορετικά μοντέλα παράγουν ασύμβατους φορείς.

```csharp
public async Task<List<SearchResult>> SearchAsync(string query, int limit = 10)
{
    // Same embedding model used for indexing
    var queryEmbedding = await _embeddingService.GenerateEmbeddingAsync(query);

    // Search in vector store
    var results = await _vectorStoreService.SearchAsync(
        queryEmbedding,
        limit
    );

    return results;
}
```

### Βήμα 2: Αναζήτηση ομοιότητας

Η διανυσματική βάση δεδομένων υπολογίζει την ομοιότητα μεταξύ του φορέα έρευνας και όλων των αποθηκευμένων διανυσμάτων.

**Ομοιότητα συνημίτονου** (Το πιο δημοφιλές για ομαλοποιημένα διανυσματικά):

```
similarity = (A · B) / (||A|| × ||B||)
```

Εύρος: -1 έως 1 (υψηλότερα = πιο παρόμοια)

**Απόσταση Ευκλείδειας** (για μη φυσιολογικούς φορείς):

```
distance = sqrt(Σ(Ai - Bi)²)
```

Εύρος: 0 έως ↔ (κατώτερο = πιο παρόμοιο)

**Προϊόν κουκκίδας** (όταν οι φορείς είναι προ-κανονισμένοι):

```
similarity = A · B
```

Εύρος: -1 έως 1 (υψηλότερα = πιο παρόμοια)

**Παράδειγμα από την υπηρεσία Qdrant μου:**

```csharp
var searchResults = await _client.SearchAsync(
    collectionName: "blog_posts",
    vector: queryEmbedding,
    limit: (ulong)limit,
    scoreThreshold: 0.7f,  // Only return results with >70% similarity
    payloadSelector: true   // Include all metadata
);

return searchResults.Select(hit => new SearchResult
{
    Text = hit.Payload["text"].StringValue,
    Title = hit.Payload["title"].StringValue,
    Score = hit.Score,
    Source = hit.Payload["source"].StringValue
}).ToList();
```

### Βήμα 3: Επανάκτηση (προαιρετική αλλά συνιστώμενη)

Η αρχική ανάκτηση είναι γρήγορη αλλά κατά προσέγγιση. Η επαναφορά χρησιμοποιεί ένα πιο εξελιγμένο μοντέλο για να ξανασκοράρει τα κορυφαία αποτελέσματα K.

```mermaid
flowchart LR
    A[Vector Search:<br/>Top 50 Results] --> B[Reranking Model]
    B --> C[Reranked:<br/>Top 10 Results]

    style B stroke:#f9f,stroke-width:3px
```

**Γιατί η επανασύνδεση βοηθάει:**

- Γρήγορη ενσωμάτωση μοντέλων βελτιστοποιούν για την ταχύτητα, θυσιάζοντας κάποια ακρίβεια
- Τα συγκλονιστικά μοντέλα είναι πιο αργά αλλά πιο ακριβή
- Η προσέγγιση δύο σταδίων εξισορροπεί την ταχύτητα και την ποιότητα

**Παράδειγμα επανεγγραφής της εφαρμογής:**

```csharp
public async Task<List<SearchResult>> SearchWithRerankAsync(
    string query,
    int initialLimit = 50,
    int finalLimit = 10)
{
    // Stage 1: Fast vector search
    var candidates = await SearchAsync(query, initialLimit);

    // Stage 2: Precise reranking
    var rerankedResults = await _rerankingService.RerankAsync(
        query,
        candidates
    );

    return rerankedResults.Take(finalLimit).ToList();
}
```

## Φάση 3: Γενιά (Δημιουργία της Απάντησης)

Τώρα που έχουμε σχετικές πληροφορίες, τις δίνουμε στην LLM μαζί με την ερώτηση του χρήστη.

```mermaid
flowchart TB
    A[User Query] --> B[Retrieved Context 1]
    A --> C[Retrieved Context 2]
    A --> D[Retrieved Context 3]

    B --> E[Construct Prompt]
    C --> E
    D --> E
    A --> E

    E --> F["System: You are a helpful assistant...\n\nContext:\n1. Docker Compose allows...\n2. Services are defined...\n3. Volumes persist data...\n\nQuestion: How do I use Docker Compose?\n\nAnswer:"]

    F --> G[LLM]
    G --> H[Generated Answer with Citations]

    style E stroke:#f9f,stroke-width:2px
    style G stroke:#bbf,stroke-width:2px
```

### Βήμα 1: Άμεση Κατασκευή

Αυτό είναι όπου RAG γίνεται μια τέχνη. Θα πρέπει να δομήσει την ώθηση έτσι το LLM:

- Χρησιμοποιεί το παρεχόμενο πλαίσιο (όχι τις εσωτερικές του γνώσεις)
- Cite πηγές, όταν είναι δυνατόν
- Παραδέχεται όταν τα συμφραζόμενα δεν περιέχουν απάντηση
- Διατηρεί σταθερό τόνο/στυλ

**Παράδειγμα γρήγορου προτύπου από το σύστημα GPT του δικηγόρου μου:**

```csharp
public string BuildRAGPrompt(string query, List<SearchResult> context)
{
    var sb = new StringBuilder();

    sb.AppendLine("You are a technical writing assistant. Your task is to answer the user's question using ONLY the provided context from past blog posts.");
    sb.AppendLine();
    sb.AppendLine("CONTEXT:");
    sb.AppendLine("========");

    for (int i = 0; i < context.Count; i++)
    {
        sb.AppendLine($"[{i + 1}] {context[i].Title}");
        sb.AppendLine($"Source: {context[i].Source}");
        sb.AppendLine($"Content: {context[i].Text}");
        sb.AppendLine($"Relevance: {context[i].Score:P0}");
        sb.AppendLine();
    }

    sb.AppendLine("========");
    sb.AppendLine();
    sb.AppendLine("INSTRUCTIONS:");
    sb.AppendLine("- Answer the question using the provided context");
    sb.AppendLine("- Cite sources using [1], [2], etc.");
    sb.AppendLine("- If the context doesn't contain enough information, say so");
    sb.AppendLine("- Maintain the technical, practical tone of the blog");
    sb.AppendLine();
    sb.AppendLine($"QUESTION: {query}");
    sb.AppendLine();
    sb.AppendLine("ANSWER:");

    return sb.ToString();
}
```

### Βήμα 2: Το συμπέρασμα της LLM

Η κατασκευασμένη προτροπή πηγαίνει στο LLM για γενιά. Αυτό μπορεί να είναι:

- **Cloud API**: OpenAI, Anthropic Claude, Google PALM
- **Τοπικό μοντέλο**: Χρήση llama.cpp, ONNX Runtime, ή TorchSharp

**Παράδειγμα χρησιμοποιώντας τοπική LLM:**

```csharp
public async Task<string> GenerateResponseAsync(string prompt)
{
    var result = await _llamaSharp.InferAsync(prompt, new InferenceParams
    {
        Temperature = 0.7f,      // Creativity (0 = deterministic, 1 = creative)
        TopP = 0.9f,             // Nucleus sampling
        MaxTokens = 500,         // Response length limit
        StopSequences = new[] { "\n\n", "User:", "Question:" }
    });

    return result.Text.Trim();
}
```

**Οι βασικές παράμετροι εξηγούνται:**

- **Θερμοκρασία**: Ελέγχει την τυχαιότητα (0 = πάντα επιλέξτε το πιο πιθανό, 1 = δείγμα τυχαία)
- **Top P**: Nucleus δειγματοληψία - εξετάσει μόνο τα σημεία που αποτελούν κορυφαία P μάζα πιθανότητας
- **Μαξ ΤόκενςCity name (optional, probably does not need a translation)**: Μήκος απόκρισης ορίου
- **Σταματήστε τις ακολουθίες**: Πότε να σταματήσει η παραγωγή

### Βήμα 3: Μετά την Επεξεργασία

Μετά το LLM παράγει μια απάντηση, συχνά πρέπει να:

- Εκχύλισέ τα και μετατρέποντάς τα σε συνδέσμους
- Τμήματα κωδικών μορφοτύπου
- Προσθέστε μεταδεδομένα (πηγές, βαθμολογίες εμπιστοσύνης)
- Καταγραφή της αλληλεπίδρασης για αποσφαλμάτωση

**Παράδειγμα μετά την επεξεργασία:**

```csharp
public RAGResponse PostProcess(string llmOutput, List<SearchResult> sources)
{
    var response = new RAGResponse
    {
        Answer = llmOutput,
        Sources = new List<Source>()
    };

    // Extract citations like [1], [2]
    var citations = Regex.Matches(llmOutput, @"\[(\d+)\]");

    foreach (Match match in citations)
    {
        int index = int.Parse(match.Groups[1].Value) - 1;
        if (index >= 0 && index < sources.Count)
        {
            var source = sources[index];
            response.Sources.Add(new Source
            {
                Title = source.Title,
                Url = GenerateUrl(source.Source),
                RelevanceScore = source.Score
            });
        }
    }

    // Convert markdown citations to hyperlinks
    response.FormattedAnswer = Regex.Replace(
        llmOutput,
        @"\[(\d+)\]",
        m => {
            int index = int.Parse(m.Groups[1].Value) - 1;
            if (index >= 0 && index < sources.Count)
            {
                var url = GenerateUrl(sources[index].Source);
                return $"[[{m.Groups[1].Value}]]({url})";
            }
            return m.Value;
        }
    );

    return response;
}
```

# Κατανόηση Εσωτερικών LLM: Tokens, KV Cache, και το πλαίσιο Windows

Πριν προχωρήσουμε σε πρακτικές εφαρμογές, είναι απαραίτητο να κατανοήσουμε πώς λειτουργούν οι LLMs εσωτερικά. Αυτή η γνώση σας βοηθά να βελτιστοποιήσετε τα συστήματα RAG και να αποφύγετε τις κοινές παγίδες.

## Τι είναι τα Τόκενς;

Tokens είναι οι βασικές μονάδες που LLMs διαδικασία. Το κείμενο δεν τροφοδοτείται απευθείας σε μοντέλα - είναι για πρώτη φορά χωρισμένο σε μάρκες.

**Παράδειγμα σηματοδότησης:**

```
Input:  "Understanding Docker containers"
Tokens: ["Under", "standing", " Docker", " containers"]
```

Διαφορετικά μοντέλα χρησιμοποιούν διαφορετικές στρατηγικές σήμανσης:

- **Μοντέλα GPT**: Χρήση κωδικοποίησης Byte-Pair (BPE) με λεξιλόγιο ~50K
- **Claude**: Παρόμοια προσέγγιση BPE
- **Μοντέλα Llama**: SentencePiece markenization

**Γιατί η σήμανση έχει σημασία για τις ΚΓΠΕ:**

```csharp
public class TokenCounter
{
    // Rough approximation: 1 token ≈ 0.75 words (English)
    public int EstimateTokens(string text)
    {
        var wordCount = text.Split(' ', StringSplitOptions.RemoveEmptyEntries).Length;
        return (int)(wordCount / 0.75);
    }

    public int EstimateTokensAccurate(string text, ITokenizer tokenizer)
    {
        // Use actual tokenizer for precision
        return tokenizer.Encode(text).Count;
    }
}
```

**Όρια παραθύρου πλαισίου:**

- GPT-3.5: 16K μάρκες
- GPT-4: 8K-128K μάρκες (ανάλογα με την παραλλαγή)
- Claude 3.5 Sonnet: 200K μάρκες
- Λάμα 3: 8K μάρκες (αν και μπορεί να επεκταθεί)

Στα συστήματα RAG, πρέπει να ταιριάζετε:

```
Total tokens = System prompt + Retrieved context + User query + Response buffer
```

Εάν RAG σας ανακτήσει 10 έγγραφα των 500 μάρκων το καθένα, αυτό είναι 5.000 μάρκες μόνο για το πλαίσιο - πριν από την ερώτηση και την απάντηση!

**Πρακτικές ΚΓΠΕ συμβολική διαχείριση:**

```csharp
public class ContextWindowManager
{
    private readonly int _maxContextTokens;
    private readonly int _systemPromptTokens;
    private readonly int _responseBufferTokens;

    public ContextWindowManager(
        int totalContextWindow = 4096,
        int systemPromptTokens = 300,
        int responseBufferTokens = 500)
    {
        _maxContextTokens = totalContextWindow;
        _systemPromptTokens = systemPromptTokens;
        _responseBufferTokens = responseBufferTokens;
    }

    public List<SearchResult> FitContextInWindow(
        List<SearchResult> retrievedDocs,
        string query)
    {
        var queryTokens = EstimateTokens(query);

        // Available tokens for retrieved context
        var availableForContext = _maxContextTokens
            - _systemPromptTokens
            - queryTokens
            - _responseBufferTokens;

        var selectedDocs = new List<SearchResult>();
        var currentTokens = 0;

        foreach (var doc in retrievedDocs.OrderByDescending(d => d.Score))
        {
            var docTokens = EstimateTokens(doc.Text);

            if (currentTokens + docTokens <= availableForContext)
            {
                selectedDocs.Add(doc);
                currentTokens += docTokens;
            }
            else
            {
                break; // Context window full
            }
        }

        return selectedDocs;
    }

    private int EstimateTokens(string text)
    {
        // Rule of thumb: 1 token ≈ 4 characters
        return text.Length / 4;
    }
}
```

## The KV Cache: Το μυστικό όπλο της LLM

Όταν ένα LLM παράγει κείμενο, δεν επεξεργάζεται τα πάντα από το μηδέν για κάθε μάρκα. **Key-Value (KV) cache** να θυμάμαι τι έχει ήδη υπολογίσει.

### Πώς λειτουργούν οι Μετασχηματιστές (απλουστευμένοι)

Μετασχηματιστές χρησιμοποιούν έναν μηχανισμό "προσοχής" όπου κάθε σύμβολο "προσκολλάται" (βλέπει) όλες τις προηγούμενες μάρκες για να κατανοήσουν τα συμφραζόμενα.

```mermaid
flowchart TB
    subgraph "Generation Step 1: 'Docker'"
        A1[Input: 'Docker'] --> B1[Compute K,V for 'Docker']
        B1 --> C1[Store in KV Cache]
        C1 --> D1[Generate: 'is']
    end

    subgraph "Generation Step 2: 'is'"
        A2[Input: 'is'] --> B2[Compute K,V for 'is']
        B2 --> C2[Store in KV Cache]
        C2 --> E2[Retrieve KV for 'Docker']
        E2 --> F2[Attend: 'is' to 'Docker']
        F2 --> D2[Generate: 'a']
    end

    subgraph "Generation Step 3: 'a'"
        A3[Input: 'a'] --> B3[Compute K,V for 'a']
        B3 --> C3[Store in KV Cache]
        C3 --> E3[Retrieve KV for 'Docker', 'is']
        E3 --> F3[Attend: 'a' to all previous]
        F3 --> D3[Generate: 'container']
    end

    D1 --> A2
    D2 --> A3

    style C1 stroke:#f9f,stroke-width:3px
    style C2 stroke:#f9f,stroke-width:3px
    style C3 stroke:#f9f,stroke-width:3px
```

**Χωρίς KV cache:**

- Βήμα 1: Διαδικασία 1 μάρκα → O(1)
- Βήμα 2: Διαδικασία 2 σημεία από το μηδέν → O(2)
- Βήμα 3: Διαδικασία 3 σημεία από το μηδέν → O(3)
- Σύνολο: O(1 + 2 + 3 + ... + N) = O(N2)

**Με KV cache:**

- Βήμα 1: Process 1 point, cache K,V → O(1)
- Βήμα 2: Διαδικασία 1 νέο σύμβολο, επαναχρησιμοποίηση cached K,V → O(1)
- Βήμα 3: Διαδικασία 1 νέο σύμβολο, επαναχρησιμοποίηση cached K,V → O(1)
- Σύνολο: O(N)

Αυτό κάνει τη γενιά **δραματικά γρηγορότερα** - τη διαφορά μεταξύ 10 μάρκων/δευτερόλεπτο και 100 μάρκων/δευτερόλεπτο.

### Η δομή KV Cache Δέντρο

Το KV cache σχηματίζει ένα "δέντρο" λόγω του πώς η προσοχή λειτουργεί στους μετασχηματιστές. Κάθε στρώμα στο μοντέλο έχει το δικό του K,V matrices.

```mermaid
graph TB
    A[Input Tokens:<br/>'What is Docker?'] --> B[Layer 1 Attention]
    B --> C[Layer 1 KV Cache]

    B --> D[Layer 2 Attention]
    D --> E[Layer 2 KV Cache]

    D --> F[Layer 3 Attention]
    F --> G[Layer 3 KV Cache]

    F --> H[... up to Layer N]
    H --> I[Output: 'Docker is']

    C -.Key-Value pairs<br/>for all input tokens.-> C
    E -.Key-Value pairs<br/>for all input tokens.-> E
    G -.Key-Value pairs<br/>for all input tokens.-> G

    style C stroke:#bbf,stroke-width:2px
    style E stroke:#bbf,stroke-width:2px
    style G stroke:#bbf,stroke-width:2px
```

**Κάθε στρώμα αποθηκεύει:**

- **Κλειδιά (Κ)**: Αντιπροσωπείες που χρησιμοποιούνται για τον υπολογισμό σκορ προσοχής
- **Τιμές (V)**: Αντιπροσωπείες που αναμειγνύονται με βάση την προσοχή

Για μοντέλο με:

- 32 στρώματα
- 4096 κρυφές διαστάσεις
- 32 κεφάλια προσοχής
- Παράθυρο πλαισίου 8K

Η κρύπτη KV για μία ακολουθία είναι:

```
2 (K and V) × 32 layers × 4096 dimensions × 8192 tokens × 2 bytes (FP16)
≈ 4.3 GB of VRAM!
```

Αυτός είναι ο λόγος για τον οποίο μακριά παράθυρα πλαίσιο είναι έντονη μνήμη.

### KV Cache σε RAG Systems

Τα συστήματα RAG μπορούν να αξιοποιήσουν τη βελτιστοποίηση της μνήμης KV με έξυπνους τρόπους:

**Απρόσκοπτη αποθήκευση** (υποστηριγμένο από κάποιους APIs όπως ο Ανθρωπιστής Claude):

```csharp
public class CachedRAGService
{
    // System prompt and retrieved context can be cached!
    public async Task<string> GenerateWithCachedContextAsync(
        string systemPrompt,          // Cached
        List<SearchResult> context,   // Cached
        string userQuery)             // Not cached, changes each time
    {
        var contextText = FormatContext(context);

        // The KV cache for systemPrompt + contextText is reused across queries
        var prompt = $@"
{systemPrompt}

CONTEXT:
{contextText}

QUERY: {userQuery}

ANSWER:";

        return await _llm.GenerateAsync(prompt, useCaching: true);
    }
}
```

**Γιατί αυτό είναι ισχυρό:**

- Πρώτη ερώτηση: Computes KV cache για το σύστημα άμεσης + context (slow)
- Επακόλουθε ερωτήσεις με το ίδιο πλαίσιο: Reuses cached KV (10x γρηγορότερα!)
- Μόνο το τμήμα ερωτημάτων του χρήστη χρειάζεται φρέσκο υπολογισμό

**Πρακτικό παράδειγμα:**

```
Query 1: "How do I use Docker?" → 2 seconds (no cache)
Query 2: "What are Docker benefits?" → 0.2 seconds (cache hit!)
Query 3: "Docker vs VMs?" → 0.2 seconds (cache hit!)
```

Και τα τρία ερωτήματα χρησιμοποιούν το ίδιο ανακτημένο πλαίσιο, οπότε η μνήμη KV για αυτό το πλαίσιο ξαναχρησιμοποιείται.

## Όρια αναφοράς και στρατηγική ΚΓΠΕ

Κατανόηση μάρκων και KV cache ενημερώνει τις αποφάσεις αρχιτεκτονικής RAG σας:

### 1. Μέγεθος κυνήγι

Μικρότερα κομμάτια = πιο ακριβής ανάκτηση, αλλά πιο πάνω:

```csharp
// Option A: Small chunks (200 tokens each)
// Retrieve 20 chunks = 4,000 tokens
// Pro: Very precise, only relevant info
// Con: More KV cache entries, slower attention

// Option B: Larger chunks (500 tokens each)
// Retrieve 8 chunks = 4,000 tokens
// Pro: Better context coherence, fewer KV entries
// Con: More noise, less precise

public class AdaptiveChunker
{
    public int DetermineChunkSize(int contextWindowSize)
    {
        if (contextWindowSize <= 4096)
            return 200; // Small chunks for limited windows

        if (contextWindowSize <= 16384)
            return 500; // Medium chunks

        return 1000; // Large chunks for big windows
    }
}
```

### 2. Πλαίσιο χρήσης παραθύρου

Μην μεγιστοποιήσετε το παράθυρο πλαίσιο - αφήστε χώρο για την γενιά:

```csharp
public class SafeContextManager
{
    public int GetSafeContextLimit(int totalContextWindow)
    {
        // Use only 75% for input, reserve 25% for output
        return (int)(totalContextWindow * 0.75);
    }

    // Example: 4K model
    // Total: 4096 tokens
    // Safe input: 3072 tokens
    // Reserved for output: 1024 tokens
}
```

### 3. Multi-Turn RAG Συνομιλίες

Σε chatbots, η ιστορία συζήτησης μεγαλώνει με κάθε στροφή:

```
Turn 1:
System + Context + Query1 = 3000 tokens
Response1 = 300 tokens
Total: 3300 tokens

Turn 2:
System + Context + Query1 + Response1 + Query2 = 3650 tokens
Response2 = 300 tokens
Total: 3950 tokens

Turn 3:
System + Context + Query1 + Response1 + Query2 + Response2 + Query3 = 4250 tokens
ERROR: Context window exceeded!
```

**Λύση: Συρόμενο παράθυρο με re-retrieval**

```csharp
public class ConversationalRAG
{
    private readonly int _maxHistoryTokens = 1000;

    public async Task<string> ChatAsync(
        List<ConversationTurn> history,
        string newQuery)
    {
        // Re-retrieve context based on current query
        var context = await RetrieveContextAsync(newQuery);

        // Keep only recent conversation history
        var relevantHistory = TrimHistory(history, _maxHistoryTokens);

        var prompt = BuildPrompt(context, relevantHistory, newQuery);

        return await _llm.GenerateAsync(prompt);
    }

    private List<ConversationTurn> TrimHistory(
        List<ConversationTurn> history,
        int maxTokens)
    {
        var trimmed = new List<ConversationTurn>();
        var currentTokens = 0;

        // Keep most recent turns
        foreach (var turn in history.Reverse())
        {
            var turnTokens = EstimateTokens(turn.Query) + EstimateTokens(turn.Response);

            if (currentTokens + turnTokens <= maxTokens)
            {
                trimmed.Insert(0, turn);
                currentTokens += turnTokens;
            }
            else
            {
                break;
            }
        }

        return trimmed;
    }
}
```

### 4. Βελτιστοποίηση κόστους Token

API-based LLMs χρέωση ανά μάρκα. RAG μπορεί να εκραγεί το κόστος, εάν δεν είναι προσεκτικοί:

```csharp
public class CostAwareRAG
{
    // OpenAI GPT-4 pricing (example):
    // Input: $0.03 per 1K tokens
    // Output: $0.06 per 1K tokens

    public decimal EstimateQueryCost(
        int systemPromptTokens,
        int retrievedContextTokens,
        int queryTokens,
        int expectedResponseTokens)
    {
        var inputTokens = systemPromptTokens + retrievedContextTokens + queryTokens;
        var outputTokens = expectedResponseTokens;

        var inputCost = (inputTokens / 1000m) * 0.03m;
        var outputCost = (outputTokens / 1000m) * 0.06m;

        return inputCost + outputCost;
    }

    // Example:
    // System: 300 tokens
    // Context: 3000 tokens (10 retrieved docs)
    // Query: 50 tokens
    // Response: 500 tokens
    //
    // Cost = ((300 + 3000 + 50) / 1000 * 0.03) + (500 / 1000 * 0.06)
    //      = (3350 / 1000 * 0.03) + (500 / 1000 * 0.06)
    //      = $0.1005 + $0.03
    //      = $0.1305 per query
    //
    // At 1000 queries/day = $130/day = $3,900/month!
}
```

**Στρατηγικές μείωσης του κόστους:**

1. Ανακτήστε λιγότερα και καλύτερα έγγραφα
2. Χρησιμοποιήστε άμεση caching (Ανθρωπικός Claude: 90% φθηνότερα για τα cached μάρκες)
3. Χρησιμοποιήστε φθηνότερα μοντέλα για την επαναβαθμολόγηση, ακριβό για την τελική γενιά
4. Πλαίσιο συμπίεσης με τη χρήση στροφαλοθαλάμου

## Οραματισμός της Πλήρης ροή RAG με Tokens

Εδώ είναι πώς οι μάρκες, KV cache, και RAG ταιριάζουν μεταξύ τους:

```mermaid
flowchart TB
    A[User Query:<br/>'How does Docker work?'<br/>≈ 12 tokens] --> B[Generate Query Embedding]

    B --> C[Vector Search]
    C --> D[Retrieved Docs:<br/>5 docs × 500 tokens<br/>= 2,500 tokens]

    D --> E[Construct Prompt]
    A --> E

    E --> F["Complete Prompt:<br/>System: 300 tokens<br/>Context: 2,500 tokens<br/>Query: 12 tokens<br/>Total: 2,812 tokens"]

    F --> G[Tokenize Prompt]
    G --> H["Token IDs:<br/>[245, 1034, 8829, ...]<br/>2,812 token IDs"]

    H --> I[LLM Layer 1]
    I --> J[Compute K,V]
    J --> K[KV Cache Layer 1:<br/>2,812 K,V pairs]

    I --> L[LLM Layer 2]
    L --> M[Compute K,V]
    M --> N[KV Cache Layer 2:<br/>2,812 K,V pairs]

    L --> O[... Layers 3-32]
    O --> P[Generate Token 1: 'Docker']

    P --> Q[Add to KV Cache]
    Q --> R[Generate Token 2: 'is']
    R --> S[Add to KV Cache]
    S --> T[... until completion]

    T --> U["Response: 'Docker is a containerization platform...'<br/>≈ 400 tokens"]

    style K stroke:#f9f,stroke-width:2px
    style N stroke:#f9f,stroke-width:2px
    style Q stroke:#bbf,stroke-width:2px
    style S stroke:#bbf,stroke-width:2px
```

**Βασικές γνώσεις:**

1. **Σημάδια εισόδου** (2,812) υποβάλλονται σε επεξεργασία μία φορά για την κατασκευή αρχικής μνήμης KV
2. **Γενιά** συμβαίνει ένα σημείο τη φορά, ξαναχρησιμοποιώντας την κρύπτη KV
3. **Κάθε νέο σύμβολο** προσθέτει στο KV cache για μελλοντικές μάρκες για να παρακολουθήσετε
4. **Σύνολο VRAM** απαιτείται = Μοντέλο βάρους + KV cache για όλες τις μάρκες
5. **Μεγαλύτερο πλαίσιο** = μεγαλύτερη μνήμη KV = μεγαλύτερη VRAM

## Πρακτικές επιπτώσεις για τις ΚΓΠΕ

Κατανόηση σημάτων και KV cache οδηγεί σε καλύτερο σχεδιασμό RAG:

**1. Προσυγκρούστε και cache κοινά συμφραζόμενα:**

```csharp
// Cache KV for frequently used system prompts + static context
var cachedSystemContext = await _llm.PrecomputeKVCache(systemPrompt + staticContext);

// Reuse for each query (much faster)
foreach (var query in userQueries)
{
    var response = await _llm.GenerateAsync(query, reuseKVCache: cachedSystemContext);
}
```

**2. Βελτιστοποιήστε τα όρια κομματιών:**

```csharp
// Bad: Arbitrary 500-character chunks
var chunks = text.Chunk(500);

// Good: Chunk on sentence boundaries, measure in tokens
public List<string> ChunkByTokens(string text, int maxTokensPerChunk)
{
    var sentences = SplitIntoSentences(text);
    var chunks = new List<string>();
    var currentChunk = new StringBuilder();
    var currentTokens = 0;

    foreach (var sentence in sentences)
    {
        var sentenceTokens = EstimateTokens(sentence);

        if (currentTokens + sentenceTokens > maxTokensPerChunk && currentTokens > 0)
        {
            chunks.Add(currentChunk.ToString());
            currentChunk.Clear();
            currentTokens = 0;
        }

        currentChunk.Append(sentence).Append(" ");
        currentTokens += sentenceTokens;
    }

    if (currentTokens > 0)
        chunks.Add(currentChunk.ToString());

    return chunks;
}
```

**3. Παρακολουθήστε τη χρήση σημάδι στην παραγωγή:**

```csharp
public class RAGTelemetry
{
    public void LogRAGQuery(
        string query,
        List<SearchResult> retrievedDocs,
        string response)
    {
        var queryTokens = EstimateTokens(query);
        var contextTokens = retrievedDocs.Sum(d => EstimateTokens(d.Text));
        var responseTokens = EstimateTokens(response);
        var totalTokens = queryTokens + contextTokens + responseTokens;

        _logger.LogInformation(
            "RAG Query: {Query} | Context: {ContextTokens} tokens from {DocCount} docs | " +
            "Response: {ResponseTokens} tokens | Total: {TotalTokens} tokens",
            query, contextTokens, retrievedDocs.Count, responseTokens, totalTokens
        );

        // Alert if approaching context limit
        if (totalTokens > _maxTokens * 0.9)
        {
            _logger.LogWarning("Approaching token limit: {TotalTokens}/{MaxTokens}",
                totalTokens, _maxTokens);
        }
    }
}
```

# Συμπέρασμα: Αρχιτεκτονική Mastery

Έχουμε καλύψει την πλήρη τεχνική αρχιτεκτονική των συστημάτων RAG:

**Φάση 1: Ευρεσιτεχνία**

- Εξαγωγή κειμένου από διάφορες πηγές
- Στρατηγικές για το κυνήγι (με βάση το τμήμα, με βάση τις φράσεις, με επικάλυψη)
- Παραγωγή ενσωμάτωσης (ONNX, API υπηρεσίες)
- Αποθήκευση διάνυσμα (Qdrant, pgvector, Pinecone)

**Φάση 2: Ανάκτηση**

- Ερωτηματογράφηση (ίδιο μοντέλο με ευρετηρίαση!)
- Αναζήτηση ομοιότητας (cosine, euclidean, dot product)
- Προαιρετική επανασύνδεση για καλύτερη ακρίβεια
- Φίλτρο μεταδεδομένα για εκλεπτυσμένα αποτελέσματα

**Φάση 3: Γενιά**

- Απρόσκοπτη κατασκευή (συνθήκη + οδηγίες + ερώτημα)
- Συνέδρια LLM (θερμοκρασία, κορυφή-p, max μάρκες)
- Μετά την επεξεργασία (εκχύλιση, μορφοποίηση)

**LLM Internals**

- Tokens: Οι θεμελιώδεις μονάδες (όχι χαρακτήρες!)
- KV Cache: Γιατί η γενιά είναι γρήγορη (γραμμική, όχι τετραδική)
- Παράθυρα πλαισίου: Διαχείριση συμβολικών ορίων στις ΚΓΠΕ
- Βελτιστοποίηση κόστους: Caching, συμπίεση, έξυπνη ανάκτηση

**Βασικές τεχνικές γνώσεις:**

1. **Το ίδιο μοντέλο ενσωμάτωσης** για ευρετηρίαση και ανάκτηση (κρίσιμη!)
2. **Το κυνήγι μετράει περισσότερο απ' όσο νομίζεις.** - διατηρεί σημασιολογική συνοχή
3. **Η αύξηση βελτιώνει την ακρίβεια** σε σχέση με το κόστος της καθυστέρησης
4. **Η διαχείριση του Token είναι απαραίτητη** - εκτίμηση πριν από την ερώτηση
5. **Η KV caching καθιστά τις ΚΓΠΕ εφικτές** - τους γρήγορους υπολογισμούς επαναχρησιμοποίησης
6. **Τα παράθυρα πλαισίου γεμίζουν γρήγορα** - 10 docs × 500 μάρκες = 5K μάρκες

# Συνέχεια στο Μέρος 3: Οι ΚΓΠΕ στην Πρακτική

Τώρα καταλαβαίνεις. **πώς λειτουργεί το RAG** Πώς μπορείτε πραγματικά να οικοδομήσετε αυτά τα συστήματα; Ποιες προκλήσεις θα αντιμετωπίσετε; Ποιες προηγμένες τεχνικές μπορείτε να χρησιμοποιήσετε;

Το **[Μέρος 3: Οι ΚΓΠΕ στην πράξη](/blog/rag-practical-applications)**, μετακινούμαστε από την αρχιτεκτονική στην εφαρμογή:

**Αληθινές εφαρμογές:**

- Σχετικές δημοσιεύσεις σύσταση σε αυτό το blog
- Σημαντική αναζήτηση blog
- Κατασκευή βοηθού συγγραφής "Δικηγόρος GPT"

**Κοινές προκλήσεις και λύσεις:**

- Στρατηγικές για το κυνήγι που διατηρούν το πλαίσιο
- Βελτίωση της ποιότητας ενσωμάτωσης για τον τομέα σας
- Διαχείριση παραθύρων πλαισίου δυναμικά
- Εμποδίζοντας τις παραισθήσεις παρά το γεγονός ότι έχουν συμφραζόμενα
- Κρατήστε ενημερωμένο το ευρετήριο σας

**Προηγμένες τεχνικές:**

- Υποθετικά Έντυπα Εγκαταστάσεων Εγγράφων (HyDE)
- Αυτοκατασκευή με φίλτρα LLM-parsed
- ΠΟΛΛΑΠΛΟΙ ΛΟΓΑΡΙΑΣΜΟΙ ΓΙΑ ΠΕΡΙΣΣΟΤΕΡΑ ΑΠΟΤΕΛΕΣΜΑΤΑ
- Πλαίσιο συμπίεσης για τη μείωση της χρήσης σημάτων
- Πολυεπιχειρησιακές ΚΓΠΕ για περίπλοκα ερωτήματα
- Μακροχρόνια μνήμη συζητήσεων

**Ξεκινάμε:**

- Σχέδιο εφαρμογής της εβδομάδας ανά εβδομάδα
- Πρακτικά παραδείγματα κώδικα
- Στρατηγικές βελτιστοποίησης
- Όταν ΔΕΝ χρησιμοποιούνται ΚΓΠΕ

**[Συνέχεια στο Μέρος 3: Οι ΚΓΠΕ στην Πρακτική →](/blog/rag-practical-applications)**

## Πόροι

**Ιδρυτικές εφημερίδες:**

- [Ανάκτηση-Αυξανόμενη Γενιά για Γνώση-Εντατικά καθήκοντα NLP](https://arxiv.org/abs/2005.11401) - Το πρωτότυπο χαρτί RAG
- [Αναζήτηση Passage Dense για απάντηση σε ερωτήσεις Open-Domain](https://arxiv.org/abs/2004.04906) - Ίδρυμα DPR
- [Η προσοχή είναι το μόνο που χρειάζεστε.](https://arxiv.org/abs/1706.03762) - Μετασχηματιστές

**Εργαλεία και πλαίσια:**

- [QdrantCity name (optional, probably does not need a translation)](https://qdrant.tech/) - Βάση δεδομένων Vector
- [ONNX Runtime](https://onnxruntime.ai/) - Τοπικές εντυπώσεις
- [LLamaSharpCity name (optional, probably does not need a translation)](https://github.com/SciSharp/LLamaSharp) - Τοπικό συμπέρασμα LLM
- [Μετασχηματιστές ποινή](https://www.sbert.net/) - Μοντέλα ενσωμάτωσης

**Περαιτέρω ανάγνωση:**

- [Ανθρωπιστική: Περιεκτική Ανάκτηση](https://www.anthropic.com/index/contextual-retrieval) - Προηγμένες τεχνικές

**Πλοήγηση σειράς:**

- [Μέρος 1: Προέλευση και Θεμελιώδη Στοιχεία](/blog/rag-primer) - Ιστορία και κίνητρο
- **Μέρος δεύτερο: Αρχιτεκτονική και Εσωτερικά** (αυτό το άρθρο) - Τεχνική βαθιά κατάδυση
- [Μέρος 3: Οι ΚΓΠΕ στην πράξη](/blog/rag-practical-applications) - Κατασκευή πραγματικών συστημάτων

**[Συνέχεια στο Μέρος 3 →](/blog/rag-practical-applications)**