Це Частина 5 з серії DocSummarizer – ,, а це – МSK1. Це також вершина Серії GraphRAG і Серія Semantic Search. Ми ' поєднаємо все в веб-приложений, який можна застосувати
🚨🚨 АКТУАЛЬНЫЙ АRTICLE МSK1 Ми все ще працюємо над деякими кінками та додаємо додаткиM SK2 Але ядро зроблено і працює добре. Сподіваюсь на поновлення протягом наступних кількох тижнівMSC4 Це буде на lucidRAGMST5comMst6 ЯM stsк7 Я додам скриншоти тут, як тільки я вичерплю дизайнMSt8
Весь сенс створення інфраструктури РАG - використати її для чогось реального.
За останні кілька тижнів ми створили
Тепер ми підключимо їх до lucidRAG - автономна веб-аplikaція для багатостороннього -розпитів на питання з допомогою візуалізації графів знань
Вебсайт: lucidrag.com | Источник: GitHub
завантажити документи. Задавати питанняM SK1 отримати відповіді з цитатами та графом знань, що показує, як концепти пов 'язані
Головні характеристики:
Дизайнові обмеження:
В жодному випадку LLM не використовується для відокремлення, введення, вилучення об 'єктівM SK2 або зберігання MSC3 лише для синтезу відповідей над отриманимиMSL4 цитатаMKL5 підтверджений доказMCL6
Вektorний пошук розривається лише для певних типів запитів:
| Тип запиту МSK1 Проблема з пошуком векторів МSK2 Вигадка графа | |
|---|---|
| КрестніM SK1документ МSK2 МSK3Як X пов 'язано з Y?" | Об' єднання об 'єктів між Docs | |
| Об 'єктM SK1центричний | МSK3 Як щодо Докера?" | Перехід графу від об' єкта МSK6 |
| Глобальні підсумки МSK1 МSK2Главні темиM SK3 ♫ | Детективність в суспільстві ♫ МSK5 ♫ |
lucidRAG використовує як вектори : для точності, так і графи , для контексту, МSK2 Попити на графи мають глибину
Ця програма поєднує три проекти, які ми вже збудували: StyloFlow - сигнал - двигун робочого потоку МSK2
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? Замість закодованих трубок , кожен етап обробки - це МSK1 хвиля МSK2, яка випромінює сигнали . хвиле запускаються тоді, коли умови їх триггерування співпадають | МSK4 | що дає можливість паралельного виконання | ( | вмонтування |+ | видобуток об 'єктів StyloFlow: Сигнель-Організація потоку роботи для подібних деталей.
Коли ви завантажуєте документ, він проходить через три етапи:
Даний кінцевий пункт завантаження підтверджує файл, обчислює хаш контенту для відокремленняM SK1 і ставить його в чергах для обробки фона:
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
Найголовніша ідея: ми хашуємо першими , заощаджуємо пізніше МSK2 Це запобігає марнуванню часу на обробку подібних завантажень
// 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;
}
Пізніше обробник підбирає заліковані документи і запускає їх через DocSummarizer:
var result = await _summarizer.SummarizeFileAsync(job.FilePath, progressChannel);
Ця одна лінія робить багато роботи (гляньте DocSummarizer Part 1):
Після відокремлення, ми виділяємо об 'єкти, використовуючи геріатичний підхід GraphRAG '
var segments = await _vectorStore.GetDocumentSegmentsAsync(documentId);
var entityResult = await _entityGraph.ExtractAndStoreEntitiesAsync(documentId, segments);
Це використовує IDF оцінку та структурні сигнали, а не за один -chunk LLM дзвінок - patrz GraphRAG - частина 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.");
}
Великі документи можуть займати хвилини, щоб їх обробити. Але застряглий документ має не блокувати всю чергу ' each document gets its own timeout МSK3
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");
}
}
Поєднаний символ гарантує, що ми все ще дотримуємося замкнутої програми, додаючи limit для документу per-document limitM SK1
Кожен документ, що обробляється, отримує канал прогресу для оновлення SSE. Але якщо пользователь закриває свій браузер всерединіMSC1upload, цей канал стає сиротоюM SK3 Ми відстежуємо час створення і регулярно очищаємоМSK4
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 хвилини в фоновому процесорі
Ми використовуємо дві бази даних для різних цілей:
PostgreSQL/SQLite (EF CoreM SK2 зберігає метадані dokumentu - те, що існує , статус обробки МSK2 відносини
Дук-ДБ зберігає вектори і граф об 'єкту. Це ' ефемерний M SK2 ви можете побудувати його з джерельних документів. Це відокремлення означає, що уektorи зберігають коррупцію не
// 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
});
Поиск регулює декомпозицію запиту, якщо необхідно, потім синтезує відповідьM SK1
// 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
});
}
В інтерфейсі є одна сторінка з документами ліворуч, чат праворучМSK1
┌──────────────────┬─────────────────────────────────────┐
│ 📁 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.comM SK1 режим демонстрації блокує завантаження та використовує до-завантаження контентуMSC3 tryb демонстрації існує для того, щоб зробити громадські застосування безпечнимиMST4 детерміністичнимиM ST5 і дешевими без спеціальногоM st6 кодових шляхівMst7
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);
}
Це дає вам змогу оновити зміст демонстрації, просто копіюючи файли - не потрібно перезапускатиM SK1
dotnet run --project Mostlylucid.RagDocuments -- --standalone
Використовуючи SQLite + DuckDB локально. Open http://localhost:5080.
services:
lucidrag:
build: .
ports: ["5080:8080"]
depends_on: [postgres, ollama]
| МSK0 Komponenт | Źródło МSK2 Задача |
|---|---|
| Дослідження Dokumentів МSK1 DocSummarizer | PDFM SK3 DOCX, Замітка МSK5 |
| вбудова на NX | DocSummarizer МSK2 локальний |
| Видобуток об 'єктів | |
| Гібридні пошуки МSK1 Обидві МSK2 BMM SK3 + BERT з РРФ мSK5 | |
| Асинхове оброблення МSK1 Нові МSK2 Замкнуті канали , відхилення часу | |
| Веб інтерфейс МSK1 Новое МSK2 HTMX + Альпіjski MSК4 js МСК5 |
Нуль هزینه API для індексування - вбудова - ONNX , об 'єкти - эвристичні МSK2 Ви платите тільки за синтез LLM в часі запиту МSK3 і це працює з локальною Олламі
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.