用于执行器的 RAG @:混合搜索和自动索引
一. 导言导言 导言 导言 导言
📖+RAG系列的一部分 这是生产集成模式 :
- RAG 起源和基本要点 “-”什么嵌入是“,”,为什么它们重要?
- RAG 建筑和内部 @ -_ Chunking,}代号@,}矢量数据库
- 内容:3: 建立完整的RAG系统
- “4a :ONNX”“& Qdrant 履行” - CPU-友好语义搜索基金会
- 操作中的语义搜索 -typeahead,混合搜索@,和UI组件
- @ 5:_ 混合搜索@ MS K1\ 自动 @ MASK2 @ Indexing 生产一体化模式
- 6: 图表集 - 用于 proposal_- 水平理解的知识图表
内 第一部分:4_a,и硂暗膀 : ONNX 嵌入cm Qudrant . 第一部分#4b,+#我们覆盖了搜索UI和混合搜索执行@.+}现在We'+%1将使它制作-Q备妥 自动自动索引化 @( @Zzero@-#touch 内容更新 <)> 通过文件系统观察}.{
双世界最佳混合搜索@:_Best
语义搜索非常有力,但传统的全文搜索在确切的短语和技术术语方面仍然出色。 解决方案#? 双用@. @
为何要混为一谈 不同的方法有不同的长处
- PostgreSQL 全@ - @Text (覆盖于此): 确切匹配@, 技术术语 @,Boolean运算符
- 语义矢量搜索:意指上下文 @, 语境@,}同义词{,}与概念相关的内容
相互融合 (RRF)
我们用 相互排名融合 组合多个搜索源的结果@: @%
flowchart TB
A[User Query: 'docker containers'] --> B[PostgreSQL Full-Text Search]
A --> C[Semantic Vector Search]
B --> D["Results:<br/>1. 'Docker Basics' (rank 1)<br/>2. 'Containerizing Apps' (rank 2)<br/>3. 'Docker Compose' (rank 3)"]
C --> E["Results:<br/>1. 'Containerizing Apps' (rank 1)<br/>2. 'Kubernetes Guide' (rank 2)<br/>3. 'Docker Basics' (rank 3)"]
D --> F[RRF Algorithm]
E --> F
F --> G["Combined Results:<br/>1. 'Containerizing Apps'<br/> (1/61 + 1/62 = 0.0328)<br/>2. 'Docker Basics'<br/> (1/61 + 1/63 = 0.0322)<br/>3. 'Docker Compose'<br/> (1/63 = 0.0159)"]
style A stroke:#10b981,stroke-width:2px
style B stroke:#3b82f6,stroke-width:2px
style C stroke:#6366f1,stroke-width:2px
style D stroke:#3b82f6,stroke-width:2px
style E stroke:#6366f1,stroke-width:2px
style F stroke:#ec4899,stroke-width:4px
style G stroke:#8b5cf6,stroke-width:2px
RRF 公式: score = Σ(1 / (k + rank))
k-=YTET -伊甸园字幕组=- 翻译:rank此搜索方法中的 = 位置@'}结果- 成果: 两者兼 每个来源加起来都得分
RRF为什么工作?
- 调解同样的结果是两个来源的得分都更高。
- 公平公正没有搜索方法能不公平地支配一切
- 简单化: 不需要复杂的调试
执行 执行情况
public class HybridSearchService : IHybridSearchService
{
private readonly ISemanticSearchService _semanticSearchService;
private const int RrfConstant = 60;
public async Task<List<SearchResult>> SearchAsync(
string query,
string language = "en",
int limit = 10,
CancellationToken cancellationToken = default)
{
// Execute both searches in parallel
var semanticResults = await _semanticSearchService.SearchAsync(
query, limit * 2, cancellationToken);
// Filter by language and apply RRF
var filteredResults = semanticResults
.Where(r => r.Language == language)
.ToList();
return ApplyReciprocalRankFusion(filteredResults)
.Take(limit)
.ToList();
}
private List<SearchResult> ApplyReciprocalRankFusion(List<SearchResult> results)
{
var rrfScores = new Dictionary<string, RrfScore>();
for (int i = 0; i < results.Count; i++)
{
var result = results[i];
var key = $"{result.Slug}_{result.Language}";
if (!rrfScores.ContainsKey(key))
rrfScores[key] = new RrfScore { Result = result };
// RRF formula: 1 / (k + rank)
rrfScores[key].Score += 1.0 / (RrfConstant + i + 1);
}
return rrfScores.Values
.OrderByDescending(x => x.Score)
.Select(x => x.Result)
.ToList();
}
}
注:: 这里显示的语义搜索只@ . @ in producting,}同时执行 PostgreSQL full-}文本搜索,并将这些结果纳入 RRF 计算=.}
融入
如果您已经实施了 PostgreSQL 完整@ - @ text search @ MS K2所覆盖的添加语义搜索为直截了当@: @
// Program.cs
services.AddSemanticSearch(configuration);
services.AddSingleton<IHybridSearchService, HybridSearchService>();
[HttpGet("search/hybrid")]
public async Task<IActionResult> HybridSearch(string query, string language = "en")
{
var results = await _hybridSearchService.SearchAsync(query, language);
return PartialView("_SearchResults", results);
}
使用文件系统监视器自动索引
最强大的特征 : 自动自动索引化@. 保存一个博客 post@, it's 即刻可搜索\ -}没有手动干预 *.
如何运作
flowchart TB
A[Save Markdown File] --> B[FileSystemWatcher Detects Change]
B --> C{File in Main Directory?}
C -->|Yes| D[Save to Database]
C -->|No| E[Save to Database Only]
D --> F[Create BlogPostDocument]
F --> G[Generate Embedding via ONNX]
G --> H[Store in Qdrant]
H --> I[Post Searchable Immediately]
style A stroke:#10b981,stroke-width:2px
style B stroke:#f59e0b,stroke-width:2px
style C stroke:#ec4899,stroke-width:3px
style D stroke:#3b82f6,stroke-width:2px
style E stroke:#6b7280,stroke-width:2px
style F stroke:#8b5cf6,stroke-width:2px
style G stroke:#6366f1,stroke-width:3px
style H stroke:#ef4444,stroke-width:2px
style I stroke:#10b981,stroke-width:2px
关键设计决定 仅在 主标记按下目录* ,* 不是子目录 translated/, drafts/, comments/).}这样可以保持搜索索引的清洁性
文件监视器集成
博客已经有一个 MarkdownDirectoryWatcherService我们将其扩展至触发语义索引 :
// In MarkdownDirectoryWatcherService.cs
private async Task OnChangedAsync(WaitForChangedResult e)
{
if (e.Name == null) return;
await retryPolicy.ExecuteAsync(async () =>
{
var savedModel = await blogService.SavePost(slug, language, markdown);
// Index ONLY if file is in main directory (no path separators in name)
if (!e.Name.Contains(Path.DirectorySeparatorChar) &&
!e.Name.Contains(Path.AltDirectorySeparatorChar))
{
await IndexPostForSemanticSearchAsync(scope, savedModel, language);
}
});
}
private async Task IndexPostForSemanticSearchAsync(
IServiceScope scope,
BlogPostDto post,
string language)
{
var semanticSearchService = scope.ServiceProvider.GetService<ISemanticSearchService>();
if (semanticSearchService == null) return; // Not configured
var document = new BlogPostDocument
{
Id = $"{post.Slug}_{language}",
Slug = post.Slug,
Title = post.Title,
Content = post.PlainTextContent,
Language = language,
Categories = post.Categories?.ToList() ?? new List<string>(),
PublishedDate = post.PublishedDate
};
await semanticSearchService.IndexPostAsync(document);
_logger.LogInformation("Indexed {Slug} ({Language}) in semantic search", post.Slug, language);
}
处理删除
当一个文章被删除时 , n从语义索引中删除\ MPK1\ {
private async Task OnDeletedAsync(WaitForChangedResult e)
{
await blogService.Delete(slug, language);
// Delete from semantic search ONLY if file was in main directory
if (!e.Name.Contains(Path.DirectorySeparatorChar) &&
!e.Name.Contains(Path.AltDirectorySeparatorChar))
{
var semanticSearchService = scope.ServiceProvider.GetService<ISemanticSearchService>();
await semanticSearchService?.DeletePostAsync(slug, language);
}
}
初始索引编制背景服务
启动时@,}在 Qdrant @: 中尚未设置现有职位的背景服务索引
flowchart TB
A[Application Starts] --> B[Wait 10 seconds]
B --> C[Initialize Semantic Search]
C --> D{Model Exists?}
D -->|No| E[Download from Hugging Face]
D -->|Yes| F[Load ONNX Model]
E --> F
F --> G[Scan Main Markdown Directory]
G --> H{For Each .md File}
H --> I[Compute Content Hash]
I --> J{Hash Changed?}
J -->|Yes| K[Generate Embedding]
J -->|No| L[Skip - Already Indexed]
K --> M[Store in Qdrant]
M --> H
L --> H
H -->|Done| N[Indexing Complete]
style A stroke:#10b981,stroke-width:2px
style C stroke:#6366f1,stroke-width:2px
style E stroke:#f59e0b,stroke-width:2px
style F stroke:#6366f1,stroke-width:3px
style G stroke:#8b5cf6,stroke-width:2px
style J stroke:#ec4899,stroke-width:3px
style K stroke:#6366f1,stroke-width:2px
style M stroke:#ef4444,stroke-width:2px
style N stroke:#10b981,stroke-width:2px
public class SemanticIndexingBackgroundService : BackgroundService
{
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
// Wait for app to be ready
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
// Initialize (downloads model if needed)
await _semanticSearchService.InitializeAsync(stoppingToken);
// Get all posts from main directory only
var markdownFiles = Directory.GetFiles(
_markdownConfig.MarkdownPath,
"*.md",
SearchOption.TopDirectoryOnly); // NOT subdirectories
foreach (var file in markdownFiles)
{
var needsIndexing = await _semanticSearchService.NeedsReindexingAsync(
slug, language, contentHash, stoppingToken);
if (needsIndexing)
await _semanticSearchService.IndexPostAsync(document, stoppingToken);
}
}
}
这确保了#:
- Lazy 模型装货 - 初用下载@, 没有屏蔽启动
- 递增指数化 @- 仅新增@/ 已更改的日志 re-indexed *(via内容 hash{)
- 仅主目录 - 草稿和翻译文件
我们所建造的
跨部分: @4+A#,+4+B=,}和@5,#我们现在有#MS K5#
- ✅ CPU-友好语义搜索 - 不需要 GPU
- ✅ 相关职位的发现 --Smantic 相似的内容
- ✅ 自然语言搜索 @ - 查找意谓@, 不只是关键字
- ✅ 混合查找 最精致的语义
- ✅ 自动索引化 @ -_ Zero@-_touch内容更新
- ✅ 自 己#- 主持 - 您的数据留在您的服务器上
未来增强#:
- @ - @ awarSearch - 来自特定类别的促动效果
- 多语种嵌入器 特定嵌入模型
- 开放搜索船一体化 在混合混合物中添加 OpenSearch看我的《开放搜索》)
结论结论
这将完成 RAG-风格语义搜索@.}与 第一部分:4_a -=YTET -伊甸园字幕组=- 翻译: 第一部分#4b (@searchUI),}您需要的一切 都能够将智能搜索添加到您的 & MS K2 @NET 应用程序中 {-} 完全运行在 CPU},}(以零附加成本计算)>.}
继续学习
- RAG 部件 QZ1: 起源和基本原理 嵌入背后的理论
- RAG 部件 & 2: @% 建筑和内部 深入潜入RAG系统
- RAG 部分3:实用应用 - 完成使用LLM集成的 RAG
- “4a :ONNX”“& Qdrant 履行” -Foundation @:嵌入和矢量存储
- 操作中的语义搜索 -typeahead,混合搜索@,和UI
- 使用 PostgreSQL 的完整- @ text 搜索 混合搜索的文本一面
资源资源资源 资源和资源资源资源
Qdrant &矢量数据库
- 带有 Qdrant 的自定义“-” 矢量数据库 @ -_ 深潜到 Qdrant 概念中@ MS K1} HNSW 索引 *,}\ 过滤 * ,} 和 C#}客户端
- Qdrant 混合搜索 @-Qdrant'}本地混合支援
混合搜索
- 相互排名合并文件 RRF算法
文件系统监视
完整代码
所有代码都可用于 @ : @ 多数为平质网,
Mostlylucid.SemanticSearch/-核心语义搜索库Mostlylucid/Blog/WatcherService/带有语义索引的 -文件监视器