# DoomSummarizer:

<!--category-- AI, LLM, RAG, C#, Deep Research, Lucene, Knowledge Graph, Time Boxed Tool -->
<datetime class="hidden">2026-01-29T10:00</datetime>

Deep Research utan djupa fickor.

När """, "" Deep Research "" - - -" "" landade i de bästa AI-verktygen, , "" Jag var nyfiken på vad det här är, : "", och när jag förstod hur den såg ut- -såg jag att jag redan hade byggt den mesta av den.

### DoomSummarizer utnyttjar ett liknande arbetsflöde , men dess design optimerar " läsning | + | reducering |" | processen till att vara mestadels deterministisk och lokaliserad |

I detta angreppssätt eliminerar styrkan i minskningstegen behovet av en stor, dyr modell för att få tillfredsställande resultat.

Framståendet, Jag hade redan många nödvändiga byggstenar , inklusive multi- källupptackning M SK3 entite extraktion , kunskapsgrafer MSC5 och inbädding MST6baserad ranking MSV7 som delades med lucidRAG och DocSummarizer-serien MSM8

Den sakna komponenten var inte brist på förmågan, ,, utan snarare förståelse för hur man effektivt kan tillämpa de här redan existerande elementen.

I kommersiella verktyg betyder ofta

eller

Väldigt

eller

Begreppet som planerar en forskningsmetod

En stor del av värdet ligger i att förbättra inmatningen.:, att få fram friskare, bättre, högre signalkällor än något annat model.’, att skära ner utbildningsdata, -, avlägsna data och ,, och minska risken för att modellen ska göra saker till slut.

exempel: [OpenAI Deep Research](https://openai.com/index/introducing-deep-research/), [Gemini Deep Research](https://blog.google/products/gemini/new-gemini-app-features-march-2025/), och [Perplexity Deep Research](https://www.perplexity.ai/hub/blog/introducing-perplexity-deep-research) ( kallas också “Forschungsmodit ” i deras gränssnitt

DoomSummarizer följer samma arbetsruttlet, men den är konstruerad så att större delen av “read + reducer ” arbeten är deterministiska och lokala, och LLM används främst för syntetisering.

Om reduktionsstegen är stark, så behöver du inte ett stort, dyrt model för att få ett bra resultat.

Mellan [lucidRAG](https://github.com/scottgal/lucidrag) och DocSummarizer-serien, Jag hade redan större delen av byggstenarna : multi, -, källuppfinning, ,, entite extraktion, M SK4, kunskapsgrafer, MSC5, inbäddar, MST6, grading, Mst7

Den sakna biten var *orkestering*. Hur tar man alla dessa signaler och sammanfogar dem i något som är coherent med en liten modell

Det där's DoomSummarizer . Namerat efter doom scrolling , eftersom det scrollar så du inte behöver.

Men den här artikeln handlar inte om verktyget, det handlar om idéerna som gör det att fungera.

[![GitHub-utsläpp](https://img.shields.io/github/v/release/scottgal/lucidrag?filter=doomsummarizer*&label=doomsummarizer)](https://github.com/scottgal/lucidrag/releases?q=doomsummarizer)

Det här är **Del 5** av DocSummarizer-serien.

Om du vill ha grunden först:

- [Del 1: Arkitektur](/blog/building-a-document-summarizer-with-rag)
- [Del 2: Att använda verktyget](/blog/docsummarizer-tool)
- [Del 3: avancerade koncept](/blog/docsummarizer-advanced-concepts)
- [Del 4: Byggnader RAG Pipelines](/blog/docsummarizer-rag-pipeline)

Det här är också en [Time Boxed Tool](/blog/timeboxedtool-consoleimage).

---


[toc]

## Kernidén : Reduced RAG

Standard RAG gör redan semantisk extrahering.

smärtan är det sista steget.:, du hamnar fortfarande i ett högljud.,, ett överlapparde hög med bitar som hamnar i en LLM och ber den att förenkla kopior.M SK2, väger källor, ,, och håller ett jämnt tråd.

Tänk om du gjorde mer arbete *före* LLM ser allt

Att "'" är mönstret. **minska innan du genererar**. Ta rådokument och reducera dem deterministiskt till sina nödvändiga signaler : inbäddar, , klassificeringar,, dominerande segment, , enhetens profiler, M SK5 När LLM ser något,

```mermaid
flowchart TD
    subgraph REDUCE["REDUCE: Distil to Signals"]
        Q[Query] --> Decompose[Composite Decomposition]
        Decompose --> S1[Subquery 1]
        Decompose --> S2[Subquery 2]
        S1 & S2 --> Fetch[Multi-Source Fetch]
        Fetch --> Embed[ONNX Embed Each Item]
        Embed --> Rank[6-Signal RRF Fusion]
        Rank --> TextRank[TextRank Extraction]
        TextRank --> NER[Entity Extraction]
        NER --> Profile[Entity Profile Vectors]
        Profile --> Segments[Salient Segment Extraction]
    end

    subgraph SYNTH["SYNTHESIZE"]
        Segments --> Standard["Standard Path - 1 LLM Call"]
        Segments --> LongForm["Long-Form Path - N+3 LLM Calls"]
    end

    style REDUCE fill:#1a1a2e,color:#e0e0e0
    style SYNTH fill:#162447,color:#e0e0e0
```

Reduktionsfasen är deterministiska: inbäddar , klassificering, , entite extraktion, , TextRank, M SK4 segment scoring,. Inga LLM sändningar

Syntesen är den enda LLM-stegen. För en standardfråga är de reducerade signalerna tillräckligt rena för att **ett samtal** gör jobbet. För lång tid-formdokumenten , en befälhavare planerar strukturen och huvudmodellen genererar sektioner i parallella sträckor**N+3** ringer på N sektioner).

För mer detaljer på det här mönstret, [Reduzerad RAG](/blog/reduced-rag).

## Kartafasen: Från fråga till källor

Innan något minskar, måste sökningen bli sökaktioner.

Ett litet lokalt sensormodell (0.6B i mitt fall, JSON-läger , temperatur | 0.1) tolkar rawqueryn till en strukturerad syfte |, | och sedan en YAML driven källreduktor som omvandlar det syftet till konkreta fetcher

```mermaid
flowchart TD
    Q["User Query"] --> Sentinel["Sentinel: JSON Interpretation - categories, intent, entities, - temporal hints, tone"]
    Sentinel --> Router["Source Router - YAML category → source mapping"]
    Router --> S1["gnews:AI safety"]
    Router --> S2["search:AI regulation"]
    Router --> S3["bbc:technology"]
    Router --> S4["reddit • hn"]
    S1 & S2 & S3 & S4 --> Fetch["Parallel Fetch - circuit breaking + rate limiting"]
    Fetch --> Merge["Merge + Deduplicate - → reduce phase"]

    style Sentinel fill:#1e3a5f,color:#e0e0e0
    style Router fill:#0d3b2e,color:#e0e0e0
```

Befälhavaren extraherar strukturerade fält

- Topic-kategorier (med självförtroende vikter
- Query intent (nyhetslära, QAM SK2 forskning , jämförelse
- Temporal känslighet (breaking / idag / vecka
- Namerade enheter
- För komplexa frågor

**Tillvägagångspunkt** händer i sex faser:

1. **Explicita källor**: sa användaren `"from HackerNews"` → `hn` går rakt in
2. **söka sökningar**: aktiviserade sökord blir kraftfulla, - kvalificerade API-sändningar, ( DuckDuckGo, , Brave,, Google News RSS, M SK6
3. **Kategorisruttering**: temats vikter kartläggs till RSS-flöden via YAML configM SK1 En fråga om " AI-reglering " riktningar till teknikflöden *och* policy feeds
4. **Förstärkning av forskning**: `research` eller `deep_dive` intents lägga till archive
5. **Enhetens rikaring**: Namerade subjekter från NER får sina egna gnews-frågan. `gnews:OpenAI`)
6. **Diversitetsgolvet**: om mindre än 3 källor selected

Resultatet: identifisatorer av källor: :, ~10 hämtade parallellt med varje: -, källorskrets brödning.

Viber ändrar sökordna innan de träffar något API. Till exempel, `--vibe doom` prefixfrågar med *"synnerliga problem-risker-problem i*, omforma vad som kommer tillbaka innan någon rangorde händer

Om du vill ha bakgrunden på resiliensbitarna, se [Att använda Polly för pensioner](/blog/usingpollyforretries) (kretsbrytare [Backpressure i kösystem](/blog/backpressure-queueing-systems) (hastighetsbegränsning och baktryck

## Att rekonstruera sökningen

De flesta söksystem behandlar " Vad är det som är nytt i AI-säkerheten och vad är de senaste lagarna?

Befälhavaren bryter ihop komplexa frågor:

```json
{
  "is_composite": true,
  "subqueries": [
    "What's new in AI safety?",
    "What are the latest AI regulations?"
  ]
}
```

Det intressanta valet är vad som händer härnäst. Varje undersökande får sin egen ONNX inbäddning ( se [ONNX: Att köra ML-modeller lokalt](/blog/docsummarizer-advanced-concepts#onnx-running-ml-models-locally)). När jag sätter poäng på artiklar **max kosinusliknande** across all subquery embeddings, inte genomsnittet

Varför max? Ett artikel som perfekt besvarar M SK1 AI-säkerheten " borde inte bestraffas för att inte säga någonting om ♫" reglerna ". En jämförelse skulle gömma det under medelmåttiga partiella matchar M SK6 Max likhet betyder: om en artikel snäller *alla* en del av din fråga , det stiger. Det andra alternativet, , medelvärde, belönar blanda artiklar som vag rör allting

## Att strömma heterogena signaler

Jag har sex klassificerade signaler, de har helt olika skalor.

```mermaid
flowchart LR
    subgraph Signals
        BM25[BM25 - Keyword Match]
        Fresh[Freshness - 48h Half-Life]
        Auth[Authority - HN/Reddit Score]
        QSim[Query Similarity - Cosine]
        Vibe[Vibe Alignment - Cosine]
        Qual[Quality - Clickbait Detector]
    end

    subgraph RRF[RRF Fusion]
        direction TB
        R[Rank Each Signal - Independently]
        F["score = Σ weight × 1/(60 + rank)"]
    end

    BM25 & Fresh & Auth & QSim & Vibe & Qual --> R --> F

    style RRF fill:#1e3a5f,color:#e0e0e0
```

HN-punkter går till tusentals . Kosineliknande är -1 till | 1. Frihet bryter sig exponentiellt M SK3 Du kan | ' | inte helt säkert lägga ihop dessa tillsammans | МSK5 | Skalerna är okompatibla

Det nyckelsignalet här är BM25 över flera fält (BMM SK2F). Om du vill ha bakgrunden på BM25, se [DocSummarizer Part 3](/blog/docsummarizer-advanced-concepts#bm25-the-sparse-retrieval-workhorse).

**[Reciprokal rangfuusion](/blog/docsummarizer-advanced-concepts#hybrid-search-with-rrf)** (Cormack et al *ranker*: `score = Σ weight × 1/(k + rank)` (med `k=60`).

Om du vill ha hela bakgrunden på RRF, Del 3 täcker den . Här är kurvan **vikter anpassar sig till söktyp**. Befälhavaren upptäcker avsikten , och scoraren omformar sig själv

- Timeline queries (" Vad hände idag ?") knuffa nyhet upp och kvalitet ner
- Berättarliga frågor (" Hur fungerar kvantberäkningarna ?") gör motsatsen M SK2 djupet spelar mer roll än nymodighet ).
- Roundups och jämförelser har sina egna tunerade profiler.

## Viber som en extraherande dimension

Det här började som ett skämt.

Ett "liv, " är inte **första rangeringssignal i klass -** inbäddad i den att hämta data

```mermaid
flowchart LR
    V["--vibe doom"] --> Expand["Query Expansion - 'concerning risks issues in...'"]
    V --> Embed["Vibe Embedding - 'vulnerability breach layoffs - recession crisis warning...'"]
    Embed --> Score["Cosine Similarity - per article"]
    Score --> RRF["RRF Signal - weight: 0.4"]

    style V fill:#8b0000,color:#e0e0e0
```

Tre saker händer när du sätter en vibe:

1. **Frågans utökning** - söket får prefix med humör
2. **Vibe-inbäddar** - representerande text blir inbäddad som ett 384-dim mål
3. **Rangingsignal** - artiklar med kosinelikhet till vibe-inbäddningen

Custom viber fungerar identiskt. `--vibe "contemplative philosophical"`- din text blir både sökprefixen och rangeringsmålet . Systemet skiljer inte mellan förutdefinierade och custom-definerade ord.

## Enhetens profiler , Inte enhetens tal

Här är ett misstag jag gjorde först.

Två artiklar nämner båda "KalifornienM SK1

1. *"OpenAI ' Kaliforniens kontor annonserar säkerhetsteam*
2. *"Kaliforniska mandelfarmare står inför torka*

Beräkning av de delaktiga enheten : 1. Därför besläktade M SK2 Uppenbarligen fel

Fixen var att tänka på enheter som vector, inte strängar.

Det här är [begränsad fuzziness-mönster](/blog/constrained-fuzziness-pattern): NER-modellen föreslår enheter , men deterministisk viktering bestämmer vad som är viktigt

Varje dokument får en **Profil med vikt** (a  384-dim-vektorkodering *som* varelser dyker upp och hur separerade de är

```
weight = TF × IDF × confidence × type_weight
profile = L2_normalize(Σ entity_embedding × weight)
```

```mermaid
flowchart TD
    subgraph Doc1["Article: OpenAI Safety Team"]
        E1["OpenAI (ORG) - IDF: high - weight: 2.1"]
        E2["California (LOC) - IDF: low - weight: 0.3"]
        E3["Safety Team (MISC) - IDF: high - weight: 1.8"]
    end

    subgraph Doc2["Article: Almond Farming"]
        E4["California (LOC) - IDF: low - weight: 0.3"]
        E5["Almond Farmers (MISC) - IDF: high - weight: 1.9"]
        E6["Drought (MISC) - IDF: medium - weight: 1.2"]
    end

    Doc1 --> P1["Profile Vector - Dominated by OpenAI + Safety"]
    Doc2 --> P2["Profile Vector - Dominated by Farming + Drought"]
    P1 -. "low similarity" .- P2

    style Doc1 fill:#1a1a2e,color:#e0e0e0
    style Doc2 fill:#2d1a2e,color:#e0e0e0
```

IDF är nyckeln . "Kalifornien" dyker upp i tonvisar av föremål | - | låg IDF | МSK4 | svag signal |. | " | OpenAI |" | är distinktiv ♫ - | hög IDF

Den smarta delen: **Sättande TF**. Istället för råbeteckningstalet, använder jag `1 + log(mentions)`. En enhet som nämns ♫ 50 ♫ gånger i en lång artikel får ♫' ♫ inte ♫ МSK3 ♫ samma vikt som en som näms en gång ♫

## Semantisk Graphforskning

Enhetens profiler är inte bara för att förstå dokument, de är **dragningsdimension**. Varje documen, 🥱, t, ', får en index i en HNSW-graf.

Om du vill ha mer detaljer om HNSW och DuckDB VSS, se [GraphRAG-delen 2: Minimalt hållbara GrafRAG](/blog/graphrag-minimum-viable-implementation).

```mermaid
flowchart TD
    subgraph Retrieval["Three-Layer Retrieval (UNION)"]
        L1["Lucene FTS - keyword matches"]
        L2["Embedding HNSW - semantic similarity"]
        L3["Entity Profile HNSW - entity fingerprint match"]
        L1 & L2 & L3 --> Union["Union of all candidates"]
    end

    subgraph Enrich["Post-RRF Enrichment"]
        Top["Top 5 Ranked Items"] --> Agg["Aggregate Profile - mean of entity profiles"]
        Agg --> Graph["HNSW Search - min similarity: 0.30"]
        Graph --> Related["+3 Related Articles - scored below existing items"]
    end

    Union --> RRF["RRF Fusion"] --> Enrich

    style Retrieval fill:#1a1a2e,color:#e0e0e0
    style Enrich fill:#0d3b2e,color:#e0e0e0
```

Detta fungerar i två stadier:

**Under hämtandet**: när befälhavaren extraherar | 2+| varelser från queryn |, | en query-varelseprofil obliceras med samma TF |× | IDF-formulär | . | Det här söker HNSW-indexet efter artiklar med liknande varelsers fingeravtryck | МSK5 | med minimalt likheter till |

**Efter rangering**: de högst rankade itemsna ≥5 har sina entitetsprofiler i genomsnitt i en aggregerad vektor . Den här aggregerade sökningen letar efter besläktade artiklar som nyckeln och inbäddade lagerna helt misslyckades med *"med hjälp av enheterM SK1*. Detta fångar de primära källorna som nyhetsartiklarna referenserar men inte

Tre spårningslag smälter samman genom union: Lucene-キーワード matchar | ∪ | Embedding matchar ∪ | Entity profile | HNSW matchar. | Unionen är avsiktligt bredare än intersectionen |. | Den fångar saker som varenda enskild signal skulle dyka upp på tavlan | МSK5 | RRF-fusionen sorterar sedan ut vad

Fallback-stigen ( för corpora utan än enhetens profiler | ) | använder SQL-enhetens co`HAVING shared_count >= 2`). Det fungerar , men det's OM SK3NMSC4 istället för OSSK5log NMST6 `--backfill-entity-profiles` bevel migrerar existerande korporationer till HNSW-stigen.

## Standardsynthes : En LLM sändning

Detta är den normala vägen. `scroll "AI safety and regulation"` träffar detta. När reduktionsfasen bryter ut dina källor till klassificerade, duplicerade , entityder

```mermaid
flowchart TD
    subgraph REDUCE["Reduce Phase (ALL DETERMINISTIC)"]
        Items[Fetched Items] --> Embed[ONNX Embed All Items]
        Embed --> RRF[6-Signal RRF Fusion]
        RRF --> TR[TextRank Compression]
        TR --> Seg[Segment Extraction + Salience Scoring]
        Seg --> Dedup[Deduplication + Relevance Floor]
    end

    subgraph SYNTH["Synthesis (1 LLM CALL)"]
        Dedup --> Rerank["Semantic Re-Rank - by query similarity"]
        Rerank --> Budget["Smart Evidence Budgeting - redistribute unused chars"]
        Budget --> Gen["Single LLM Call - with curated evidence"]
        Gen --> Output[Final Summary]
    end

    style REDUCE fill:#0d3b2e,color:#e0e0e0
    style SYNTH fill:#1e3a5f,color:#e0e0e0
```

Rederingsfasen gör det hårda arbetet. När LLM ser något, får den

- **Bara de främsta elementen**: relevansnivå på 30% det bästa ämnet' poängenM SK3 URL
- **Arter av mångfald**: är upphöjt till 3 punkter per domain för runda upp sökningar
- **Semantisk re-ranking**: items re- ordnade efter kosine likhet till query inbädding
- **Smart evidence budgeting**: korta grejer donerar oanvända karaktärsbudget till långa grejer , så bevisrutor är inte slösade bort
- **Strukturerade bevisblock**: `[E1] Title | topic | relevance` med innehåll uttalat till budget

Ett LLM ringer ".", det är "'" som är belöningen av [reduced RAG](/blog/reduced-rag): urval , klassificering, , duplicering,, och budgetering händer allt innan LLM ser något.

## Den långa, begränsade parallella generationen

Längsform (`--template blog-article`) är ett annat djur . Du' genererar en multi- -sektionsdokument från dussintals olika källor, och du behöver en jämlikhet mellan sektioner utan dyra LLM kompressionsnummer.

Svaret är **[begränsad fuzzy kontext dragning](/blog/constrained-fuzzy-context-dragging)**. Det här är deterministiska mekanismer som håller kvar cross-

```mermaid
flowchart TD
    subgraph Phase1["Phase 1: Evidence Preparation (DETERMINISTIC)"]
        Articles[Top 20 Articles] --> Segments[Chunk into Segments]
        Segments --> Salience[Score Salience per Segment]
        Salience --> EmbedSeg[Embed Each Segment]
    end

    subgraph Phase2["Phase 2: Document Planning (1 SENTINEL CALL)"]
        EmbedSeg --> Summary[Build Evidence Summary]
        Summary --> Sentinel["Sentinel: Generate Outline - with theme keywords per section"]
        Sentinel --> EmbedThemes[Embed Section Themes]
    end

    subgraph Phase3["Phase 3: Evidence Assignment (DETERMINISTIC)"]
        EmbedThemes --> Assign["Score: 60% theme similarity - + 25% salience + 15% relevance"]
        Assign --> Dedup[Cross-Section Deduplication]
        Dedup --> Gate["Quality Gates - • Min salience 0.35 - • Min theme sim 0.45 - • Max 2 per source per section"]
    end

    subgraph Phase4["Phase 4: Section Generation (N+2 LLM CALLS)"]
        Gate --> Intro["Intro (sequential)"]
        Intro --> Body["N Body Sections (parallel) - max 3 concurrent"]
        Body --> Conclusion["Conclusion (sequential)"]
    end

    subgraph Phase5["Phase 5: Validation (DETERMINISTIC)"]
        Conclusion --> Validate[Citation Validation - URL + Entity Grounding]
    end

    subgraph Phase6["Phase 6: Assembly (DETERMINISTIC)"]
        Validate --> Assemble[Final Document Assembly]
    end

    style Phase1 fill:#0d3b2e,color:#e0e0e0
    style Phase2 fill:#1e3a5f,color:#e0e0e0
    style Phase3 fill:#0d3b2e,color:#e0e0e0
    style Phase4 fill:#1e3a5f,color:#e0e0e0
    style Phase5 fill:#0d3b2e,color:#e0e0e0
    style Phase6 fill:#0d3b2e,color:#e0e0e0
```

Tela LLM sändningar: **1** ( **1** (intro **N** (kroppssegmenter **1** ( slutsats **N+3**. Fyra av de sex stadierna är deterministiska

### Begränsat Fuzzy Kontexttdragning

Problemet med parallella sektionsgenerering är koherens.

Istället får varje sektion **begränsad kontext** att "'" byggdes deterministiskt.

```mermaid
flowchart LR
    subgraph Context["Per-Section Context (ALL DETERMINISTIC)"]
        RS["Running Summary - 1400 char budget - recent 2 sections: full - older: heading only"]
        NP["Negative Prompts - 'Do NOT discuss: X, Y, Z' - from covered concepts"]
        EC["Entity Continuity - re-introduce entities - last seen 2+ sections ago"]
        Props["Propositions - ~15 atomic facts - per section"]
        Evidence["Curated Evidence - max 12 segments - quality-gated"]
    end

    Context --> LLM["LLM generates - with full awareness - of document state"]

    style Context fill:#1a1a2e,color:#e0e0e0
```

- **Running sammanfattning**: efter varje sektion , uppsta sektionen - bevissegmentet för överlevnadsbekräftelse | (200 bokstäver | МSK4 | är sparat |. | De senaste ♫ 2 ♫ sektionerna får full digest ♫; | de äldre sektionernas får rubriker ♫
- **Negativa instruktioner**: utlägsna koncept är extraherade ( lämpliga ord M SK2 tekniska termer *"Bring inte att diskutera dessa ämnen.*
- **Enhetens kontinuitet**: varelser som inte är nämnt i 2+ sektionerna blir flaggade för att återskapa | | introduction | . | aktiva varelser | ( | nyligen nämns | МSK5 | blir spårade | LLM får vägledning | *"Re*
- **Drift-detektorn**: efter att varje sektion genererar , dess första | 1000 | tecken är inbäddade och jämförda mot planet | | ' | temainbäddar |

I parallell Modus, genererar insidan först ( ställer basen | ), | kropp sektioner körs samtidigt `SemaphoreSlim` att kapa sammanträffanden, var och en uteslutande introämnen men inte var och ett andra ), och slutsatsen sträcker sig ända M SK2 utschließande allting ).

Sekvationsmodiet ger en hårdare jämlikhet ( varje sektion utelämnar alla tidigare koncept kumulativt ), men parallellt är ~3x snabbare för stora dokument

### Evidens tillsignering: Hjärta av minskad RAG

Varje sektion behöver bevis, men inte *alla* bevis . Varje segment får en sammanlagd poäng: **60% tema likhet** (kosine avstånd till sektionen'medvetande om temat **25% relevantitet** ( hur informativ segmentet är **15% relevans** ( hur relevant artikeln i källan är i allmänhet ). Det här betyget är helt deterministiskt

Sedan dörrarna

- **Saliens golv** (0.35): låga - kvalitetsegmenter når aldrig LLM
- **Theme likhet golv** (0.45): avlägsna
- **Cross-sektionsdeduktion** (0.80 kosine ): samma sak inte uppstår två gånger
- **Per- källkapsel** (2 segmenter per artikel per sektion ): ingen enkel källa dominerar
- **Sektionens rollmedvetenhet**: inre sektioner straffar teknisk detalj (-0.3); senare sektioner belönar det
- **Kohäsionsgrind** (0.30): bevis måste relatera till den ursprungliga frågan , inte bara till sektionens tema

LLM-targets ordtaljusterar baserat på bevisets kvalitet. Stark bevis ( upplägsenhet ≥ | | 0.6, ♫ 2+ källor , \ 4+ segmenter M SK7 | МSK8 hela ordtal MSC9 svaga bevis ♫ ♫ upplägsamhet ♫

### Propositions extraktion: Dense-X i praktiken

Innan beviset når LLM, så atomiseras det i propositioner ( inspirerade av [Dense-X Retrieval papper](https://arxiv.org/abs/2312.06648)). Varje segment bryts ner i sex typer av atom fakta **påståenden** (allgemena faktaberättelser **citat** (direkt från källan **statistik** (tal och mätetal **definitioner** ("X är YM SK1 **processer** (steg **namngeda enhetens fakta** ( fokuserar på en specifik enhetM SK1

Varje sektion får ~15 propositioner , duplicerad över sektioner med hjälp av semantisk likhet ( inte strängmatchning). LLM får strukturerade bullet-punkter grouperade efter källanM SK4 inte råförteckningar

När LLM skriver en sektion

- En tydlig titel och tema
- Curated evidence segments (
- ~15 atomprophezioner
- En jooksande sammanfattning av vad tidigare sektioner redan täckte
- Negativa påtryckningar på vad *inte* att upprepa
- Enhetens kontinuitetsledning

En liten modell kan göra utmärkt arbete med denna typ av pre-processing.

## TextRank: Deterministisk sammanfattning

När artiklarna är för långa för beviset, måste jag komprimera dem. Men jag vill inte spendera en LLM-sändning på sammanfattningar.

TextRank (Mihalcea ♫ & ♫ Tarau ♫ МSK2 ♫ 2004) ♫ gör det deterministiskt ♫

```mermaid
flowchart LR
    Text[Article Text] --> Split[Split into Sentences]
    Split --> EmbedS[Embed Each Sentence]
    EmbedS --> Graph["Build Similarity Graph - (cosine > 0.15 = edge)"]
    Graph --> PR["PageRank - (20 iterations, d=0.85)"]
    PR --> Select["Select Top-K - in Original Order"]

    style Graph fill:#1e3a5f,color:#e0e0e0
```

Varje mening får ett inbäddat . Sammlingar i pelare riktning ovan 0.15 blir grafgränser . PageRank hittar mest *centrala* fraser , de som är mest kopplade till allt annat. De är de fraser som bäst representerar dokumentet

Det nyckeldetailet: som ausgewählta meningar kommer tillbaka **i originaldokumentsordning**. Det här bevarar berättelsens flöde . Du får en koherent sammanfattning

Inget LLM sändning. Den kör i millisekundar . Kosine likhet är SIMD- accelererad via `TensorPrimitives` (`System.Numerics.Tensors`) (AVX2, AVX-512, eller ARM NEON

## Ett-Hop Link följande

Artiklar antyder andra artiklar . Dessa referenser innehåller ofta de bästa bevisen - den primära källan som en nyhetsartikel sammanfattar

DoomSummarizer följer länkar , men selektivt. Varje kandidat länkar får poäng

```
link_score = 0.7 × query_relevance(anchor_context) + 0.3 × segment_salience
```

Där segments saliens kombinerar sig

- **Position** (60%) - inverterad pyramidM SK2 de tidigare paragraferna är viktigare
- **Substans** (40%) - radslängd och informationsdensitet

länkarna under relevansnivån (0.15) är överskridda . Systemet följer journalistiska inverterade pyramiderheuristiker: viktiga länkar tenderar att dyka upp tidigt i vält

Resultaten kastars med innehållshasher och ETags. Andra prover är snabba. Se [Response Caching , ETags, och villkorliga begäran](/blog/ASPNET-STATE-BETWEEN-REQUESTS).

## Crawl Mode: Att bygga en lokal kunskapsbas

Allt som har beskrivits så här långt antar att webben tar fram data. Men du kan hoppa över webben helt

`crawl` ingests en webbsida i en lokal kunskapsbas.

```bash
doomsummarizer crawl https://docs.example.com --name example-docs --depth 3 --max-pages 200
```

Allt blir uthärdigt: full content, ONNX inbäddarM SK2 entite-profiler , SQLite FTSMska4 nyckelindex Mske5fullaM Ska6 textsökningenMka7 känslor och temapoäng | Mska8 | beräknat genom inbäddade ankar |Mska9 | inga LLM | mska10 | ökade repetition | Мska11 | kråkor skicka `If-None-Match` / `If-Modified-Since` opskrifte. Orändrade sidor återvänder HTTP 304 och hoppar över omprocessing. För server utan ETag stödM SK3 SHAMska4 innehållshackar fånga kopiorMske5

Efter att ha bläddrat

```bash
doomsummarizer scroll "how does authentication work?" --name example-docs
```

Den `--name` vlagvägar till den lokala KB istället för webbkällorna. Samma tre - lagerutfrågning bränder, : FTS, 5 fulla, M SK4 textinlärning, Mska5 filter, M Ska6, inbäddad HNSW-sökning, Msaka7, enhetens profil, HNSV-sökningen, M ska8, de är ett samhälle, Maskan9, utblandad och rangerade, Mskan10, samma reduktionskanal, Mkan11, samma syntektion, Msakan12, ingen nätverking krävs.

Storageen är lättviktig: SQLite för metadata och FTS5 indexerM SK2 DuckDB med VSS-extensionen för HNSW-vektorindexer . Inga utomverkliga tjänster

## Att sätta ihop det

```bash
doomsummarizer scroll "AI safety and regulation" --vibe doom --debug

Composite query detected: 2 subqueries
  • What's new in AI safety?
  • What are the latest AI regulations?

Searching...
├─ Lucene: 18 keyword matches (regulation^3, safety^2)
├─ Embedding: 12 semantic matches (max-sim across 2 subqueries)
├─ RRF fusion: 22 candidates, 6 signals
├─ Entity HNSW: +3 related via entity profiles
├─ TextRank: compressed 4 long articles
└─ Final: 12 items

Long-form: Phase 1 - 187 segments from 12 articles
Long-form: Phase 2 - "AI Safety Landscape" - 5 sections
Long-form: Phase 3 - Evidence assigned (cross-section dedup: 8 removed)
Long-form: Phase 4 - Generating sections...
```

## De idéer som fick det att fungera

Det här är insikterna.

1. **Reduera innan du genererar**: standardvägen behöver endast 1 LLM sändning eftersom pipelinen gör allt det svåra att lyfta deterministiskt [Reduzerad RAG](/blog/reduced-rag) är mönstret, :, Embeddinger, ,, PageRank, M SK2, Heuristik och kvalitetgrindar skapar en sådan ren input att även små modeller kan syntetisera bra.
2. **Sentinel-driven källriktning**: en liten modell tolkar sökningar till strukturerad syfte , sedan YAML- styrd ruttering väljer och bedriver vibe
3. **Max likhet för komplexa queryr**: don ' inte medelvärde för underfrågan, resultat, , ta det bästa matchet, . Ett artikel som framkallar en del av din fråga är bättre än en halva,
4. **Kombinera ranker, inte poäng**: RRF låter dig sammanfoga signaler med okompatibla skalor . Behörde M SK2 Frihet , inbäddad likheter MSC4 vibbalignering
5. **Viber som inbäddar**: vilken text som helst kan vara en extraherande dimension . Inbädda den , räkna mot den, , sätta den i RRF, weight it in
6. **Enhetens profiler som vektorer**: IDF
7. **Begränsat fuzzy kontext dragning**: [deterministisk kontextmanagement](/blog/constrained-fuzzy-context-dragging) gör det möjligt att generera parallella sektioner. kör sammanfattningar , negativa prompter M SK2 entit kontinuitet , och driftdetektor upprätthåller en jämlikhet utan LLM kompressionsuppdrag
8. **Deterministisk när möjligt**: av de sex långa -formade stadierna M SK2 fyra är deterministiska . TextRank ersätter LLM sammanfattningen MSC4 bevisassignering ersätter lLM-selectionen.

## resurser

- **[DoomSummarizer-utsläpp](https://github.com/scottgal/lucidrag/releases?q=doomsummarizer)**: enkel binär
- **[lucidRAG Repository](https://github.com/scottgal/lucidrag)**: moderprojektet
- **[källkod](https://github.com/scottgal/lucidrag/tree/main/src/DoomSummarizer)**

### Related Articles

- [Reduzerad RAG](/blog/reduced-rag): reducera - före - skapa mönster i djup
- [Begränsat Fuzzy Kontexttdragning](/blog/constrained-fuzzy-context-dragging): hur parallella sektioner genereras för att upprätthålla jämlikhet
- [Begränsad otydighetsmönster](/blog/constrained-fuzziness-pattern): det bredare mönstret bakom deterministisk kontroll + möjlighetsmässiga förslag

### Även

- [Konsolimage (Time Boxed Tool)](/blog/timeboxedtool-consoleimage)