कार्यान्वयनकर्ताओं के लिए RAG: हाइब्रिड खोज और स्वचालित अनुक्रमण
परिचय
RAG श्रृंखला का एक भाग यह भाग है
- भाग 1: RAG मूल और बुनियादी - क्या एम्बेडिंग हैं
- भाग 2: RAG वास्तुकला और आंतरिक -
- भाग 3: RAG व्यावहारिक में - पूर्ण RAG प्रणालियों का निर्माण
- भाग 4a: ONNX & Qdrant कार्यान्वयन - सीपीयू
- भाग 4b: क्रिया में सेमेटिक खोज - टाइपेहेडM SK1 हाइब्रिड खोज, और UI अवयव
- भाग 5: हाइब्रिड खोज (इस अनुच्छेदM SK1 - उत्पादन एकीकृत नमूने
- भाग 6: GraphRAG - कोर्स के लिए ज्ञान ग्राफ्स
में भाग 4a, हमने बुनियादी बनायी है भाग 4b, हम खोज UI और हाइब्रिड खोज क्रियान्वयन को शामिल किया है स्वचालित अनुक्रमण FileSystemWatcher के माध्यम से (zero-touch सामग्री अद्यतन
हाइब्रिड खोज
अर्थ खोज शक्तिशाली है लेकिन पारंपरिक पूर्ण समाधान दोनों का उपयोग करें
क्यों हाइब्रिड विभिन्न दृष्टिकोणों में भिन्न शक्तियां हैं
- पोस्टग्रेएसक्यूएल पूरा (यहां ढका हुआ): सही मिलते हैं
- सांकेतिक भेक्टर खोज: अर्थ
पारस्परिक रैंक फ्यूजन
हम उपयोग करते हैं पारस्परिक रैंक फ्यूजन बहुविध खोज स्रोतों से परिणामों को जोड़ने के लिए
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
आरआरएफ सूत्र score = Σ(1 / (k + rank))
kआरंभिक रैंकों पर प्रभाव डालने से रोकने के लिए स्थिर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();
}
}
नोट यह सिर्फ सेमेंटिक खोज को दिखाता है. उत्पादन में, PostgreSQL पूरा निष्पादित करता है- पाठ खोज समांतर रूप से और आरआरएफ गणना में उन परिणामों को शामिल करें
एकीकरण
यदि आप 'PostgreSQL पूर्ण को पहले से ही लागू किया हैजैसा कि यहाँ वर्णित हैसेमेटिक खोज जोड़ना सीधा है
// 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);
}
फ़ाइल सिस्टम वॉकर के साथ स्वचालित अनुक्रमण
सबसे शक्तिशाली विशेषता स्वचालित अनुक्रमणएक ब्लॉग पोस्ट सहेजें
यह कैसे काम करता है
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);
}
मिटाएँ
जब कोई पोस्ट मिटाया जाता है, तो इसे अर्थ अनुक्रमणिका से हटाएँ
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 में अभी तक नहीं मौजूदा पदों को indexes
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);
}
}
}
यह सुनिश्चित करता है
- लचीला मॉडल लोडिंग - पहली बार उपयोग पर डाउनलोड्स
- वृद्धिशील सूचकांकन केवल नए
- सिर्फ मुख्य निर्देशिका - ड्राफ्ट और अनुवादित फ़ाइलें अनुक्रमणिका को दूषित नहीं करते
क्या हम निर्माण किया है
सभी भागों में 4aM SK1 4b, और 5, हम अब है
- ✅ सीपीयू-सेमेटिक खोज - कोई GPU आवश्यक नहीं
- ✅ संबंधित पोस्ट खोज - Semantically similar content
- ✅ प्राकृतिक भाषा खोज - अर्थ से ढूंढें
- ✅ हाइब्रिड खोज - सर्वोत्कृष्ट अर्थात्मक
- ✅ स्वचालित अनुक्रमण - Zero-touch सामग्री अद्यतन
- ✅ स्वयं - आपका डेटा आपके सर्वर पर रहता है
भावी सुधार
- श्रेणी-अभिज्ञ खोज - विशिष्ट श्रेणियों से बौस्ट परिणाम
- बहुभाषीय सम्मिलन भाषा
- ओपन सर्च इंटीग्रेशन हाइब्रिड मिश्रण में ओपन सर्च जोड़ेंमेरी ओपन सर्च लेख देखें)
निष्कर्ष
यह RAG के व्यावहारिक कार्यान्वयन को पूरा करता है भाग 4a (उत्तरकरण भाग 4b खोज UI ), आपके नेट अनुप्रयोग में स्मार्ट खोज को जोड़ने के लिए आपके पास सब कुछ है
सीखना जारी रखें
- RAG भाग 1: मूल और बुनियादी - एम्बेडिंग के पीछे सिद्धांत
- RAG भाग 2: वास्तुकला और आंतरिक - RAG प्रणालियों में गहराई से डुबोना
- RAG भाग 3: व्यवहारिक अनुप्रयोग - LLM एकीकरण के साथ पूरा RAG
- भाग 4a: ONNX & Qdrant कार्यान्वयन - फाउंडेशन
- भाग 4b: क्रिया में सेमेटिक खोज - टाइपेहेड, हाइब्रिड खोज
- पूर्ण-PostgreSQL के साथ पाठ खोज हाइब्रिड खोज का पूरा पाठ पक्ष
संसाधन
Qdrant & भेक्टर डाटाबेस
- Qdrant के साथ Self-Hosted भेक्टर डाटाबेस - Qdrant संकल्पनाओं में गहराई से डुबोने के लिए
- क्वैंड हाइब्रिड खोजQuery - Qdrant
हाइब्रिड खोज
- पारस्परिक रैंक फ्यूजन कागज - आरआरएफ एल्गोरिथ्म
फ़ाइल सिस्टम निगरानी
- FileSystemWatcher वर्ग -
- पृष्ठभूमि सेवा वर्ग ASP में होस्ट किए गए सेवाएँ
पूर्ण कोड
सभी कोड ‘:’ पर उपलब्ध है github.comM SK1scottgal/mostlylucidweb
Mostlylucid.SemanticSearch/Core semantic search libraryMostlylucid/Blog/WatcherService/- शब्दानुक्रमण अनुक्रमणिका के साथ फ़ाइल द्रष्टा