DocSummarizer-Teil-Dokument RAG Web-Anwendung (Deutsch (German))

DocSummarizer-Teil-Dokument RAG Web-Anwendung

Thursday, 01 January 2026

//

8 minute read

Das ist Teil von der DocSummarizer Serie, und es ist auch der Höhepunkt von dem SchaubildRAG-Reihe und Semantische SucheWir kombinieren alles zu einer deployablen Web-Anwendung

Der Kern ist aber gut gemacht und funktioniert gut.Erwarten Sie Updates in den nächsten Wochen, sobald ich unten aus dem Design.

Der ganze Punkt des Aufbaus der RAG-Infrastruktur ist es, sie für etwas reales zu verwenden.

In den letzten Wochen haben wir'«ve gebaut.

  • DocSummarizer Einbettungen von ONNX-Dokumenten
  • GraphRAG Wissensgraphen zur Erkennung von Gemeinschaften
  • Semantische Suche Hybride Suche mit RRF-Fusion

Jetzt verdrahten wir sie zusammen in LucidRAG Eine eigenständige Web-Anwendung für Multi--...Dokument Frage beantworten mit Wissensgraphenvisualisierung...

Webseite lucidrag-.-com | Quelle: Eurostat. GitHub

Was macht lucidRAG?

Hochladen von Dokumenten Fragen stellen – . Erhalten Sie Antworten mit Zitaten und einem Wissensgraphen, der zeigt, wie Konzepte verbinden –.

Wesentliche Merkmale

  • Mehrsprachiges Hochladen des Dokuments mit Drag---und---Drop
  • Agenturische RAG Suchdekomposition und Selbstkorrektur -Bereinigt,Begrenzte Deterministisch in der Struktur,Bereinigender Lebenszyklus von Einzelanfragen
  • Visualisierung von Wissensgraphen Siehe Entity-Beziehungen
  • Beweisansicht Quelle Zitate auf der Ebene von -
  • Standalone-Einsatz Ausführbare Einzel- oder Docker

Konstruktionsbeschränkungen

  • Keine Cloud-Abhängigkeit für Indexierung
  • Deterministische Preprocessing-Vorbehandlung (-Cunking-,-Embedding MS K2-Entitätsextraktion )-Empfang
  • Rebuildierbarer Vektorzustand aus Quelldokumenten
  • Verwendete LLM Nur für Antwortsynthese über abgerufene Beweise

An keinem Punkt sind LLMs für das Chunking verwendet ,, , Embedding , , , Entity-Extraktion , , oder Speicher , nur zur Synthese von Antworten über abgerufene ., zitieren , - , unterstützte Evidenz ,

Warum Vektorsuche kombinieren?

Vektorsuche allein bricht für bestimmte Abfragetypen zusammen

Suchtyp des Vektor-Suchproblems |------------|----------------------|----------------| Wie bezieht sich X auf die Verbindung von Y-?" und | mit docs? Was ist mit Docker, ?" und |? Graphenquersal aus der Entity? Globale Zusammenfassungen - | - " - Hauptthemen -?" - | - Gemeinschaftsdetektion

lucidRAG verwendet beide :-Vektoren für Präzision, ,, Graphen für Kontext, ., Graphinabfragen sind tief, MSC3, begrenzt, MSV4,max, Hops, MKS6 und Scoped, um ungebundenes Traversal bei großen Corpora. zu verhindern.

Architektur im Überblick

Die App schichtet drei Projekte aus, die wir bereits gebaut haben. StyloFlow Eine signalgesteuerte Workflow-Engine (MSK)

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

Warum StyloFlow-? Anstelle von hardcodierten Pipelines ist jede Verarbeitungsstufe eine "-Wave, die Signale ausstrahlt, wenn ihre Triggerbedingungen mit , übereinstimmen und parallele Ausführung ermöglicht. StyloFlow-:-Signal---Driven Workflow Orchestration für Details zur Implementierung .

Die Verarbeitungspipeline

Wenn Sie ein Dokument hochladen, fließt es durch drei Stufen.

Stage -1:-Upload und Warteschlange

Der Upload-Endpunkt validiert die Datei, berechnet einen Content Hash zur Deduplizierung und stellt sie für die Hintergrundbearbeitung bereit.

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

Die Schlüssel-Insights... wir haben zuerst..., spare later... das verhindert, dass die Bearbeitungszeit bei doppelten Uploads verschwendet wird.

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

Stage-Empfang und Einbettung

Der Hintergrund-Prozessor nimmt Schlange Dokumente und läuft sie durch DocSummarizer.

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

Diese einzelne Linie macht eine Menge Arbeit DocSummarizer Teil):

  • Parsen Sie die Dokument-Struktur
  • Aufgeteilt in semantische Stücke unter Beachtung von Überschriften
  • ONNX-Embeddings für jeden Block generieren
  • Vektoren in DuckDB mit HNSW-Indizierung speichern

Stufe 3:-Entity Extraction

Wir extrahieren Entitäten mit GraphRAG-''s heuristischem Ansatz.

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

Dies verwendet IDF-Scoring und strukturelle Signale anstatt per---Cunk LLM-Aufrufe. GraphRAG-Teil für Details

Gebundene Kanäle für Rückdruck

Eine naive Implementierung würde ungebundene Warteschlangen verwenden, die auslaufen könnten.-von -...Gedächtnisabstürzen während des Hochladens von Fluten.

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

Wenn die Warteschlange nach oben füllt Wait Modus blockiert neue Schreibvorgänge, bis der Raum öffnet... Wir fügen einen Timeout hinzu, damit Benutzer einen eindeutigen Fehler bekommen, anstatt zu hängen.

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---Dokument Timeouts

Große Dokumente können Minuten dauern, um zu verarbeiten..-Aber ein feststeckendes Dokument sollte die gesamte Warteschlange blockieren.MSC2-Jedes Dokument erhält seinen eigenen Timeout.

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

Das verknüpfte Token stellt sicher, dass wir immer noch die Abschaltung der Anwendung respektieren, während das Hinzufügen des per---Dokuments Limits

Fortschrittliche Kanalreinigung

Jedes Verarbeitungsdokument erhält einen Fortschrittskanal für SSE-Updates, aber wenn ein Benutzer seinen Browser schließt, wird ,, dass Kanal verwaist wird... Wir verfolgen Erstellungszeiten und reinigen regelmäßig

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

A PeriodicTimer ruft dies alle -15-Minuten im Hintergrundprozessor auf

Lagerung: :, DuckDB, PostgreSQL, /, SQLite

Wir verwenden zwei Datenbanken für verschiedene Zwecke

PostgreSQL-/-SQLite-Kern speichert Dokument-Metadaten, was vorhanden ist, ,.Verarbeitungsstatus, ,. Beziehungen, MKK3. Diese Daten sind langlebig und abfragebar...

EnteDB speichert Vektoren und das Entity-Graph ,. , It , ' , ephemeral , - , Sie können es von Quelldokumenten wieder aufbauen Diese Trennung bedeutet Vektor speichern Korruption doesns , ' , t verlieren Ihr Dokument Inventar ,. ,

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

Die Chat-API

Fragen fließen durch die agentische Suchpipeline.

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

Der Suchdienst behandelt Abfragedekomposition, wenn nötig, und synthetisiert dann eine Antwort.

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

Die UI-:-HTMX-Studie Alpine-MSC2-Js

Die Benutzeroberfläche ist eine einzelne Seite mit Dokumenten auf der linken Seite.

┌──────────────────┬─────────────────────────────────────┐
│  📁 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 verwaltet State-;-HTMX behandelt Aktualisierungen der Dokumentenliste

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

Demo-Modus

Für öffentliche Bereitstellungen wie lucidrag-.-com.,-Demomodus deaktiviert Uploads und verwendet vor dem Download-MSC2-loaded content.MSC3-Demos-Modus existiert, um öffentliche Deployments sicher zu machen, ,-deterministisch und billig ohne spezielle --case-Codepfade:

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

A DemoContentSeeder background service beobachtet das Content-Verzeichnis und verarbeitet alle heruntergelassenen Dateien

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

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

Dies ermöglicht es Ihnen, Demo-Inhalte zu aktualisieren, indem Sie einfach Dateien kopieren.

LucidRAG läuft

Standalone Keine Abhängigkeiten)

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

Verwendet SQLite lokal Enten-DB http://localhost:5080.

Docker

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

Was eigentlich läuft

]|-Komponente - Quelle - Zweck - | - |-----------|--------|---------| Dokument-Parsing - DocSummarizer - PDF - , DOCX-, - Markdown - | Einbetten von ONNX-Embeddings für |, DocSummarizer für lokale |, keine API-Tasten für |, Entitätsextraktion - GraphRAG - IDF - Struktursignale

  • Hybride Suche - beides - |BM25B+BERT mit RRF - |B |-Async-Verarbeitung |-Neue gebundene Kanäle ,-Timeouts |- Web-Benutzeroberfläche Neue Version von | HTMX Alpine Version von.

Kosten

Null API-Kosten Für die Indexierung sind Embeddings ONNX.,. Entitäten heuristisch. Sie zahlen nur für LLM-Synthese zur Abfragezeit, und das funktioniert mit lokalen Ollama...

Verwandte Artikel

Finding related posts...
logo

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