Back to "RAG 建筑和内部结构"

This is a viewer only at the moment see the article on how this works.

To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk

This is a preview from the server running through my markdig pipeline

AI AI-Article LLM Machine Learning RAG Semantic Search

RAG 建筑和内部结构

Saturday, 22 November 2025

内 部分#1: 获取相关信息@,}然后用它来生成回应 . 现在我们深入技术架构

一. 导言导言 导言 导言 导言

📖系列导航 这是RAG系列的 2Q部分

如果你读了部分1, 我建议从那里开始去理解"MS K2"

  • ARAG是什么以及它为何重要
  • 从关键字搜索到语义理解的历史
  • RAG 与 fine-调试和其他方法

本篇文章假定你理解这些基本要点,并着重阐述 技术架构@,实施细节 @,和LLM内部.

RAG如何工作 : 完整的图片

从您添加文档到用户得到答案的瞬间, RAG system ,\ \ }

阶段=1: 索引_( 准备知识库 200)})

在RAG 取得任何文件前 @, 您需要为您的知识库索引@. 这是 1\ -time process *%(}虽然您可以在稍后添加新文档 @MS K4

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:

从您的源文档中解开纯文本@ . @ this could be MS K1

  • Markdown 文件 @(}就像我的博客poss@)
  • 文档的 PDFs @ (}% 1}
  • HTML & ( for web scrapping @) @%
  • 数据库记录
  • 电子邮件@,聊天logs @,等等#.

我博客“:”的例子

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

这是大多数 RAG 执行失败的地方 @ . @ 您可以\ '} 您需要语义一致的块@ MS K3 *

为何要拼凑块?

  • LLMs 有象征性限制@ ( @context window@)
  • 更精确的检索@ =}
  • 但块块必须包含足够的上下文, 才能有意义

错误的块块@ : @

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"

我的语义搜索执行中的示例@: @

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

共同的堆积策略 :

  • 固定的@ - @ size: 简单但打破语义界限
  • 句号@- @% basin: 尊重语法,但可能太小
  • 基于 ' @-" 段落:自然但可变大小
  • 基于 @-最佳结构化内容 @(_我的偏好#)
  • 存在重叠的滑动窗口: 保证在边界时不会丢失上下文

生成嵌入式

嵌入式是使语义搜索成为可能的魔力@.}嵌入器是数字中的矢量 @(arry ),它代表着文字的意思

密钥概念@: @% 相似的矢量

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

前两个矢量将是 载体空间中的 @" @close"}*(#high连带相似 *),}而第三个是遥远的@.

如何生成嵌入@: @% 现代嵌入模型是接受大规模文本数据集培训的神经网络,以学习语义关系@.大众模式 @:

  • {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2款@: @ @ 384 @ 维度@ MS K2 fastQ,}好的品质 @ MPK4 @ 我在这个博客上使用什么?
  • 文本@ - @ embedding_ MS K1}小 ( OpenAI): MS K2 维度, 非常高品质
  • BGE+-基: MS K1 维度 I, 状态-of}- NSK5 开放源

我的 ONNX 嵌入服务中的示例 @:

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}正常化后..., 共生相似性变成简单的点产物... , 让搜索速度更快

向量数据库中的仓库

优化矢量数据库,以储存和搜索高-数向量@.}不同于使用 SQL 查询的传统数据库{,矢量数据基使用相似性搜索MS K3}

密钥操作@: @%

  • 上层以元数据添加或更新矢量
  • 搜索\ : 找到 K 最相似的矢量到查询矢量
  • 筛选过滤器: 将矢量搜索与元数据过滤器相结合

Qdrant 实施示例@: @%

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

大众矢量数据库:

  • 解冻支持 @(_MY choice@)
  • pgvictor 变量: PostgreSQL 扩展 {(} @ msk1 great 如果您已经使用 postgres)}
  • 松松: 管理服务 @( 昂贵但好~)
  • 断断-=YTET -伊甸园字幕组=- 翻译:
  • 色谱DB:Python-聚焦@,轻量级

我们将探讨在即将发表的文章中建立这些数据库。

搜索相关信息 )

当用户问一个问题,时,RAG系统需要从知识库中找到最相关的信息.

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

生成查询嵌入

用户='}%s 问题被转换为矢量 相同的嵌入模型 用于索引的 @ . @ 这是关键 {-} 不同的模型产生不兼容矢量.}

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 @ to @1 @ @ @ (_higher#=}更相似的) @

欧几里地距离 ( 用于未正常化的矢量 @):

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

范围@: @ @ 0# to @∞ @ @ @ (_lower @=}更相似的@MS K5 @

点产品 @(}当矢量是预置的 - @% 正式化@):#

similarity = A · B

范围@: @ @ -1 @ to @1 @ @ @ (_higher#=}更相似的) @

我的Qdrant 服务中的示例@: @

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 结果@ .

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

为何要重新排名帮助我?

  • 优化快速嵌入模型以优化速度@, @ 牺牲某些精度
  • 排名模型慢,但更准确
  • Two- 阶段办法平衡速度和质量

重排执行的示例

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

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

即时建造

这是RAG 成为艺术的Art. 您需要构造提示, 所以 LLMMS K1

  • 用户提供的上下文@(}不是内部知识 @)
  • 可能时点选来源
  • 当上下文包含一个答案时会承认@' @t
  • 保持一致的基调@/}风格

我的律师GPT系统中的示例提示模板@:

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, 用于生成“.” 这可以是‘:’

  • AIPI 云尘@:OpenAI, 人类克劳德MS K2Google PaLM
  • 当地模式@ :_ 使用喇叭 @ .cpp,}ONNX 运行时间@,或火炬沙尔普

使用本地LLM:}示例

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

关键参数解释@: @%

  • 温度:Q 控制随机性@(0Q @=Q 总是选择最有可能的 @MS K3} @1Q @#=Z 随机抽样_){
  • 顶P:Nucleus 采样 - 只考虑构成顶级P概率质量的符号
  • 最大调当量限制回复长度
  • 停止序列“: 何时停止生产”

步骤 @ 3:_ post@ MS K1_ 处理

在LLM 生成响应后 @,}我们经常需要到@:

  • 提取引文并将其转换为链接
  • 格式化代码块
  • 添加元数据@(}来源 @,}信任分数=)
  • 记录调试的交互

@-_process@:}后加示例

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 内部( I) @ : @ Tokens@ MS K1 @ kV Cache},和上下文窗口

在我们转向实际应用之前, 关键在于了解LLMS如何在内部工作@.}这种知识有助于优化RAG系统并避免常见的陷阱_.}

什么是Tokens ?

Tokens 是LLMs process . text is @'}直接反馈给模型的基本单位 {-} it'}它们首先细分为符号=%.

示例符号@: @%

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

不同模式使用不同的代谢策略

  • GPT模型: 使用 Byte-Pair Encoding MS K2BP) 和 @~50K字典
  • 克劳德类似BPE方法
  • Llama 模型@ :_ 句式Piece 象征性化

为什么象征性化对RAG:很重要?

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: 8 K-128K 标牌( 取决于变量MSC4
  • Claude @3.5_Sonnet@:} @ 200_K 神令
  • Llama @3: @ @ 8} K 标牌@(_ 虽然可以展期 @ I){

在RAG systems,中, 您必须适配@ :

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

如果您的 RAG 在查询和回复前为上下文 {-} 获取“ 10” 文档@ MS K1 标记每个,, k3 * @ 5,000 _ 符号仅用于 profession { msk5\ }\ MSC6\ {

实际的RAG象征性管理@:

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

KV Cache : LLM='秘密武器

当一个 LLM 生成文本时 ,} 它会从零开始对每个牌子进行全部的重新处理\ I.}它使用 a 密钥@ - @ Value @ MS K1 @ KVQ)} 缓存 来记住它已经计算了什么"."

变换者如何工作 @ ( @ 简化@ MS K1 @

变换器使用“ " @ avention”" 机制来理解上下文@ MS K6}

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 缓存@ : @%

  • 步骤“1:”进程 “1”符号“MS K2”O(1)
  • 步骤 @2:Q 处理\ 2Q 从头到尾的标记@→Q O(2)}
  • 步骤 @3:Q 处理\ 3Q 从头到尾的标记@→Q O(3)}
    • MS K7 NSK8 N @= OQ(N²)

使用 KV 缓存@ : @

  • 步骤 @ 1: process { 1 matter\ MS K2} 缓存@ ,V @ MPK4 O(1)
  • 步骤 @ 2: process % 1 newmaking , 再利用缓存 K,V _→ O(1)
  • 步骤 @ 3: process % 1 newmaking , 再利用缓存 K,V _→ O(1)
  • -=YTET -伊甸园字幕组=- 翻译:

这代人 飞快的更快 -+#10@signals @/_QII和{100>之间的差别

KV 缓存树结构

KV 缓存组成一个“ "” tree"" , 因为对变压器的注意力是如何起作用的。 “ .” 在模型中的每一层都有自己的 K,+V 矩阵@ MSC4}

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

每层商店 :

  • 密钥 @ ( @ K)}: 用于计算注意分数的表示式
  • 数值 @(#V})#: 基于关注的混合代表

用于使用“:”的模型

  • 32 层
  • 4096 隐藏的维度
  • @32# 关注的负责人
  • 8K 上下文窗口

一个序列的 KV 缓存为 @ : @

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

这就是为什么长的上下文窗口是内存@- @ tracting .}%

RAG 系统中的 KV 缓存

RAG 系统可以用聪明的方式利用 KV 缓存优化@:

即时缓存 “(”得到一些AIP的支持,

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

为何如此强大?

  • 首个查詢: 計算系統提示的 KV 快取@ + 上下文 @ (slow @ MPK3}
  • 随后以相同上下文查询@: 重新使用缓存的 KV @(10}#x apprepeciate !)
  • 只有用户查询部分需要新计算

实例::

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 缓存被重新使用 d.

火力限制和RAG战略

获取信件和 KV 缓存, 通知您的 RAG 架构决定@: @% 1

* 1. 振动大小

小块@ = @ 更精确的检索@ I,} 但更多高空@ MPK2

// 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.上下文窗口的利用

最多从上下文窗口@- @leave room for singer_:}请勿离开房间

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
}

-=YTET -伊甸园字幕组=- 翻译:

在聊天室里 MSKK0 每转一圈,对话历史都会不断增长

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 的解决方案@ :滑落窗口

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. Tokon 成本优化

API=-以百万计的LLMs收费,如果不小心的话,RAG可爆炸成本*:

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. 获取更少@, @ better}%SK1_已排序的文档
  2. 使用便捷的缓存标记@ ( @ Anthropic Claude_ :} < MS K2 价格更便宜 {)}
  3. 使用更便宜的模型,在最后一代中再用成本昂贵的模式
  4. 使用汇总压缩环境

将全RAG与 Toks 一同流动的视觉化

这里的'@ ,KV缓存 @ MS K2和RAG如何匹配\ :

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 缓存中, 用于未来待处理的标记
  4. 记录和档案管理共计 需要所有标记的 KV 缓存@ = @ model weights
  5. 长期 最大 KV 缓存@ = @ 更多 VRAM

对ARAG的实际影响

获取标识和 KV 缓存会改进RAG 设计@: @

1. pre- 计算并缓存共同背景@:

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

优化块边框@: @

// 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. 监控在生产中的象征性使用@:

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

结论:- 建筑精准

我们覆盖了RAG系统的完整技术架构 :

阶段1: 索引化

  • 从各种来源提取的文本
  • 整顿策略 @(_section-}基于#,}句子@-_based{,}与重迭)}
  • 嵌入式生成 (ONNXMS K1 API 服务)
  • 矢量存储 @ (drant, pgvectorMS K2pineconone})

*阶段 2:检索

  • 查询是否以索引方式嵌入 @( @same 模式@ !)
  • 近似性搜索 @(cosine,eccilidean_,doproduction@)
  • 提高精确度的可选重新排序
  • 改进结果的元数据过滤

生产阶段

  • 即时构造 @ (_ context {+} 指令@ MS K2} 查询)}
  • 最大代号@)
  • 后 - programme {(}\ 引用提取,} 格式@)

LLM 内部

  • Tokens: 基本单位 @ (not字符@!)
  • KV Cache:# 为什么下一代速度很快 @ ( @ linear @ I,} 不是二次曲线@ MSC3 @
  • 上下文窗口@: 管理 RAG 的批号限制
  • 成本优化@: @ caching,}压缩 @ ,# 智能检索

关键技术见解:*

  1. 相同的嵌入模型 用于索引和检索@( @jectric_!)}(% 1)
  2. 比你想的要重要得多 保持语义一致性
  3. 排级提高精确度 以延时费用计算
  4. 名声管理至关重要 查询前的 - @ 估计
  5. KV 缓存使RAG变得可行 -再利用快速计算
  6. 上下文窗口快速填充 - @10 @ docs #×500#标语@=5#K象征性

继续运行到 & 3: @ RAG 在实际操作中的部件

你现在明白了 RAG是如何工作的 在技术层面 .}但理论只能让你到现在为止... ...你如何实际建立这些系统?

内 内容:3:@,#我们从建筑到实施#:

真实的-#世界应用程序 @:}

  • 这个博客上的相关文章建议
  • 语义博客搜索
  • GPT " 写作助理

共同的挑战和解决办法

  • 保持环境的启动战略
  • 改进您域域的嵌入质量
  • 动态管理环境窗口
  • 尽管有背景,但防止幻觉
  • 正在不断更新您的索引@- @to @-_date

先进技术

  • 假想文档嵌入@ (_ HyDE})
  • 使用 LLM - paraceed 过滤器自定义
  • 用于取得全面成果的多-Qquery RAG
  • 用于减少象征性使用的上下文压缩
  • 用于复杂查询的多端-Hop RAG
  • LongM- 定期对话记忆

开始:

  • 周计划执行计划
  • 实用守则实例
  • 优化战略
  • 不使用 RAG 时

继续执行“3:”和“RAG”部分

资源资源资源 资源和资源资源资源

基础文件

工具与框架@: @%

继续读取@: @%

系列导航:

继续到 & M3 @ →

logo

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