RAG 建筑和内部结构
内 部分#1: 获取相关信息@,}然后用它来生成回应 . 现在我们深入技术架构
一. 导言导言 导言 导言 导言
📖系列导航 这是RAG系列的 2Q部分
- 第一部分:起源和基本 以及核心概念。
- 建筑和内部 @( @ this article research@)}在技术上深入潜入RAG的操作方式
- 内容:3: 建立真正的系统 挑战 和先进技术
- “4a :ONNX”“& Qdrant 履行” - CPU-友好语义搜索基金会
- 操作中的语义搜索 -typeahead,混合搜索@,和UI
- @ 5:_ 混合搜索@ MS K1\ 自动 @ MASK2 @ Indexing 生产一体化模式
- 6: 图表集 - 用于 proposal_- 水平理解的知识图表
如果你读了部分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!
}
降低成本战略
- 获取更少@, @ better}%SK1_已排序的文档
- 使用便捷的缓存标记@ ( @ Anthropic Claude_ :} < MS K2 价格更便宜 {)}
- 使用更便宜的模型,在最后一代中再用成本昂贵的模式
- 使用汇总压缩环境
将全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
密钥洞见@: @%
- 输入符号 @(2,812) 被处理过一次以建立初始 KV 缓存
- 产生 重复使用 KV 缓存
- 每个新标志 添加到 KV 缓存中, 用于未来待处理的标记
- 记录和档案管理共计 需要所有标记的 KV 缓存@ = @ model weights
- 长期 最大 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,}压缩 @ ,# 智能检索
关键技术见解:*
- 相同的嵌入模型 用于索引和检索@( @jectric_!)}(% 1)
- 比你想的要重要得多 保持语义一致性
- 排级提高精确度 以延时费用计算
- 名声管理至关重要 查询前的 - @ 估计
- KV 缓存使RAG变得可行 -再利用快速计算
- 上下文窗口快速填充 - @10 @ docs #×500#标语@=5#K象征性
继续运行到 & 3: @ RAG 在实际操作中的部件
你现在明白了 RAG是如何工作的 在技术层面 .}但理论只能让你到现在为止... ...你如何实际建立这些系统?
内 内容:3:@,#我们从建筑到实施#:
真实的-#世界应用程序 @:}
- 这个博客上的相关文章建议
- 语义博客搜索
- GPT " 写作助理
共同的挑战和解决办法
- 保持环境的启动战略
- 改进您域域的嵌入质量
- 动态管理环境窗口
- 尽管有背景,但防止幻觉
- 正在不断更新您的索引@- @to @-_date
先进技术
- 假想文档嵌入@ (_ HyDE})
- 使用 LLM - paraceed 过滤器自定义
- 用于取得全面成果的多-Qquery RAG
- 用于减少象征性使用的上下文压缩
- 用于复杂查询的多端-Hop RAG
- LongM- 定期对话记忆
开始:
- 周计划执行计划
- 实用守则实例
- 优化战略
- 不使用 RAG 时
资源资源资源 资源和资源资源资源
基础文件
- 重新获取@ - @ 启动生成知识 < NLP 强化NLP任务 > 原始的RAG文件
- 用于 OpenQ- @Domay 问答的高级通道检索器 -DPR基金会
- 你们需要的注意力 - 变形器
工具与框架@: @%
继续读取@: @%
- 人类学:}上下文检索 *- 先进技术
系列导航:
- 第一部分:起源和基本 历史和动机
- 建筑和内部 @( @ this article resident develop (技术深潜)
- 内容:3: 建立真正的系统