This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
DocSummarizer 部件 : @ 5 @ @ MPK1 @ 清晰的RAG}% MS K2 多-Document RAG Web 应用程序
Thursday, 01 January 2026
这是 部分#5 和它也是世界顶峰 图图RAG系列 和 语义搜索系列@._We'}将所有内容合并成可部署的网络应用程序@.#
“.”将会在LawryraGQ.com .I''在这里添加截图,
建立RAG基础设施的目的就是要用它来做一些真实的东西
在过去几周内,我们建造了:
- DocSummamer 缩写器 -文档解析@,语义填充 @,ONNX嵌入
- 图图RAG “-实体提取”“,知识图 ,社区探测”
- 语义搜索 -BMZK1+BERT混合搜索与RRF聚变
现在我们把他们联合起来 更清晰 - @ a 独立的网络应用程序, 用于多@ MS K1_ document problem question 回答知识图解可视化的答题@.}
网站#: 清晰度@ . @ com | 资料来源:: 吉特胡布
何为清晰的RAG
上传文档@.问问题 @.以引用获得答案,并有一个显示概念如何连接.的知识图表。
密钥特性 @:
- 多@ - @ document 上传 拖动@ - @ 和 @ MS K1}% 跳
- RAG 代理RAG -查询分解和自定义=-校正=@(,,在结构上的确定性单要求生命周期_)
- 知识图图可视化 - 见实体关系
- 证据观点 @-_ 句子-}来源引用
- 独立部署 - 單一執行檔或 docker
设计限制 :
- 索引编制不依赖云
- 磁性预处理@(_chunking @,}嵌入,}实体提取=)}
- 可重建的矢量状态
- 使用的LLM 仅 供回答合成检索过的证据
在任何关卡中都从未使用 LLMs 来填块@, @ 嵌入\ ,} 实体提取, @ 或存储 *-{ 仅用于对已取回的回答进行合成=%,# 引用_ MS K5 @ 支持的证据=.}
为何合并矢量搜索@+知识图表 @?#
某些查询类型的矢量单搜索分类@: @%
| 查询类型 @ | 矢量搜索问题@ | 图表解答 * | |
|---|---|---|---|
| @"#X 和Y?"}{ | _BARBAR_Cross@-}文件= | Q X有什么关系? | |
| “ | ”实体“-”核心“MS K2” ,“"” 那么“Docker @?"” 来自实体“ | ”的“I | ”图表翻转情况如何? |
| 全球摘要 @ | #_"}主題 @MS K3> @ | 社区探测@ | # |
清晰的RAG 使用两个“:”矢量来精确@,}图表用于上下文 @.}图表查询是深度-}<(>max {2}{Hops)},并用于检索文档的范围,以防止大型子公司无限制地穿行*.}
建筑结构概览
应用三层三项目 we'_#已经建立 ,}{通过 斯泰罗花 - 信号- 驱动的工作流程引擎:
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
为什么斯泰洛花 ? 而不是硬编码的输油管,}每个处理阶段都是一个Q"}波浪•"}当触发条件与允许平行执行的“,”相匹配时,发出信号{.〞波跑 StyloFlow :信号-Driven工作流管弦化 执行细节@. @%
处理管道
当您上传文档时 ,}它会流过三个阶段\ OMK1
阶段 @ 1: 上传和排队
上传端点验证文件@ , @ 计算一个内容散列, 用于解码\ MS K1} @ 并排队用于背景处理@ MPK2
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
关键洞察@: @ we hash first @ , @ save later\ .} 这防止在重复上传中浪费处理时间 @ MPK3 @
// 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);
此单行可做很多工作 @ (_ See DocSummarizer 部件 *\ 1 {):
- 分析文档结构 *{(_PDF,} DOCX} , Markdown@)
- 按标题划分成语义块
- 生成每个块块的ONNX嵌入
- 使用 HNSW 索引化将矢量存储在 DuckDB 中
实体采掘阶段
在块积后, 我们使用 GraphraG' 来提取实体@ : @
var segments = await _vectorStore.GetDocumentSegmentsAsync(documentId);
var entityResult = await _entityGraph.ExtractAndStoreEntitiesAsync(documentId, segments);
这使用了以色列国防军的评分和结构信号,而不是使用任何-chunk LLM电话@-见。 图表( G) 部分 @ 2 @% 详细描述@. @%
后压带宽通道
一个天真的执行将会使用无约束的队列@ , @ 冒着在上传洪涝期间发生崩溃的风险\ MS K1_ @ - @ momory crash
private readonly Channel<DocumentProcessingJob> _queue =
Channel.CreateBounded<DocumentProcessingJob>(new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.Wait
});
当队列填满时 @ , @ Wait 新区块模式新写到打开空间@. @ we 添加一个超时, 这样用户就有一个清晰的错误, 而不是挂上@ :} @
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.");
}
佩尔-文件超时
大文档处理@ . @ but a stuck 文档应该'}%t 屏蔽整个队列\ MS K2每份文件都有其自身的超时=:}
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");
}
}
链接的标记确保了我们仍然尊重应用程序关闭, 同时添加 per-\ document 限制@ .
进度频道清理
每个处理文档都有一个 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;
}
AA PeriodicTimer 在背景处理器中每分钟 15 @ minutes
存储@: @ duckDB @ + @ postgreSQL @ /_ SQLite
我们为不同目的使用两个数据库 :
PostgreSQL/SQ莱特 @(EF 核心@) 文档存储元数据 @ -}\ MS K1\ 处理状态@ ,\ 关系 @ I. 此数据是耐久和可查询的@ MPK4}
鸭子数据库 存储矢量和实体图形@. @ it'_%s epheperal *-}您可以从源文档中重建它 < . > 此分隔表示矢量仓库腐败}{'不会丢失您的文件清册>.}
// 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)
聊天 API
问题通过毒气搜索管道流出
[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
});
}
阿尔卑斯山 .日
用户界面是一个单页,左侧为文档 ,}右侧的聊天时间:}
┌──────────────────┬─────────────────────────────────────┐
│ 📁 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 管理 stateMS K1HTMX 处理文档列表更新@:
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;
}
};
}
演示模式
对于诸如 Llearrag.com,示范模式等公共部署, 禁用上传并使用 pre@ -loaded 内容@.Demo 模式使公共部署安全@ MPK4 确定性 @,, 没有特殊@ I-case 代码路径@MS K7}
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";
}
AA DemoContentSeeder 背景服务 监视内容目录并处理任何已丢弃的文件@: @%
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (!_config.DemoMode.Enabled) return;
await SeedExistingContentAsync();
StartFileWatcher(_config.DemoMode.ContentPath);
}
这样您就可以仅通过复制文件 {-} 来更新演示内容。 没有需要的重新启动@.}fn微软雅黑
运行清晰
无依赖性 **
dotnet run --project Mostlylucid.RagDocuments -- --standalone
使用 SQLite @ + @ duckDB 本地端@ MS K1 打开 http://localhost:5080.
嵌入器
services:
lucidrag:
build: .
ports: ["5080:8080"]
depends_on: [postgres, ollama]
实际运行的
| 目的 @ | |||
|---|---|---|---|
| 文档解析 @ | DocSummarizer @MS K2 PDF, DOCX}, 标记@ | ||
| ONNX 嵌入@ | DocSummarizer @ | 本地},没有 API 键 * | |
| I | 实体提取 | G MS K2IFD+结构信号MSKO4 | |
| “ | 混合搜索”“ | ”都“MS K2B25++BERT与RF | ” |
| Async 处理 @ | 新的# | Bounded croute+,超时@ | |
| @ | @WebUI# | @New | _HTMX@+}Alpine @.js @MS K5 @ |
费用成本
零APPI费用 您在查询时间只支付LLM合成费@,}和本地Ollama. 的工作单位。