DocSummarizer Part 5 - lucidRAG: MultiM SK3Document RAG Web Application (العربية (Arabic))

DocSummarizer Part 5 - lucidRAG: MultiM SK3Document RAG Web Application

Thursday, 01 January 2026

//

8 minute read

هذا هو الجزء 5 من سلسلة DocSummarizer , و هو ' أيضاً الذروة من سلسلة GraphRAG و سلسلة البحث الدلالي. نحن' ندمج كل شيء في تطبيق على الانترنت قابل للتطبيق

🚨🚨 أRTICLE PREVIEW 🚨🚨 مازلت أعمل على بعض اللاصق وإضافة الخصائص. لكن الجوهر قد تم عمله و يعمل بشكل جيدM SK3 نتوقع تحديثات خلال الأسابيع القليلة القادمةMSC4 ستكون في lucidRAGMST5comMst6 سأقوم بإضافة شاشات الشاشة هنا بمجرد أن أخرج التصميمM st8

الفكرة الأساسية لبناء بنية تحتية RAG هي استخدامها لشيء حقيقي

خلال الأسابيع القليلة الماضية

  • DocSummarizer - تحليل الوثائق, تجزئة لغويةM SK2 مداخلات أونكس
  • GraphRAG - إستخراج الكائناتM SK1 رسومات المعرفة, كشف المجتمع
  • البحث الدلالي - BM25 + بحث بيرت هجين مع الإنصهار RRF

الآن نربطهم معاً في lucidRAG - تطبيق ويب مستقل لعدة -إجابات على أسئلة مستندة مع رسم بياني للمعرفة

الموقع الإلكتروني: lucidrag.com | المصدر: GitHub

ما يفعله lucidRAG

تحمّل الملفات. أسأل الأسئلة. تحصل على الإجابات مع الاستشهادات ورسم بياني للمعرفة يوضح كيف ترتبط المفاهيمM SK2

الخصائص الرئيسية:

  • رفع وثيقة متعددة- مع سحب-and-drop
  • RAG الوكيميائي - تفكيك الطلبية ونفسها- اصلاح ( مقيدةM SK3 ديناميكية في البنيةMSC4 دورة حياة الطلبية الواحدة)
  • رسم بياني للمعرفة - شاهد علاقات الكيان
  • وجهة نظر الأدلة - جملة-إقتباسات المصدر على مستوى
  • نشر مستقل - واحد يمكن تنفيذه أو دوكر

قيود التصميم:

  • لا يعتمد السحاب على التصنيف
  • المعالجة الإفتراضية المسبقة (التقاط , الدمج | , | استخراج الكائنات |)
  • يمكن إعادة بناء حالة الفيكتور من مستندات المصدر
  • LLMs المستخدمة فقط لتحليل الإجابة على الأدلة المستخرجة

في أي نقطة لا تستخدم الـ LLMs لتجزئة .

لماذا تدمج البحث فيكتوري + رسمات المعرفة?

البحث الفيكتوري لوحده يكسر بعض أنواع الشؤال:

نوع الសំណួរ مشكلة البحث فيكتوري حل الرسوم
متقاطعة-وثيقة " كيف يرتبط X بالـ YM SK4 ♫ ترابط الكيانات عبر الملاحظات
الكيان- مركزي " ماذا عن الدوكرM SK4 ♫ تمرير الرسم البياني من الكيان МSK6
ملخصات عالمية "المواضيع الرئيسية?" ♫ الكشف المجتمعي МSK5

يستعمل lucidRAG كلاً من الـ: و , و الرسوم البيانية . و الأسئلة البيانية هي عمق- محدودة( اعظمي" ♫2" متداخلة) و مخصصة للقيام بالبحث في المستجدات لمنع التنقل دون حدود على المجسمات الكبيرة

نظرة عامة على العمارة

طبقة التطبيق ثلاثة مشاريع قمنا ببنائها بالفعل تدفق Stylo - إشارة

lucidRAG
├── Controllers/Api/    # REST endpoints
├── Services/           # Business logic
│   ├── DocumentProcessingService   # Wraps DocSummarizer
│   ├── EntityGraphService          # Wraps GraphRAG
│   └── Background/                 # Async queue processing (StyloFlow waves)
└── Views/              # HTMX + Alpine.js UI

لماذا StyloFlow? بدلاً من خطوط أنابيب موزعة بدقة , كل مرحلة المعالجة هي " موجه ," التي تبعث إشارات , . الموجات تعمل عندما تتطابق ظروف التنبيه ., تسمح بالتنفيذ المتوازن . StyloFlow:Signal-Driven Workflow Orchestration من أجل تفاصيل التنفيذ.

خط المعالجة

عندما تقوم بتحميل مستندة , تتدفق خلال ثلاث مراحل :

المرحلة 1: تحميل و الاصطفاء

النقطة النهائية للتحميل تتحقق من الملف , تقوم بحساب هاش المحتوى لفك النسخ , وتصطفه لعمليات الخلفية

public async Task<Guid> QueueDocumentAsync(Stream fileStream, string fileName)
{
    // Compute hash to detect duplicates
    var contentHash = ComputeHash(fileStream);

    var existing = await _db.Documents
        .FirstOrDefaultAsync(d => d.ContentHash == contentHash);
    if (existing != null)
        return existing.Id; // Already processed

الفكرة الرئيسية: نخترق أولاً, نحفظ لاحقاً . هذا يمنع هدر وقت المعالجة عند تحميلات متكررةM SK3

    // Save to disk, create DB record
    var docId = Guid.NewGuid();
    await SaveFileToDiskAsync(fileStream, docId, fileName);

    // Queue for background processing
    await _queue.EnqueueAsync(new DocumentProcessingJob(docId, filePath));

    return docId;
}

المسرح 2: الطباعة والداخل

المعالج الخلفي يلتقط الوثائق المصطفة ويشغلها من خلال DocSummarizer:

var result = await _summarizer.SummarizeFileAsync(job.FilePath, progressChannel);

هذه الخطة الواحدة تفعل الكثير من العمل (انظر أجزاء DocSummarizer 1):

  • Parse the document structure (PDF , DOCX, MarkdownM SK3
  • تقسم إلى أجزاء سيمانسية فيما يخص الرؤوس
  • صنع مداخلات أونكس لكل قطعة
  • تخزين ال vectors في DuckDB مع HNSW إحصاء

المرحلة 3: استخراج الكائنات

بعد تجزئة , نستخرج الكائنات باستخدام GraphRAG ' المقاربة الهيuristics

var segments = await _vectorStore.GetDocumentSegmentsAsync(documentId);
var entityResult = await _entityGraph.ExtractAndStoreEntitiesAsync(documentId, segments);

هذا يستخدم تقييم IDF و الإشارات الهيكلية بدلاً من إتصالات -chunk LLM - أنظر GraphRAG Part 2 للتفصيل.

القنوات المحدودة للضغط الخلفي

التنفيذ البديهي سوف يستخدم صفوف غير محدودة

private readonly Channel<DocumentProcessingJob> _queue =
    Channel.CreateBounded<DocumentProcessingJob>(new BoundedChannelOptions(100)
    {
        FullMode = BoundedChannelFullMode.Wait
    });

عندما يملأ الصفوف, Wait يقف نظام الكتابة الجديدة حتى يتم فتح المساحة. نضيف وقتاً متأخراً بحيث يحصل المستخدم على خطأ واضح بدلاً من أن يعلق

using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(ct);
timeoutCts.CancelAfter(TimeSpan.FromMinutes(5));

try {
    await _queue.Writer.WriteAsync(job, timeoutCts.Token);
} catch (OperationCanceledException) when (!ct.IsCancellationRequested) {
    throw new InvalidOperationException("Queue full. Try again later.");
}

Per- أوقات استراحة مستندة

الوثائق الكبيرة يمكن أن تستغرق دقائق من المعالجة.

while (!stoppingToken.IsCancellationRequested)
{
    var job = await _queue.DequeueAsync(stoppingToken);

    // 30-minute timeout per document
    using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(stoppingToken);
    timeoutCts.CancelAfter(TimeSpan.FromMinutes(30));

    try {
        await ProcessDocumentAsync(job, timeoutCts.Token);
    } catch (OperationCanceledException) when (!stoppingToken.IsCancellationRequested) {
        await MarkDocumentFailedAsync(job.DocumentId, "Processing timed out");
    }
}

الرموز المترابطة تضمن أننا لازلنا نحترم إيقاف تطبيقنا بينما نضيف الحد لكل - "document limit"

مسح قناة التقدم

كل وثيقة معالجة تحصل على قناة تقدم ل تحديثات الSSE. لكن إذا أغلق أحد المستخدمين شاشتهم في منتصف الـ-upload, فإن ذلك المسار يصبح ناضجًا

private readonly ConcurrentDictionary<Guid, ProgressChannelEntry> _progressChannels = new();

public int CleanupAbandonedChannels()
{
    var cutoff = DateTimeOffset.UtcNow - TimeSpan.FromHours(1);
    var cleaned = 0;

    foreach (var kvp in _progressChannels.Where(x => x.Value.CreatedAt < cutoff))
    {
        if (_progressChannels.TryRemove(kvp.Key, out var entry))
        {
            entry.Channel.Writer.TryComplete();
            cleaned++;
        }
    }
    return cleaned;
}

أ PeriodicTimer يطلب هذا كل 15 دقائق في المعالج الخلفي.

زيرمه: DuckDB + PostgreSQLM SK2SQLite

نستخدم قاعدة بيانات لأغراض مختلفة:

PostgreSQL/SQLite (EF CoreM SK2 يحفظ بيانات المجازة للوثيقة - ما هو موجود , حالة المعالجة, العلاقاتM SK3 هذه البيانات مستدامة وقابلة للبحث

DuckDB يحفظ الفيكتورات والرسم البياني للكائنات..

// Metadata in PostgreSQL
public class DocumentEntity
{
    public Guid Id { get; set; }
    public string Name { get; set; }
    public string ContentHash { get; set; }
    public DocumentStatus Status { get; set; }
}

// Vectors in DuckDB (managed by DocSummarizer)
// Entities in DuckDB (managed by GraphRAG)

واجهة الدردشة

تتدفق الأسئلة من خلال خط البحث الأجذري:

[HttpPost]
public async Task<IActionResult> ChatAsync([FromBody] ChatRequest request)
{
    // 1. Get or create conversation for memory
    var conversation = await GetOrCreateConversationAsync(request.ConversationId);

    // 2. Search with hybrid retrieval
    var searchResult = await _search.SearchAsync(request.Query, new SearchOptions
    {
        TopK = 10,
        IncludeGraphData = request.IncludeGraphData
    });

خدمة البحث تتعامل مع تحلل الطلب إذا كان ذلك ضروريا, ثم تقوم بتحليل الإجابة:

    // 3. Generate answer with LLM
    var answer = await _summarizer.SummarizeAsync(
        request.Query,
        searchResult.Segments,
        new SummarizeOptions { IncludeCitations = true });

    // 4. Save to conversation history
    await SaveToConversationAsync(conversation.Id, request.Query, answer);

    return Ok(new ChatResponse
    {
        Answer = answer.Text,
        Sources = answer.Citations,
        GraphData = searchResult.GraphData
    });
}

UI: HTMX + AlpineM SK2js

الواجهة هي صفحة واحدة مع مستندات على اليسار

┌──────────────────┬─────────────────────────────────────┐
│  📁 Documents    │  💬 Chat                            │
│  ─────────────   │  [Answer] [Evidence] [Graph]       │
│  [+ Upload]      │                                     │
│  📄 api-docs.pdf │  Q: How does auth work?            │
│  📝 readme.md    │  A: JWT tokens stored... [1][2]    │
│  ─────────────   │                                     │
│  🕸️ Graph: 168   │  ┌─────────────────────────────┐   │
│                  │  │ Ask about your documents... │   │
└──────────────────┴──┴─────────────────────────────┴───┘

Alpine.js يدير الحالة; HTMX يعالج تحديثات قائمة الوثائقM SK2

function ragApp() {
    return {
        messages: [],
        isTyping: false,

        async sendMessage() {
            const query = this.currentMessage.trim();
            this.messages.push({ role: 'user', content: query });
            this.isTyping = true;

            const result = await fetch('/api/chat', {
                method: 'POST',
                body: JSON.stringify({ query })
            }).then(r => r.json());

            this.messages.push({
                role: 'assistant',
                content: result.answer,
                sources: result.sources
            });
            this.isTyping = false;
        }
    };
}

نموذج التجريب

بالنسبة للتطبيقات العامة مثل lucidrag.com, tryb العرض يحجب تحميلات ويستخدم ما سبقM SK2محتويات تم تحميلهاMSC3 نظام العرض موجود لجعل تطبيقات العامة آمنةMST4منطقيّة MST5 و رخيصة بدون خاصيةM ST6طريق رمزية صغيرةM st7

public class DemoModeConfig
{
    public bool Enabled { get; set; } = false;
    public string ContentPath { get; set; } = "./demo-content";
    public string BannerMessage { get; set; } = "Demo Mode: Pre-loaded RAG articles";
}

أ DemoContentSeeder خدمة الخلفية تراقب مجلد المحتوى وتعالج أي فايل سقطة:

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    if (!_config.DemoMode.Enabled) return;

    await SeedExistingContentAsync();
    StartFileWatcher(_config.DemoMode.ContentPath);
}

هذا يسمح لك بتحميل المحتوى التجريبي ببساطة عن طريق نسخ الملفات - لا حاجة لإعادة البداية

تشغيل lucidRAG

مستقلة (لا إختلافات )

dotnet run --project Mostlylucid.RagDocuments -- --standalone

يستخدم SQLite + DuckDB محليا. Open http://localhost:5080.

الدوكر

services:
  lucidrag:
    build: .
    ports: ["5080:8080"]
    depends_on: [postgres, ollama]

ما يجري في الواقع

مكون مصدر هدف
تحليل الوثائق DocSummarizer PDFM SK3 DOCX, العلامة المنخفضة
مداخلات الـONNX DocSummarizer محلية, لا توجد مفاتيح API
إستخراج الكائنات GraphRAG IDF + الإشارات الهيكلية
البحث الهجين كلاهما S BM25 + بريت مع RRF M
معالجة غير متجانسة القنوات المحدودة МSK3 وقفات زمنية
UI للويب جديد HTMX + Alpine . js

التكلفة

صفر من تكاليف API من أجل التصنيف - المداخلات هي ONNX, الكائنات هيوريسيةM SK2 تدفع فقط لsynthesis LLM في وقت الاستطلاع , وهذا يعمل مع Ollama المحلية

المقالات المرتبطة

Finding related posts...
logo

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