Add Docling corpus pipeline, retrieval evaluation and hybrid search with reranking

- prepare_corpus.py: split double-page scans (split_spreads.py), re-OCR with
  OCRmyPDF/Tesseract only where the existing text layer is worse, Docling
  layout extraction with heading hierarchy, QA report per PDF
- docling_loader.py: cleanup (margin noise, split paragraphs, footnote marks,
  OCR comma/period repair), page mapping back to the original PDF, chunk
  headers with author, title and section
- local_rag.py: bge-m3 embeddings, configurable chunking and loader, index
  settings check (index_info.json), hybrid/union BM25 search with Reciprocal
  Rank Fusion and bge-reranker-v2-m3 cross-encoder; query instruction text
  removed after it lowered recall
- eval_retrieval.py: CLI options, same retrieval path as the chat, tolerant
  file name matching; goldsets and evaluation reports for the Kafka corpus
- grobid_test.py, grobid_qa.py, extract_docling.py, compare_extractors.py:
  extractor tests and quality checks
- .gitignore: corpus PDFs, vector stores and extractor output stay local
- server.py: uses ./vectorstore_docling_hdr

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-07 16:48:12 +02:00
co-authored by Claude Opus 5.5
parent 7d70169ee3
commit d54b3e5b8b
49 changed files with 5007 additions and 75766 deletions
+26
View File
@@ -8,12 +8,29 @@ venv/
env/ env/
ENV/ ENV/
# Korpus-PDFs (urheberrechtlich geschützt, nicht ins Repository)
data/
data_test/
# Vector store # Vector store
vectorstore/ vectorstore/
# Uploads # Uploads
uploads/ uploads/
# GROBID-Testausgabe
grobid_out/
# Extraktor-Vergleich (Docling u. a.)
extract_out/
# Tesseract-Sprachmodelle (heruntergeladen)
tessdata/
# Aufbereitetes Korpus (prepare_corpus.py) und Vergleichsindizes
corpus_processed/
vectorstore_*/
# IDE # IDE
.vscode/ .vscode/
.idea/ .idea/
@@ -23,3 +40,12 @@ uploads/
# OS # OS
.DS_Store .DS_Store
Thumbs.db Thumbs.db
# Secrets / API keys (never commit these)
.env
.env.*
*.key
*.pem
*key*.txt
*key*.docx
*key*.zip
+40
View File
@@ -146,6 +146,46 @@ Open [http://127.0.0.1:8000](http://127.0.0.1:8000) or [http://localhost:8000](h
| `GET /api/providers` | Which providers are available (`openai` false if no API key at startup) | | `GET /api/providers` | Which providers are available (`openai` false if no API key at startup) |
| `GET /api/health` | Server and whether a vector store is loaded | | `GET /api/health` | Server and whether a vector store is loaded |
**`POST /api/chat` response fields:**
| Field | Type | Meaning |
|-------|------|---------|
| `answer` | string | The generated answer (empty string on error) |
| `error` | string or `null` | Error message, e.g. empty question or OpenAI not configured |
| `retrieved` | list or `null` | The chunks used as context for this answer |
Each entry in `retrieved` has:
| Field | Type | Meaning |
|-------|------|---------|
| `content` | string | The chunk text handed to the LLM |
| `source` | string | Path of the file the chunk came from |
| `page` | int or `null` | Page number (PDFs only) |
| `score` | float or `null` | FAISS **L2 distance** – **lower = more similar** (it is a distance, not a similarity percentage) |
---
## Inspect what was retrieved (debugging)
Every answer reports which chunks it was based on, in two places:
- **Browser:** open the developer console (Chrome/Edge: `F12` or `Cmd`+`Option`+`I`, Firefox: `F12`) before sending a question. Each answer logs a collapsed group `[RAG] Retrieved N chunk(s)` with the full text, source file and page of every chunk.
- **Server terminal:** the terminal running `uvicorn` prints the rephrased standalone query, the retrieval query, the L2 distances, a 150-character preview of each chunk, and the raw LLM response.
Use this when an answer looks wrong: if the right chunks were never retrieved, tune `RETRIEVAL_K`, `QUERY_ADDITIONAL_INSTRUCTIONS` or the chunk size; if the chunks were correct but the answer was not, tune `ANSWER_PROMPT` (see [Configuration reference](#configuration-reference)).
---
## Quick check from the command line
[local_rag.py](local_rag.py) can be run directly for a smoke test without the web server:
```bash
python local_rag.py
```
It loads the existing vector store, lists the documents in it, and runs **one hardcoded example question** through Ollama. This is only a demo: edit the `question` variable in `main()` to ask something else, and note that it always uses Ollama (the provider is not configurable from the command line). For normal use, run the web app instead.
--- ---
## How it works (high level) ## How it works (high level)
+848
View File
@@ -0,0 +1,848 @@
<title>Erst Retrieval, dann Generierung</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Newsreader:ital,opsz,wght@0,6..72,400;0,6..72,600;1,6..72,400&family=IBM+Plex+Mono:wght@400;600&family=IBM+Plex+Sans:ital,wght@0,400;0,500;0,600;1,400&display=swap">
<style>
html { hyphens: auto; }
:root {
--paper: #eff1f3;
--surface: #ffffff;
--surface-2: #e6e9ed;
--ink: #14161a;
--ink-2: #4d545f;
--ink-3: #79818e;
--rule: #d3d8de;
--accent: #146f68;
--accent-soft:#d5e8e6;
--cut: #a3302a;
--cut-soft: #f2dedc;
--mark: #f0e2b4;
--shadow: 0 1px 2px rgba(20,22,26,.06), 0 8px 24px -16px rgba(20,22,26,.28);
--serif: "Newsreader", Georgia, "Times New Roman", serif;
--sans: "IBM Plex Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
--mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--paper: #131519;
--surface: #1a1d23;
--surface-2: #22262d;
--ink: #e7e9ed;
--ink-2: #a2aab6;
--ink-3: #7b838f;
--rule: #2d323b;
--accent: #57b8ae;
--accent-soft:#173b39;
--cut: #e2796f;
--cut-soft: #3a201e;
--mark: #453a1a;
--shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px -16px rgba(0,0,0,.8);
}
}
:root[data-theme="dark"] {
--paper: #131519;
--surface: #1a1d23;
--surface-2: #22262d;
--ink: #e7e9ed;
--ink-2: #a2aab6;
--ink-3: #7b838f;
--rule: #2d323b;
--accent: #57b8ae;
--accent-soft:#173b39;
--cut: #e2796f;
--cut-soft: #3a201e;
--mark: #453a1a;
--shadow: 0 1px 2px rgba(0,0,0,.4), 0 8px 24px -16px rgba(0,0,0,.8);
}
* { box-sizing: border-box; }
body {
background: var(--paper);
color: var(--ink);
font-family: var(--sans);
font-size: 16px;
line-height: 1.62;
-webkit-font-smoothing: antialiased;
}
.wrap {
max-width: 50rem;
margin: 0 auto;
padding: 3.5rem 1.5rem 6rem;
}
/* ---------- masthead ---------- */
.masthead { border-bottom: 2px solid var(--ink); padding-bottom: 1.75rem; }
.eyebrow {
font-family: var(--mono);
font-size: .72rem;
letter-spacing: .13em;
text-transform: uppercase;
color: var(--accent);
margin: 0 0 1rem;
}
h1 {
font-family: var(--serif);
font-weight: 600;
font-size: clamp(2.4rem, 6vw, 3.6rem);
line-height: 1.04;
letter-spacing: -.02em;
margin: 0 0 1rem;
text-wrap: balance;
}
.standfirst {
font-family: var(--serif);
font-size: 1.24rem;
line-height: 1.5;
color: var(--ink-2);
margin: 0;
max-width: 40rem;
}
.masthead-meta {
display: flex;
flex-wrap: wrap;
gap: .5rem 1.5rem;
margin-top: 1.5rem;
font-family: var(--mono);
font-size: .74rem;
color: var(--ink-3);
}
.masthead-meta span { white-space: nowrap; }
/* ---------- the rule callout ---------- */
.thesis {
margin: 2.5rem 0 0;
padding: 1.5rem 1.75rem;
background: var(--surface);
border-left: 3px solid var(--accent);
box-shadow: var(--shadow);
}
.thesis p { margin: 0 0 .9rem; }
.thesis p:last-child { margin-bottom: 0; }
.chain {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: .4rem .55rem;
margin: 1.25rem 0 0;
font-family: var(--mono);
font-size: .78rem;
}
.chain b {
font-weight: 400;
background: var(--surface-2);
border: 1px solid var(--rule);
padding: .22rem .55rem;
color: var(--ink-2);
}
.chain b.live { background: var(--accent-soft); border-color: var(--accent); color: var(--ink); }
.chain i { font-style: normal; color: var(--ink-3); }
/* ---------- phases ---------- */
.phase { margin-top: 3.5rem; }
.phase-head {
display: grid;
grid-template-columns: 3.25rem 1fr;
gap: 0 1rem;
align-items: baseline;
border-top: 1px solid var(--rule);
padding-top: 1.1rem;
}
.num {
font-family: var(--mono);
font-size: .95rem;
font-weight: 600;
color: var(--accent);
letter-spacing: .04em;
}
h2 {
font-family: var(--serif);
font-weight: 600;
font-size: 1.85rem;
line-height: 1.15;
letter-spacing: -.015em;
margin: 0;
text-wrap: balance;
}
.phase-sub {
grid-column: 2;
font-family: var(--mono);
font-size: .73rem;
letter-spacing: .07em;
text-transform: uppercase;
color: var(--ink-3);
margin: .5rem 0 0;
}
.phase-body { margin-left: 0; }
@media (min-width: 40rem) { .phase-body { margin-left: 4.25rem; } }
h3 {
font-family: var(--sans);
font-weight: 600;
font-size: 1rem;
letter-spacing: -.005em;
margin: 2rem 0 .6rem;
}
p { margin: 0 0 1rem; max-width: 42rem; }
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
a:focus-visible, summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
ul, ol { margin: 0 0 1rem; padding-left: 1.2rem; max-width: 42rem; }
li { margin-bottom: .5rem; }
li::marker { color: var(--ink-3); }
strong { font-weight: 600; }
em.term { font-style: normal; background: var(--mark); padding: 0 .2em; }
code {
font-family: var(--mono);
font-size: .855em;
background: var(--surface-2);
padding: .1em .35em;
border-radius: 2px;
overflow-wrap: break-word;
}
pre {
font-family: var(--mono);
font-size: .8rem;
line-height: 1.62;
background: var(--surface);
border: 1px solid var(--rule);
border-left: 3px solid var(--ink-3);
padding: 1rem 1.15rem;
overflow-x: auto;
margin: 0 0 1.25rem;
}
pre code { background: none; padding: 0; font-size: 1em; }
pre .c { color: var(--ink-3); }
/* ---------- defect list ---------- */
.defects { display: grid; gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin-bottom: 1.5rem; }
.defect { background: var(--surface); padding: 1.1rem 1.25rem; }
.defect h4 {
font-family: var(--sans);
font-size: .95rem;
font-weight: 600;
margin: 0 0 .4rem;
display: flex;
gap: .6rem;
align-items: baseline;
}
.defect h4 .sev {
font-family: var(--mono);
font-size: .64rem;
letter-spacing: .1em;
text-transform: uppercase;
padding: .12rem .4rem;
white-space: nowrap;
border: 1px solid currentColor;
}
.sev.high { color: var(--accent); background: var(--accent-soft); }
.sev.med { color: var(--ink-3); background: var(--surface-2); }
.defect p { margin: 0; font-size: .93rem; color: var(--ink-2); max-width: none; }
.defect p + p { margin-top: .55rem; }
/* ---------- Glossar ---------- */
.gloss {
display: grid;
gap: 1px;
background: var(--rule);
border: 1px solid var(--rule);
margin: 0 0 1.5rem;
}
.gloss > div {
background: var(--surface);
padding: .75rem 1.1rem;
display: grid;
grid-template-columns: 1fr;
gap: .1rem;
}
@media (min-width: 34rem) {
.gloss > div { grid-template-columns: 11rem 1fr; gap: 0 1.25rem; }
}
.gloss dt { font-weight: 600; font-size: .93rem; }
.gloss dd { margin: 0; font-size: .92rem; color: var(--ink-2); }
/* ---------- keep / cut ---------- */
.kc { display: grid; grid-template-columns: 1fr; gap: 1px; background: var(--rule); border: 1px solid var(--rule); margin-bottom: 1.5rem; }
@media (min-width: 34rem) { .kc { grid-template-columns: 1fr 1fr; } }
.kc-col { background: var(--surface); padding: 1.1rem 1.25rem; }
.kc-col h4 {
font-family: var(--mono);
font-size: .7rem;
letter-spacing: .12em;
text-transform: uppercase;
margin: 0 0 .75rem;
}
.kc-keep h4 { color: var(--accent); }
.kc-cut h4 { color: var(--cut); }
.kc-col ul { list-style: none; padding: 0; margin: 0; font-size: .9rem; }
.kc-col li { margin-bottom: .45rem; color: var(--ink-2); padding-left: 1rem; position: relative; }
.kc-col li::before { position: absolute; left: 0; font-family: var(--mono); }
.kc-keep li::before { content: "+"; color: var(--accent); }
.kc-cut li::before { content: "\2212"; color: var(--cut); }
/* ---------- tables ---------- */
.tw { overflow-x: auto; margin: 0 0 1.5rem; border: 1px solid var(--rule); background: var(--surface); }
table { border-collapse: collapse; width: 100%; font-size: .88rem; min-width: 34rem; }
th, td { text-align: left; padding: .7rem .9rem; vertical-align: top; border-bottom: 1px solid var(--rule); }
thead th {
font-family: var(--mono);
font-size: .68rem;
letter-spacing: .09em;
text-transform: uppercase;
color: var(--ink-3);
font-weight: 400;
background: var(--surface-2);
white-space: nowrap;
}
tbody tr:last-child td { border-bottom: none; }
/* Fließtextzellen (.note) umbrechen; kurze Bezeichner bleiben einzeilig. */
td:first-child:not(.note) { font-weight: 600; }
td:not(.note) { white-space: nowrap; }
td.note { color: var(--ink-2); }
/* ---------- callout ---------- */
.aside {
border: 1px solid var(--rule);
border-top: 2px solid var(--cut);
background: var(--surface);
padding: 1.1rem 1.25rem;
margin: 0 0 1.5rem;
}
.aside h4 {
font-family: var(--mono);
font-size: .7rem;
letter-spacing: .12em;
text-transform: uppercase;
color: var(--cut);
margin: 0 0 .6rem;
}
.aside p { margin: 0; font-size: .93rem; color: var(--ink-2); }
.aside p + p { margin-top: .6rem; }
/* ---------- checklist ---------- */
.checklist { list-style: none; padding: 0; margin: 0; counter-reset: ck; max-width: none; }
.checklist li {
counter-increment: ck;
display: grid;
grid-template-columns: 2rem 1fr;
gap: .75rem;
padding: .8rem 0;
border-bottom: 1px solid var(--rule);
margin: 0;
}
.checklist li::before {
content: counter(ck, decimal-leading-zero);
font-family: var(--mono);
font-size: .78rem;
color: var(--ink-3);
padding-top: .15rem;
}
.checklist li > b,
.checklist li > span { grid-column: 2; }
.checklist b { display: block; font-weight: 600; }
.checklist span { color: var(--ink-2); font-size: .92rem; }
footer {
margin-top: 4rem;
padding-top: 1.25rem;
border-top: 2px solid var(--ink);
font-family: var(--mono);
font-size: .74rem;
color: var(--ink-3);
}
@media (prefers-reduced-motion: reduce) {
* { animation: none !important; transition: none !important; }
}
/* ---------- print / PDF ---------- */
@page {
size: A4;
margin: 16mm 15mm 18mm;
}
@media print {
/* Always print the light palette, whatever the OS is set to. */
:root, :root[data-theme="dark"], :root:not([data-theme="light"]) {
--paper: #ffffff;
--surface: #ffffff;
--surface-2: #eef0f3;
--ink: #14161a;
--ink-2: #434954;
--ink-3: #6d7581;
--rule: #c8ced6;
--accent: #10605a;
--accent-soft:#dcecea;
--cut: #932b25;
--cut-soft: #f5e3e1;
--mark: #f2e6bd;
--shadow: none;
}
* { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
body { font-size: 10.2pt; line-height: 1.5; background: #fff; }
.wrap { max-width: none; padding: 0; }
/* Let tables use the full measure instead of scrolling. */
.tw { overflow: visible; }
table { min-width: 0; font-size: 8.8pt; }
th, td { padding: .42rem .5rem; }
td:first-child { white-space: normal; }
p, ul, ol { max-width: none; }
pre { font-size: 8.2pt; white-space: pre-wrap; word-break: break-word; }
h1 { font-size: 30pt; }
h2 { font-size: 16pt; }
h3 { font-size: 10.8pt; margin-top: 1.15rem; }
.masthead { padding-bottom: 1.1rem; }
.thesis { margin-top: 1.5rem; padding: 1rem 1.2rem; }
.phase { margin-top: 1.9rem; }
.phase-body { margin-left: 3.4rem; }
/* Keep composed blocks whole across page boundaries. */
.thesis, .defect, .kc-col, .aside, .tw, pre, .chain, .gloss > div,
.checklist li, .phase-head { break-inside: avoid; page-break-inside: avoid; }
.phase-head, h3 { break-after: avoid; page-break-after: avoid; }
a { color: var(--ink); text-decoration: none; }
footer { margin-top: 2rem; }
}
</style>
<div class="wrap" lang="de">
<header class="masthead">
<p class="eyebrow">Übergabenotiz · Tuning des RAG-Korpus</p>
<h1>Erst Retrieval, dann Generierung</h1>
<p class="standfirst">Eine Arbeitsanleitung zum Tuning der local_rag-Pipeline für ein zweiteiliges Korpus aus literarischen Primärtexten und wissenschaftlicher Sekundärliteratur. Was sich zu probieren lohnt, in welcher Reihenfolge — und woran sich erkennen lässt, ob es etwas gebracht hat.</p>
<div class="masthead-meta">
<span>Projekt: local_rag</span>
<span>Stack: LangChain · FAISS · MiniLM · Ollama/OpenAI</span>
<span>Zielgruppe: wer die Pipeline als Nächstes betreut</span>
</div>
</header>
<div class="thesis">
<p><strong>Die eine Regel: von vorne nach hinten arbeiten und nach jedem Schritt messen.</strong> Probleme der Retrieval-Qualität <em>zeigen</em> sich immer am Ende der Kette — eine schwache Antwort, eine erfundene Behauptung, ein unpassendes Zitat. <em>Verursacht</em> werden sie fast immer weiter vorne: dabei, wie aus einem PDF Text wird und wie dieser Text zerteilt und eingebettet wird.</p>
<p>Praktische Konsequenz: der Versuchung widerstehen, zuerst den Prompt anzupassen. Wer den Antwort-Prompt erweitert, obwohl die abgerufenen Passagen schlecht sind, bringt dem Modell lediglich bei, elegant um schlechte Belege herumzuschreiben — und die scheinbare Verbesserung verdeckt die eigentliche Ursache für alle, die später hinsehen.</p>
<div class="chain">
<b class="live">Extraktion</b><i>&rarr;</i><b class="live">Chunking</b><i>&rarr;</i><b class="live">Embedding</b><i>&rarr;</i><b class="live">Retrieval</b><i>&rarr;</i><b class="live">Reranking</b><i>&rarr;</i><b>Prompt</b><i>&rarr;</i><b>Generierung</b>
<i>&nbsp;&nbsp;türkis = in dieser Notiz behandelt</i>
</div>
</div>
<!-- ===================== GLOSSAR ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">&#8801;</div>
<h2>Kurzes Glossar</h2>
<p class="phase-sub">Die Begriffe, die unten vorausgesetzt werden</p>
</div>
<div class="phase-body">
<p>Wer nicht täglich mit solchen Systemen arbeitet, stolpert weiter unten über ein knappes Dutzend Fachwörter. Sie sind hier einmal in Alltagssprache erklärt; das genügt, um die ganze Notiz zu verstehen.</p>
<dl class="gloss">
<div><dt>Chunk</dt><dd>Ein Textabschnitt. Dokumente werden vor dem Einlesen in solche Stücke zerschnitten, weil immer nur einzelne Abschnitte gesucht und an das Sprachmodell weitergereicht werden, nie ganze Bücher.</dd></div>
<div><dt>Embedding</dt><dd>Eine lange Zahlenreihe, die den Inhalt eines Textstücks ausdrückt. Texte mit ähnlicher Bedeutung bekommen ähnliche Zahlenreihen — dadurch lässt sich Ähnlichkeit rechnen statt nur Wörter vergleichen.</dd></div>
<div><dt>Vektor</dt><dd>Das mathematische Wort für ebendiese Zahlenreihe. „Zwei Vektoren liegen nah beieinander" heißt: die beiden Texte ähneln sich inhaltlich.</dd></div>
<div><dt>Vektorspeicher, Index</dt><dd>Die durchsuchbare Ablage aller Chunks samt ihren Zahlenreihen. Im Projekt ist das der Ordner <code>vectorstore</code>. Er wird erzeugt, nicht von Hand gepflegt, und kann jederzeit neu gebaut werden.</dd></div>
<div><dt>Retrieval</dt><dd>Das Heraussuchen der passenden Chunks zu einer Frage. Der Schritt, um den es in dieser Notiz fast ausschließlich geht — noch bevor ein Sprachmodell überhaupt beteiligt ist.</dd></div>
<div><dt>Token</dt><dd>Die Recheneinheit für Text, ungefähr ein Wortteil. Ein Modell mit einer Grenze von 256 Token kann also nur etwa 150 bis 200 deutsche Wörter auf einmal verarbeiten.</dd></div>
<div><dt>dense / sparse</dt><dd>Zwei Suchprinzipien. <em>Dense</em> sucht nach Bedeutungsähnlichkeit und findet auch Umschreibungen; <em>sparse</em> sucht nach den tatsächlichen Wörtern und ist bei Eigennamen und Zitaten überlegen. Beides zusammen heißt hybride Suche.</dd></div>
<div><dt>Reranking</dt><dd>Ein zweiter, genauerer Sortierdurchgang: Man holt bewusst viele Kandidaten und lässt sie von einem präziseren Verfahren neu ordnen, bevor die besten weitergegeben werden.</dd></div>
<div><dt>Prompt</dt><dd>Der Text, den das Sprachmodell tatsächlich zu sehen bekommt — Frage, Gesprächsverlauf und die gefundenen Chunks in einem Stück. Steht in <code>local_rag.py</code>.</dd></div>
<div><dt>Repository</dt><dd>Der Projektordner unter Versionsverwaltung (Git). „Gehört ins Repository" heißt schlicht: als Datei im Projekt ablegen, damit die Änderung nachvollziehbar bleibt.</dd></div>
</dl>
</div>
</section>
<!-- ===================== SYMPTOME ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">&there4;</div>
<h2>Vom Symptom ausgehen</h2>
<p class="phase-sub">Wo zu suchen ist, wenn etwas nicht stimmt</p>
</div>
<div class="phase-body">
<p>Wer bereits konkrete Probleme identifiziert hat, findet hier die Phase, die dafür am wahrscheinlichsten verantwortlich ist. Bei allgemeinem Tuning, ohne einem Symptom nachzugehen, werden die Phasen einfach der Reihe nach durchgearbeitet.</p>
<div class="tw">
<table>
<thead>
<tr><th>Beobachtung</th><th>Wahrscheinliche Ursache</th><th>Siehe</th></tr>
</thead>
<tbody>
<tr>
<td class="note">Chunks enthalten Fußnoten, Literaturangaben oder Kolumnentitel mitten im Fließtext</td>
<td class="note">Flache Textextraktion, die die Seitenstruktur ignoriert</td>
<td>Phase 02</td>
</tr>
<tr>
<td class="note">Chunks brechen mitten im Satz ab; Zitate kommen unvollständig an</td>
<td class="note">Zeichenbasiertes Splitting ohne Rücksicht auf Absatzgrenzen</td>
<td>Phase 03</td>
</tr>
<tr>
<td class="note">Eine Passage, von der bekannt ist, dass es sie gibt, wird nie gefunden — egal wie die Frage formuliert wird</td>
<td class="note">Trunkierung durch das Embedding-Modell oder eine Sprachdiskrepanz</td>
<td>Phase 01, 03</td>
</tr>
<tr>
<td class="note">Suchen nach Eigennamen, Kunstwörtern oder Werktiteln laufen ins Leere</td>
<td class="note">Kein lexikalischer Abgleich — rein dense Retrieval rät semantisch</td>
<td>Phase 04</td>
</tr>
<tr>
<td class="note">Es kommen ungefähr passende Passagen zurück, aber nie die <em>beste</em> zuerst</td>
<td class="note">Es fehlt eine Reranking-Stufe</td>
<td>Phase 04</td>
</tr>
<tr>
<td class="note">Die abgerufenen Passagen überzeugen beim Lesen, die Antwort bleibt trotzdem schwach</td>
<td class="note">Tatsächlich ein Generierungsproblem — der einzige Fall, in dem der Prompt die richtige Stellschraube ist</td>
<td>Nach Phase 04</td>
</tr>
</tbody>
</table>
</div>
</div>
</section>
<!-- ===================== 00 ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">00</div>
<h2>Einen Maßstab schaffen, bevor irgendetwas geändert wird</h2>
<p class="phase-sub">Ein halber Tag · Voraussetzung für alles Weitere</p>
</div>
<div class="phase-body">
<p>Jede Änderung hinterlässt den Eindruck, es sei besser oder schlechter geworden. Dies ist der Versuch, diese Veränderungen etwas strukturierter auf ihre Qualität zu überprüfen. Diese Phase lohnt sich selbst dann, wenn sonst nichts geändert wird.</p>
<h3>Einen Satz von 25–40 Referenzfragen anlegen</h3>
<p>Echte Fragen aus der tatsächlichen Forschungsarbeit, verteilt über beide Korpora und über verschiedene Fragetypen: eine Faktenfrage an einen Primärtext, eine Frage zur Deutung einer Passage, eine „wer vertritt welche These zu X"-Frage an die Sekundärliteratur, dazu zwei bis drei Fragen, deren Antwort beide Korpora berührt. Zu jeder Frage wird festgehalten, in welcher Datei und auf welcher Seite die Antwort tatsächlich steht.</p>
<pre><code><span class="c"># eval/goldset.csv</span>
frage,korpus,erwartete_quelle,erwartete_seite,anmerkung
"Wie wird Odradek beschrieben?",primaer,hausvater.pdf,3,"Beschreibung, nicht Deutung"
"Welche Rolle spielt das Gesetz in Vor dem Gesetz?",primaer,prozess.pdf,182,
"Wer liest die Verwandlung als Familienkonflikt?",sekundaer,mueller_2003.pdf,44,"Argument im Fließtext"</code></pre>
<h3>Das Retrieval für sich bewerten, ohne LLM in der Schleife</h3>
<p>Die Bewertung des Retrievals braucht keine Generierung und ist deshalb kostenlos, deterministisch und in Sekunden statt Minuten durch. Ein kurzes Skript lädt den Vektorspeicher, stellt jede Referenzfrage und berechnet <strong>Recall@k</strong> — taucht die erwartete Quelle mit der erwarteten Seite unter den ersten <em>k</em> Treffern auf? — für k = 5, 10 und 20, dazu den Mean Reciprocal Rank.</p>
<p>Dieser Ausgangswert gehört als schlichte Textdatei ins Repository. Jede spätere Änderung wird damit zur Differenz gegenüber einer Zahl, und eine Änderung, die diese Zahl nicht bewegt, lässt sich guten Gewissens zurücknehmen.</p>
<h3>Das Skript liegt bereit: <code>eval_retrieval.py</code></h3>
<p>Es ist absichtlich schlicht gehalten und lässt sich ohne Programmierkenntnisse bedienen. Alle Einstellungen stehen als Großbuchstaben-Zeilen oben in der Datei; geändert wird dort und sonst nirgends.</p>
<ol class="checklist">
<li><b>Eigene Fragen eintragen</b><span><code>eval/goldset.csv</code> in Excel, Numbers oder LibreOffice öffnen und die drei Beispielzeilen durch echte Fragen ersetzen. <em>frage</em>: die Frage selbst. <em>korpus</em>: primaer oder sekundaer, damit beide Hälften getrennt ausgewertet werden. <em>erwartete_quelle</em>: der Dateiname, in dem die Antwort steht — ohne Pfad, Groß- und Kleinschreibung egal. <em>erwartete_seite</em>: die Seitenzahl, die der PDF-Betrachter anzeigt; im Zweifel leer lassen, dann zählt nur die Datei. <em>anmerkung</em>: freies Feld für Notizen. Als CSV speichern — ob das Tabellenprogramm Kommas oder Semikolons schreibt, erkennt das Skript selbst.</span></li>
<li><b>Skript starten</b><span>Im Terminal, im Projektordner. Der erste Befehl schaltet die für dieses Projekt eingerichtete Python-Umgebung ein — das ist nötig, weil dort die passenden Zusatzpakete liegen; ohne ihn bricht der zweite Befehl mit einer Fehlermeldung über fehlende Module ab. Ollama muss an dieser Stelle nicht laufen, ein OpenAI-Schlüssel wird nicht gebraucht.</span></li>
<li><b>Zahlen lesen</b><span><em>Recall@5</em> ist der Anteil der Fragen, deren Fundstelle unter den ersten fünf Treffern steht: 0.60 heißt drei von fünf Fragen. <em>MRR</em> beschreibt, wie weit oben die Treffer stehen — 1.00 wäre immer Platz eins, 0.50 im Schnitt Platz zwei. In der Liste der Einzelfragen bedeutet ein Strich statt einer Rangzahl, dass die Stelle unter den ersten 20 Treffern gar nicht auftauchte; diese Fragen sind die interessantesten.</span></li>
<li><b>Bericht aufheben</b><span>Das Skript schreibt <code>eval/baseline.txt</code>. Diese Datei ins Repository legen und bei jeder späteren Messung mit einer kurzen Notiz versehen, was zwischenzeitlich geändert wurde. Genau dieser Vergleich ist der Zweck der Übung.</span></li>
<li><b>Gegenprobe zum Zusatztext</b><span>Oben im Skript steht <code>USE_QUERY_INSTRUCTIONS</code>. Auf <code>True</code> gesetzt hängt das Skript den Zusatztext aus <code>local_rag.py</code> an jede Frage an. Ein Lauf mit <code>True</code> und einer mit <code>False</code> beantworten die Frage aus Phase 01, ob dieser Text tatsächlich hilft.</span></li>
</ol>
<pre><code>conda activate local_rag
python eval_retrieval.py</code></pre>
<div class="aside">
<h4>Was das Skript nicht tut, und was schiefgehen kann</h4>
<p>Es bewertet ausschließlich, <em>ob die richtige Stelle gefunden wird</em> — nicht, ob die Antwort gut formuliert ist. Das ist Absicht: Antwortqualität lässt sich erst sinnvoll beurteilen, wenn die Belege stimmen.</p>
<p>Wenn plötzlich alle Fragen ohne Treffer bleiben, liegt es fast immer an einer von drei Kleinigkeiten: Der Dateiname in der CSV weicht von dem im Index ab; der Index wurde seit dem letzten Umbau nicht neu erzeugt; oder die Seitenzahl stimmt nicht. Beim Letzten hilft das Skript bereits mit — es rechnet die menschliche Zählung ab 1 in die maschinelle ab 0 um und lässt eine Seite Abweichung zu, weil ein Chunk über einen Seitenumbruch reichen kann. Wer ganz sicher gehen will, lässt die Seitenspalte zunächst leer und ergänzt sie später.</p>
</div>
<h3>Zwanzig zufällige Chunks selbst lesen</h3>
<p>Zwanzig Chunks direkt aus dem Speicher exportieren und als Mensch lesen. Beachtenswert sind: Sätze, die mitten im Teilsatz abbrechen; Anmerkungen, die in den Fließtext hineinragen; Seitenzahlen und Spaltentitel mitten im Absatz; Trennstriche, die vom Zeilenumbruch übrig geblieben sind; Literaturverzeichniszeilen, die wie Fließtext indexiert wurden. Grob schätzen, welcher Anteil eines Chunks Material ist, auf das man eine Antwort wirklich stützen möchte. Dieser Anteil ist der zweite Ausgangswert — und meist der aufschlussreichere von beiden.</p>
</div>
</section>
<!-- ===================== 01 ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">01</div>
<h2>Konfigurationsfallen, die noch geprüft werden sollten</h2>
<p class="phase-sub">Schnell geprüft · unverhältnismäßig große Wirkung</p>
</div>
<div class="phase-body">
<p>Vier Eigenschaften eines Setups aus LangChain, FAISS und sentence-transformers, die das Retrieval unbemerkt verschlechtern und leicht zu übersehen sind.</p>
<div class="defects">
<div class="defect">
<h4><span class="sev high">Zuerst prüfen</span> Chunk-Größe über dem Kontextfenster des Embedding-Modells</h4>
<p>Jedes sentence-transformer-Modell hat eine maximale Sequenzlänge, und Text darüber hinaus wird <strong>stillschweigend verworfen, bevor der Vektor berechnet wird</strong> — ohne Warnung, ohne Fehler. Liegt die Chunk-Größe über diesem Fenster, ist das Ende jedes Chunks nicht durchsuchbar: es kann nie auf eine Frage passen, landet aber trotzdem im Kontext des LLM, sobald der verbleibende Anfang passt.</p>
<p>Bei den üblichen Voreinstellungen fällt das deutlich ins Gewicht. Das Modell <code>all-MiniLM-L6-v2</code> endet bei 256 Token — das sind rund 900 bis 1.100 Zeichen deutscher Prosa — während <code>CHUNK_SIZE</code> in <code>local_rag.py</code> die Länge der Textstücke in Zeichen angibt und oft auf 1.000 bis 2.000 steht. Beide Zahlen müssen zusammenpassen.</p>
<p>Nachsehen lässt sich die Modellgrenze mit drei Zeilen: eine Datei namens <code>check.py</code> im Projektordner anlegen, den folgenden Text hineinkopieren und sie mit <code>python check.py</code> starten. Die ausgegebene Zahl ist die Obergrenze in Token; liegt <code>CHUNK_SIZE</code> in Zeichen deutlich über dem Dreifachen davon, wird abgeschnitten. Abhilfe: entweder die Chunks verkleinern oder auf ein Modell mit größerem Fenster wechseln (Phase 03).</p>
<pre><code>from local_rag import LocalRAG
rag = LocalRAG()
print(rag.embeddings.client.max_seq_length)</code></pre>
</div>
<div class="defect">
<h4><span class="sev high">Zuerst prüfen</span> Natürlichsprachliche Anweisungen in der Embedding-Query</h4>
<p>Es liegt nahe, das Retrieval zu steuern, indem man der Suchanfrage einen Satz anhängt — etwa „bevorzuge argumentierenden Fließtext, keine Literaturlisten". Embedding-Modelle lesen jedoch keine Anweisungen und besitzen keine Repräsentation von Verneinung: Der Satz wird schlicht in einen Vektor überführt und in die Anfrage eingemittelt, was jede Suche in Richtung von Text zieht, der <em>über</em> Literaturangaben und Argumente spricht. Er zieht das gemeinte Material also ebenso wahrscheinlich an, wie er es abstößt.</p>
<p>Retrieval steuert man über die Ingestion (Phase 02) und über Metadatenfilter (Phase 03), nicht über die Formulierung der Anfrage. Ist eine solche Zeichenkette im Einsatz, sollte Recall@k mit und ohne sie gemessen werden — man muss wissen, in welche Richtung sie tatsächlich gewirkt hat, bevor man entscheidet.</p>
</div>
<div class="defect">
<h4><span class="sev med">Hygiene</span> Rohe L2-Distanz auf nicht normalisierten Vektoren</h4>
<p>Es gibt zwei gängige Arten, den Abstand zweier Zahlenreihen zu messen. Die eine achtet nur auf die <em>Richtung</em> (Kosinus-Ähnlichkeit), die andere zusätzlich auf die <em>Länge</em> der Reihe (L2-Distanz). Die bisher verwendeten Modelle sind auf die erste Art trainiert. Misst man trotzdem nach der zweiten, entscheidet die Länge mit — und die hängt an Umfang und Wiederholungsgrad eines Chunks, weshalb lange, nichtssagende Textbausteine bevorzugt werden.</p>
<p>Die Umstellung ist eine einzelne Zeile: beim Laden und Erzeugen des Speichers in <code>local_rag.py</code> zusätzlich <code>distance_strategy=DistanceStrategy.COSINE</code> übergeben. Nebeneffekt: Die Ähnlichkeitswerte im Protokoll werden dadurch untereinander vergleichbar.</p>
</div>
<div class="defect">
<h4><span class="sev med">Hygiene</span> Ein Index, der nicht mehr zum Code passt, der ihn liest</h4>
<p>Der Vektorspeicher ist ein Erzeugnis, kein Dokument — vergleichbar mit einem Registerband, der zu genau einer Auflage gehört: Er gilt nur für das Embedding-Modell, die Chunk-Einstellungen und das Extraktionswerkzeug, die ihn hervorgebracht haben. Ändert sich eines der drei, entspricht der Index auf der Platte stillschweigend nicht mehr den Einstellungen im Quelltext. Diese drei Werte also neben jedem Index festhalten, bei jeder Änderung neu bauen und vor jedem Benchmark-Lauf ohnehin — sonst misst man ein Artefakt der Vergangenheit.</p>
</div>
</div>
</div>
</section>
<!-- ===================== 02 ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">02</div>
<h2>Was die beiden Ingestion-Pipelines brauchen</h2>
<p class="phase-sub">Größter Hebel bei verrauschtem Extraktionstext</p>
</div>
<div class="phase-body">
<p>Die beiden Pipelines verlangen von der Extraktion Verschiedenes: Bei den Primärtexten ist es vor allem eine Frage der Textbeschaffung, bei der Sekundärliteratur entscheidet sich die Extraktionsqualität. Bevor man eine der beiden für erledigt erklärt, lohnt der Blick darauf, was ein allgemeiner Loader eigentlich tut — <code>PyPDFLoader</code> und Verwandte — ein <em>Loader</em> ist das Stück Programm, das ein PDF einliest und in reinen Text verwandelt — behandeln eine Seite als einen flachen Zeichenstrom, also als eine einzige lange Buchstabenkette ohne Wissen darüber, was Überschrift, Fließtext oder Anmerkung war.</p>
<h3>Die Primärtext-Pipeline</h3>
<p>Erzählungen und Romane sind vergleichsweise einfach — kein Literaturverzeichnis, kein zweispaltiger Satz. Eine weitere Bearbeitung des Ausgangsmaterials könnte in erster Linie Seitenzahlen, Anmerkungen o.ä. entfernen.</p>
<h3>Die Sekundär-Pipeline: ein Parser, der Dokumentstruktur erkennt</h3>
<p>Hier liegt bei einer Vorverarbeitung viel Potenzial. Idealerweise verwenden wir einen Extraktor, der <em>typisierte Elemente</em> statt eines Zeichenstroms zurückgibt: Er liefert also nicht nur den Text, sondern zu jedem Stück auch die Angabe, worum es sich handelt — Überschrift, Fließtext, Fußnote, Literaturangabe. Damit wird aus einem mühsamen Textbereinigungsproblem eine schlichte Entscheidung pro Sorte: diese behalten, jene weglassen.</p>
<div class="kc">
<div class="kc-col kc-keep">
<h4>In der Regel als Fließtext indexieren</h4>
<ul>
<li>Abstract</li>
<li>Zwischenüberschriften (als Chunk-Kontext)</li>
<li>Argumentierender Fließtext</li>
<li>Blockzitate aus Primärtexten</li>
<li>Tabellen- und Abbildungslegenden</li>
</ul>
</div>
<div class="kc-col kc-cut">
<h4>In der Regel abtrennen</h4>
<ul>
<li>Fuß- und Endnoten</li>
<li>Literaturverzeichnis</li>
<li>Kolumnentitel, Seitenzahlen</li>
<li>Zeitschriften-Boilerplate, Copyright-Zeilen</li>
<li>Affiliationen, Danksagungen</li>
</ul>
</div>
</div>
<p>Es empfiehlt sich, Fußnoten/Anmerkungen mit <code>element_type: footnote</code> in den Metadaten zu indexieren. Standardmäßig könnte man sie vom Retrieval ausschließen und bei Bedarf wieder zuschalten. Löschen wäre unumkehrbar.</p>
<h3>Werkzeuge, grob nach Aufwand geordnet</h3>
<div class="tw">
<table>
<thead>
<tr><th>Werkzeug</th><th>Was es liefert</th><th>Aufwand / Passung</th></tr>
</thead>
<tbody>
<tr>
<td>GROBID</td>
<td class="note">Eigens für wissenschaftliche PDFs gebaut. Gibt TEI-XML aus, in dem Fließtext, Anmerkungen und Literaturverzeichnis getrennt ausgezeichnet sind, dazu geparste Zitationsmetadaten.</td>
<td class="note">Kostenlos, läuft im Haus. Wird als Docker-Paket gestartet — ein Fertigpaket, das man startet, ohne die Bestandteile einzeln installieren zu müssen. <strong>Die stärkste Passung für ein Sekundärliteratur-Korpus</strong> — für genau diesen Dokumenttyp entwickelt.</td>
</tr>
<tr>
<td>Docling</td>
<td class="note">Wandelt PDFs in strukturiertes Markdown/JSON mit korrekter Lesereihenfolge, Überschriften, Tabellen und Anmerkungen als eigenen Elementen.</td>
<td class="note">Kostenlos, lokal, aktiv entwickelt. Gute Allzweckwahl und leichter einzuführen als GROBID.</td>
</tr>
<tr>
<td>unstructured</td>
<td class="note">Zerlegt in typisierte Elemente (<code>NarrativeText</code>, <code>Title</code>, <code>Footer</code>, <code>ListItem</code>); das Filtern nach Typ sind wenige Zeilen.</td>
<td class="note">Open-Source-Bibliothek plus gehostete API; LangChain-Anbindung existiert bereits. Der schnellste Weg von einem Standard-Setup aus.</td>
</tr>
<tr>
<td>PyMuPDF</td>
<td class="note">Text-Spans mit Koordinaten und Schriftgrößen — genug für eine eigene Heuristik, etwa: Spans unter ~85 % der häufigsten Grundschriftgröße im unteren ~20 % der Seite verwerfen.</td>
<td class="note">Kostenlos, schnell, keine neue Infrastruktur. Empfindlich gegenüber wechselnden Layouts; gut, solange ein Korpus typografisch einheitlich ist.</td>
</tr>
</tbody>
</table>
</div>
<div class="aside">
<h4>Den Extraktor prüfen</h4>
<p>Unabhängig von der Wahl: fünf PDFs mit den unübersichtlichsten Layouts des Korpus nehmen und den extrahierten Fließtext mit dem vergleichen, was man von Hand ausgezeichnet hätte. Die Extraktionsqualität schwankt zwischen Verlagen erheblich, und ein Werkzeug, das beim zweispaltigen Satz der einen Zeitschrift hervorragend ist, kann bei der nächsten scheitern.</p>
</div>
</div>
</section>
<!-- ===================== 03 ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">03</div>
<h2>Chunking und Embeddings gemeinsam wählen</h2>
<p class="phase-sub">Für sich genommen bedeutet keine der beiden Einstellungen etwas</p>
</div>
<div class="phase-body">
<p>Alles in dieser Phase spielt sich in einer einzigen Datei ab: <code>local_rag.py</code>. Zur Orientierung vorab, welche Änderung wo ansetzt und was danach fällig wird. Die Zeilennummern sind Stand heute und verschieben sich beim Bearbeiten — verlässlich ist der genannte Ort.</p>
<div class="tw">
<table>
<thead>
<tr><th>Änderung</th><th>Stelle in <code>local_rag.py</code></th><th>Danach nötig</th></tr>
</thead>
<tbody>
<tr><td>Embedding-Modell</td><td class="note">In <code>LocalRAG.__init__</code>, der Aufruf <code>HuggingFaceEmbeddings(...)</code> — Zeile 48</td><td class="note">Index neu bauen</td></tr>
<tr><td>Chunk-Größe</td><td class="note">Die Konstanten <code>CHUNK_SIZE</code> und <code>CHUNK_OVERLAP</code> ganz oben — Zeile 16/17</td><td class="note">Index neu bauen</td></tr>
<tr><td>Trennlogik</td><td class="note">Der <code>RecursiveCharacterTextSplitter</code> in <code>__init__</code> — Zeile 53</td><td class="note">Index neu bauen</td></tr>
<tr><td>Metadaten</td><td class="note">In <code>add_documents</code>, zwischen <code>loader.load()</code> und <code>split_documents(...)</code> — Zeile 114/115</td><td class="note">Index neu bauen</td></tr>
<tr><td>Klein suchen, groß liefern</td><td class="note">In <code>query_with_history</code>, der Aufruf <code>similarity_search_with_score</code> — Zeile 251</td><td class="note">Größerer Umbau, siehe unten</td></tr>
</tbody>
</table>
</div>
<p>„Index neu bauen" heißt jedes Mal dasselbe: <code>add_pdfs.py</code> mit <code>CLEAR_VECTORSTORE_FIRST = True</code> laufen lassen. Der alte Index passt nach solchen Änderungen nicht mehr und würde stillschweigend Unsinn liefern. Direkt danach <code>eval_retrieval.py</code> starten und die neue Zahl mit der alten vergleichen.</p>
<h3>Das Embedding-Modell kommt zuerst</h3>
<p>Bei einem deutschsprachigen Korpus könnte ein englisch trainiertes Modell mit kurzem Kontextfenster problematisch sein. Kandidaten, alle lokal lauffähig, sind:</p>
<ul>
<li><strong>BAAI/bge-m3</strong> — mehrsprachig, verkraftet lange Eingaben und liefert aus einem Modell dense <em>und</em> sparse Repräsentationen, womit hybride Suche (Phase 04) fast geschenkt ist. Hier eine gute Voreinstellung.</li>
<li><strong>intfloat/multilingual-e5-large</strong> — starke mehrsprachige Qualität, 512-Token-Fenster. Achtung: Dieses Modell erwartet, dass jedem Text ein Kennwort vorangestellt wird — <code>query:</code> vor der Frage, <code>passage:</code> vor dem Textabschnitt —, damit es beide unterschiedlich behandelt. Wird das vergessen, verschlechtern sich die Ergebnisse messbar, ohne dass eine Fehlermeldung darauf hinweist.</li>
<li><strong>jinaai/jina-embeddings-v3</strong> — mehrsprachig, langer Kontext, aufgabenspezifische Adapter.</li>
</ul>
<p>Im Code steht der Modellname genau einmal, in <code>LocalRAG.__init__</code>. Ein Wechsel ist wörtlich ein Austausch dieser Zeichenkette; das Modell wird beim ersten Start automatisch heruntergeladen, was einige Minuten dauern kann:</p>
<pre><code><span class="c"># local_rag.py, in LocalRAG.__init__ (derzeit Zeile 48)</span>
self.embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-m3" <span class="c"># vorher: sentence-transformers/all-MiniLM-L6-v2</span>
)</code></pre>
<p>Ein Hinweis, der bei der Modellwahl den Ausschlag geben kann: Bei <code>bge-m3</code> genügt tatsächlich diese eine Zeile. Bei <code>multilingual-e5-large</code> reicht sie <em>nicht</em>, weil das Modell die oben erwähnten Kennwörter erwartet — Fragen und Textabschnitte müssten an zwei weiteren Stellen unterschiedlich behandelt werden. Wer den Aufwand klein halten will, nimmt deshalb bge-m3.</p>
<p>Wenn eine API in Frage kommt, sind gehostete Embedding-Modelle (Voyage, Cohere, OpenAI) bei mehrsprachigem Material meist noch einmal besser, zu Kosten, die bei dieser Korpusgröße vernachlässigbar sind — vorher ist allerdings zu klären, ob Institutsmaterial überhaupt an einen externen Dienst übergeben werden darf — das entscheidet der Datenschutz, nicht die Pipeline.</p>
<h3>Danach die Chunk-Größe auf das setzen, was das Modell tatsächlich liest</h3>
<p>Die Größe steht als Konstante ganz oben in der Datei, gut sichtbar und ohne Umwege änderbar. Die Werte sind Zeichen, nicht Token:</p>
<pre><code><span class="c"># local_rag.py, Zeile 16/17</span>
CHUNK_SIZE = 2000
CHUNK_OVERLAP = 400 <span class="c"># wie viel benachbarte Chunks einander überlappen</span></code></pre>
<p>Die Überlappung sorgt dafür, dass ein Gedanke, der genau auf einer Schnittkante liegt, nicht verloren geht — er steht dann am Ende des einen und am Anfang des nächsten Chunks. Als Faustregel liegt sie bei etwa einem Fünftel der Chunk-Größe.</p>
<p>Steht das Modell fest, wollen die beiden Korpora unterschiedlich geteilt werden:</p>
<ul>
<li><strong>Primärtexte:</strong> an Absatz- und Szenengrenzen trennen statt nach fester Zeichenzahl. Erzählte Bedeutung übersteht Schnitte mitten im Satz nicht, und literaturwissenschaftliche Fragen hängen oft an der Gestalt einer Passage.</li>
<li><strong>Sekundärtexte:</strong> an Abschnittsgrenzen trennen, mit einer Obergrenze — und erwägen, <strong>jedem Chunk vor dem Embedding die Zwischenüberschrift und einen kurzen bibliografischen Nachweis voranzustellen</strong>, sodass er „Müller 2003, 3.2 Familienstruktur" mit sich führt. Ein isolierter Absatz aus der Mitte einer Argumentation ist sonst kontextlos; bei wissenschaftlichen Korpora hilft das zuverlässig.</li>
</ul>
<p>Womit an welchen Stellen getrennt wird, steuert der Parameter <code>separators</code>. Die Liste wird von oben nach unten abgearbeitet: Erst wird an doppelten Zeilenumbrüchen — also Absätzen — geteilt, und nur wenn ein Stück dann immer noch zu groß ist, greift der nächste Eintrag. So bleiben Absätze ganz, solange es geht:</p>
<pre><code><span class="c"># local_rag.py, in LocalRAG.__init__ (derzeit Zeile 53)</span>
self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
separators=["\n\n", "\n", ". ", " ", ""], <span class="c"># Absatz, Zeile, Satz, Wort, Notfall</span>
)</code></pre>
<p>Wer für die beiden Korpora unterschiedlich trennen will, findet dafür genau eine Stelle. In <code>add_documents</code> wird jedes geladene Dokument durch denselben Splitter geschickt — hier würde eine Fallunterscheidung ansetzen, etwa anhand des Ordners, aus dem die Datei stammt:</p>
<pre><code><span class="c"># local_rag.py, in add_documents (derzeit Zeile 114/115)</span>
docs = loader.load()
chunks = self.text_splitter.split_documents(docs) <span class="c"># &lt;- der eine Verzweigungspunkt</span>
all_docs.extend(chunks)</code></pre>
<h3>Klein suchen, groß liefern</h3>
<p>Kleine Chunks finden präzise, große Chunks antworten gut. Man muss sich nicht entscheiden: kleine Chunks (ca. 400–800 Zeichen) für den Abgleich indexieren, dem LLM aber deren <em>übergeordneten</em> Abschnitt übergeben. LangChain — die Programmbibliothek, auf der das Projekt aufbaut — bringt dafür eine fertige Komponente namens <code>ParentDocumentRetriever</code> mit, die genau diese Verdopplung von Such- und Liefereinheit übernimmt. Es ist das übliche Mittel, wenn ein kurzes Fragment über wenige passende Wörter einen Trefferplatz gewinnt und dabei viel unverbundenes Umgebungsmaterial in den Kontext zieht.</p>
<p>Ehrlicherweise ist das der größte Eingriff dieser Phase und anders als die bisherigen keine Einzeiler-Sache. Das Projekt greift derzeit unmittelbar auf den Vektorspeicher zu:</p>
<pre><code><span class="c"># local_rag.py, in query_with_history (derzeit Zeile 251)</span>
docs_with_scores = self.vectorstore.similarity_search_with_score(retrieval_query, k=k)</code></pre>
<p>Für „klein suchen, groß liefern" müsste dieser Aufruf durch den Retriever ersetzt und zusätzlich eine zweite Ablage für die großen Abschnitte angelegt werden — FAISS speichert nur die kleinen Stücke. Vorübergehend entfallen dadurch auch die Ähnlichkeitswerte in der Protokollausgabe. Deshalb steht dieser Punkt hinten: erst die billigen Änderungen messen, dann entscheiden, ob der Umbau überhaupt noch nötig ist.</p>
<h3>Metadaten (falls vorhanden)</h3>
<p>Metadaten sind Zusatzangaben, die an jedem Chunk mitgeführt werden — vergleichbar mit den Feldern eines Bibliothekskatalogs. Sie werden nicht durchsucht, aber man kann nach ihnen filtern. Falls sie durch eine Vorverarbeitung existieren, sollten mindestens mitgeführt werden: <code>element_type</code> (Fließtext / Anmerkung / Literaturverzeichnis), <code>work</code> bzw. <code>author_year</code>, <code>section</code> und <code>page</code>. Erst damit lassen sich Anmerkungen von einer Suche ausschließen oder eine Suche auf ein einzelnes Werk beschränken, ohne irgendetwas neu zu bauen.</p>
<p><code>source</code> und <code>page</code> setzt der Loader bereits von allein. Eigene Angaben kommen an derselben Stelle dazu, an der auch getrennt wird — wichtig ist allein die Reihenfolge: <strong>vor</strong> dem Aufteilen, denn die Chunks erben die Metadaten des Dokuments, aus dem sie stammen:</p>
<pre><code><span class="c"># local_rag.py, in add_documents — zwischen Laden und Aufteilen</span>
docs = loader.load()
for doc in docs: <span class="c"># neu</span>
doc.metadata["korpus"] = "sekundaer" <span class="c"># z. B. aus dem Ordnernamen</span>
doc.metadata["element_type"] = "body"
chunks = self.text_splitter.split_documents(docs) <span class="c"># erbt die Angaben von oben</span></code></pre>
<p>Gefiltert wird später bei der Suche, indem dem Aufruf ein <code>filter</code> mitgegeben wird — etwa <code>filter={"element_type": "body"}</code>, um Anmerkungen auszublenden. Ob es etwas gebracht hat, zeigt <code>eval_retrieval.py</code> unmittelbar danach.</p>
</div>
</section>
<!-- ===================== 04 ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">04</div>
<h2>Hybride Suche, danach Reranking</h2>
<p class="phase-sub">Mögliche Ansätze bei der Optimierung der Suche</p>
</div>
<div class="phase-body">
<h3>Stichwortsuche neben die Vektoren stellen</h3>
<p>Reines "Dense Retrieval" ist genau bei dem schwach, wovon der vorhandene Korpus voll ist: Eigennamen, Kunstwörter, Werktitel, wörtliche Zitate. Eine Anfrage nach <em>Odradek</em> oder <em>Gregor Samsa</em> will einen lexikalischen Treffer, keine semantische Näherung. Also BM25 parallel zur Vektorsuche laufen lassen: BM25 ist die klassische Stichwortsuche, wie man sie aus Bibliothekskatalogen kennt — sie zählt, welche Suchwörter wie häufig und wie selten im Text vorkommen, und findet damit genau das Wörtliche, das die Bedeutungssuche übersieht. Die beiden Trefferlisten werden anschließend zu einer verrechnet; das Standardverfahren dafür heißt Reciprocal Rank Fusion und gewichtet einen Treffer danach, wie weit oben er in den einzelnen Listen stand. In LangChain sind dafür <code>EnsembleRetriever</code> und <code>BM25Retriever</code> vorgesehen, es bleibt also eine kurze Änderung. Im literaturwissenschaftlichen Retrieval ist das oft ein großer und sofort sichtbarer Gewinn.</p>
<h3>Danach mit einem Cross-Encoder umsortieren</h3>
<p>Die Form des Retrievals ändert sich: statt 5 werden 30 bis 50 Kandidaten geholt, von einem Cross-Encoder gegen die Anfrage bewertet und auf die besten 5 bis 8 eingedampft. Ein Cross-Encoder ist ein zweites, kleineres Modell mit einer anderen Arbeitsweise: Die normale Suche berechnet für Frage und Textabschnitt getrennt je eine Zahlenreihe und vergleicht diese; der Cross-Encoder bekommt beides <em>zusammen</em> vorgelegt und beurteilt direkt, ob der Abschnitt zur Frage passt. Das ist zu langsam, um damit hunderttausende Chunks zu durchsuchen, aber genau richtig für eine Handvoll Kandidaten — und deutlich treffsicherer — und er ist besonders gut darin, zitatlastige, inhaltsarme Passagen nach hinten zu schieben.</p>
<ul>
<li><strong>BAAI/bge-reranker-v2-m3</strong> — mehrsprachig, lokal lauffähig; auf GPU bequem, auf CPU machbar, aber langsam.</li>
<li><strong>Cohere Rerank</strong> — API, starke mehrsprachige Qualität, minimaler Integrationsaufwand.</li>
</ul>
<p>Nach Phase 02 und 03 ist das typischerweise der größte verbleibende Sprung.</p>
<div class="aside">
<h4>Erst jetzt die Prompt-Datei öffnen</h4>
<p>Wenn Recall@k sich nicht mehr verbessert und die abgerufenen Passagen beim Lesen überzeugen, sind die verbleibenden Fehler Generierungsfehler — und <em>dann</em> sind Antwort-Prompt, Rephrase-Schritt, Modellwahl und Zitationsformat die richtigen Stellschrauben. Früher danach zu greifen kostet nicht nur Zeit; es überdeckt die eigentliche Ursache und erschwert die nächste Diagnose.</p>
</div>
</div>
</section>
<!-- ===================== KURZFASSUNG ===================== -->
<section class="phase">
<div class="phase-head">
<div class="num">&#10003;</div>
<h2>Kurzfassung</h2>
<p class="phase-sub">Eine Arbeitsreihenfolge · nach jedem Schritt messen</p>
</div>
<div class="phase-body">
<ol class="checklist">
<li><b>Referenzfragen anlegen und Ausgangswert festhalten</b><span>25–40 echte Fragen mit bekannter Fundstelle; Recall@5/10/20 ohne LLM. Zahlen ins Repository.</span></li>
<li><b>Zwanzig Chunks von Hand lesen</b><span>Abschätzen, welcher Anteil Apparat und Layout-Rauschen ist. Meist die aufschlussreichste halbe Stunde.</span></li>
<li><b>Chunk-Größe gegen das Modellfenster prüfen</b><span>Alles jenseits des Fensters wird verworfen, bevor der Vektor berechnet wird.</span></li>
<li><b>Prüfen, was Anweisungstext in der Anfrage bewirkt</b><span>Recall mit und ohne messen, statt anzunehmen, dass er hilft.</span></li>
<li><b>Auf ein mehrsprachiges Modell mit langem Kontext wechseln</b><span>bge-m3 als Voreinstellung; Chunk-Größe passend nachziehen. Neu bauen, neu messen.</span></li>
<li><b>Sekundärkorpus mit einem strukturbewussten Parser neu extrahieren</b><span>GROBID oder Docling. Anmerkungen und Literaturverzeichnis in Metadaten auszeichnen, nicht löschen.</span></li>
<li><b>Falls möglich: Saubere digitale Editionen für die Primärtexte beschaffen</b><span>PDF-Extraktion umgehen, wo es schon eine TEI- oder Plaintext-Ausgabe gibt.</span></li>
<li><b>Falls die Dokumente geparst wurden und nicht mehr direkt auf PDFs gearbeitet wird: element_type als Metadatum ergänzen, mit Filter</b><span>Anmerkungen standardmäßig ausschließen, aber zuschaltbar halten, damit beides testbar bleibt.</span></li>
<li><b>BM25 neben die Vektoren stellen</b><span>Eigennamen und wörtliche Zitate brauchen lexikalischen Abgleich.</span></li>
<li><b>30–50 Kandidaten holen und auf 5–8 umsortieren</b><span>Meist der größte verbleibende Gewinn, und er setzt sich hinter einen unveränderten Index.</span></li>
<li><b>Erst jetzt Antwort-Prompt und Modell überarbeiten</b><span>Ab hier sind die verbleibenden Fehler tatsächlich Generierungsfehler.</span></li>
</ol>
</div>
</section>
<footer>
Erst Retrieval, dann Generierung · Arbeitsnotizen zur local_rag-Pipeline
</footer>
</div>
+22 -4
View File
@@ -1,14 +1,19 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
"""Add all PDFs under a folder to the RAG vector store. Run from project root.""" """Add all PDFs under a folder to the RAG vector store. Run from project root.
The settings below are the defaults; they can be overridden on the command line, e.g.
python add_pdfs.py --data data --index ./vectorstore_docling --loader docling
"""
import argparse
import shutil import shutil
from pathlib import Path from pathlib import Path
from local_rag import LocalRAG from local_rag import CHUNK_OVERLAP, CHUNK_SIZE, PDF_LOADER, PROCESSED_DIR, LocalRAG
# Folder to scan for PDFs (recursively). # Folder to scan for PDFs (recursively).
DATA_ROOT = Path("/Users/Philipp/Desktop/workspace/python/gpt_publikationen/data_vs") DATA_ROOT = Path("/Users/Philipp/Desktop/workspace/python/gpt_publikationen/data_vs")
# Must match server.py so the chat loads the same index. # The chat (server.py) currently loads ./vectorstore_docling_hdr; pass --index to build that one.
VECTORSTORE_PATH = "./vectorstore" VECTORSTORE_PATH = "./vectorstore"
# If True: delete the existing vector store folder, then index all PDFs from scratch. # If True: delete the existing vector store folder, then index all PDFs from scratch.
@@ -17,6 +22,17 @@ CLEAR_VECTORSTORE_FIRST = True
if __name__ == "__main__": if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
parser.add_argument("--data", type=Path, default=DATA_ROOT, help="folder to scan for PDFs")
parser.add_argument("--index", default=VECTORSTORE_PATH, help="vector store folder")
parser.add_argument("--loader", default=PDF_LOADER, choices=["pypdf", "docling"])
parser.add_argument("--processed", default=PROCESSED_DIR, help="Docling output (prepare_corpus.py)")
parser.add_argument("--chunk-size", type=int, default=CHUNK_SIZE)
parser.add_argument("--chunk-overlap", type=int, default=CHUNK_OVERLAP)
parser.add_argument("--no-headers", action="store_true", help="no author/title header per chunk")
args = parser.parse_args()
DATA_ROOT, VECTORSTORE_PATH = args.data, args.index
if CLEAR_VECTORSTORE_FIRST and Path(VECTORSTORE_PATH).exists(): if CLEAR_VECTORSTORE_FIRST and Path(VECTORSTORE_PATH).exists():
shutil.rmtree(VECTORSTORE_PATH) shutil.rmtree(VECTORSTORE_PATH)
print(f"Cleared existing vector store: {VECTORSTORE_PATH}") print(f"Cleared existing vector store: {VECTORSTORE_PATH}")
@@ -28,6 +44,8 @@ if __name__ == "__main__":
if not pdfs: if not pdfs:
raise SystemExit("No PDFs found.") raise SystemExit("No PDFs found.")
rag = LocalRAG(vectorstore_path=VECTORSTORE_PATH) rag = LocalRAG(vectorstore_path=VECTORSTORE_PATH, pdf_loader=args.loader,
processed_dir=args.processed, chunk_size=args.chunk_size,
chunk_overlap=args.chunk_overlap, chunk_headers=not args.no_headers)
rag.add_documents([str(p) for p in pdfs]) rag.add_documents([str(p) for p in pdfs])
print("Done.") print("Done.")
+109
View File
@@ -0,0 +1,109 @@
"""
Vergleicht die Extraktoren mit denselben Prüfungen aus grobid_qa.py.
Für jeden Titel aus extract_docling.TITLES werden bewertet:
pypdf der jetzige PyPDFLoader (eine Einheit pro Seite, keine Struktur)
grobid grobid_out/data/<pdf>.tei.xml
docling extract_out/docling/<pdf>.json
docling_ocr extract_out/docling_ocr/<pdf>.json
docling_tess extract_out/docling_tess/<pdf>.json
docling_ocrmypdf extract_out/docling_ocrmypdf/<pdf>.json (Referenz: extract_out/ocrmypdf/<pdf>)
Fehlende Ausgaben werden ausgelassen.
Aufruf (Projektumgebung genügt, Docling wird hier nicht gebraucht):
conda activate local_rag
python compare_extractors.py
Ausgabe: Vergleichstabelle im Terminal und in extract_out/vergleich/vergleich.md
(+ .csv), Einzelberichte mit Belegstellen in extract_out/vergleich/<extraktor>/.
"""
import csv
import re
from pathlib import Path
import grobid_qa as qa
PDF_DIR = Path("data")
OUT_DIR = Path("extract_out") / "vergleich"
# Titel wie in extract_docling.py, ohne Docling importieren zu müssen
TITLES = re.findall(r'^\s*"([^"]+)",', Path("extract_docling.py").read_text(encoding="utf-8"), re.M)
EXTRACTORS = {
"pypdf": lambda stem: None,
"grobid": lambda stem: Path("grobid_out/data") / f"{stem}.tei.xml",
"docling": lambda stem: Path("extract_out/docling") / f"{stem}.json",
"docling_ocr": lambda stem: Path("extract_out/docling_ocr") / f"{stem}.json",
"docling_tess": lambda stem: Path("extract_out/docling_tess") / f"{stem}.json",
"docling_ocrmypdf": lambda stem: Path("extract_out/docling_ocrmypdf") / f"{stem}.json",
"ocrmypdf_bereinigt": lambda stem: Path("extract_out/docling_ocrmypdf") / f"{stem}.json",
}
# Diese Varianten werden so geprüft, wie docling_loader.py sie bereinigt an den Index gibt.
CLEANED = {"ocrmypdf_bereinigt"}
# Für diese Varianten ist das neu erkannte PDF die Referenz: geprüft wird, ob Docling
# dessen Textschicht vollständig übernimmt, und wie gut die neue OCR ist.
REFERENCE_PDF = {"docling_ocrmypdf": Path("extract_out/ocrmypdf"),
"ocrmypdf_bereinigt": Path("extract_out/ocrmypdf")}
# Spalten der Vergleichstabelle: Kennzahl -> Kurzname
COLUMNS = {
"absaetze": "Absätze", "ueberschriften": "Überschr.", "fussnoten": "Fußnoten",
"giant_para_pct": "Riesen%", "lost_pct": "Verlust%", "misfiled_pct": "Fehlabl%",
"contam_per_100k": "Fremd", "fnmark_per_10k": "FN-Zch", "ocr_per_10k": "OCR",
"hyphen_per_10k": "Trenn", "broken_per_100p": "Zerriss", "order": "Reihenf",
}
def mark(m: dict, key: str) -> str:
value = m.get(key)
light = m.get("ampel", {}).get(key)
suffix = {"ROT": "!", "GELB": "?"}.get(light, "")
return f"{value}{suffix}"
def main():
pdfs = [p for p in sorted(PDF_DIR.glob("*.pdf")) if any(p.name.startswith(t) for t in TITLES)]
rows = []
for pdf in pdfs:
for name, locate in EXTRACTORS.items():
out = locate(pdf.stem)
if out is not None and not out.exists():
continue
ref = REFERENCE_PDF[name] / pdf.name if name in REFERENCE_PDF else pdf
m, ex = qa.check(ref, out, cleaned=name in CLEANED)
report_dir = OUT_DIR / name
report_dir.mkdir(parents=True, exist_ok=True)
qa.write_report(m, ex, report_dir / f"{pdf.stem}.md")
rows.append({**m, "titel": pdf.name.split("_")[0].rstrip(","), "datei": pdf.name,
"extraktor": name})
print(f"{pdf.name[:50]:50} {name:12} Extraktor {m['grobid']}", flush=True)
header = ["Titel", "Extraktor", "Urteil", *COLUMNS.values()]
lines = ["| " + " | ".join(header) + " |", "|" + "---|" * len(header)]
for r in rows:
cells = [r["titel"], r["extraktor"], r["grobid"], *(mark(r, k) for k in COLUMNS)]
lines.append("| " + " | ".join(map(str, cells)) + " |")
legend = ("\n? = gelb, ! = rot. Absätze/Überschriften/Fußnoten: Anzahl erkannter Elemente. "
"Bei pypdf ist jede Seite eine Einheit, Verlust und Reihenfolge sind dort trivial.")
OUT_DIR.mkdir(parents=True, exist_ok=True)
(OUT_DIR / "vergleich.md").write_text("# Extraktoren im Vergleich\n\n" + "\n".join(lines) + legend + "\n",
encoding="utf-8")
with open(OUT_DIR / "vergleich.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=["datei", "extraktor", "grobid", "pdf", *COLUMNS],
extrasaction="ignore", delimiter=";")
w.writeheader()
w.writerows(rows)
print()
widths = [12, 16, 6] + [9] * len(COLUMNS)
print(" ".join(h[:w].ljust(w) for h, w in zip(header, widths)))
for r in rows:
cells = [r["titel"], r["extraktor"], r["grobid"], *(mark(r, k) for k in COLUMNS)]
print(" ".join(str(c)[:w].ljust(w) for c, w in zip(cells, widths)))
print(legend.strip())
print("Details:", OUT_DIR)
if __name__ == "__main__":
main()
BIN
View File
Binary file not shown.
-70594
View File
File diff suppressed because one or more lines are too long
-5153
View File
File diff suppressed because one or more lines are too long
+319
View File
@@ -0,0 +1,319 @@
"""
Lädt mit prepare_corpus.py erzeugte Docling-Dokumente als LangChain-Dokumente.
Bereinigung (clean_blocks, reines Python, auch von grobid_qa.py benutzt):
- Seitenränder (Kolumnentitel, Seitenzahlen) fallen weg
- OCR-Zeichensalat vom Buchrand ("£", "X N £", "CE EEE VKU") wird verworfen,
Salat vor einem echten Absatz abgeschnitten
- an Spalten- oder Seitenwechseln getrennte Absätze werden wieder zusammengeführt,
Trennstriche aus Zeilenumbrüchen entfernt ("Veröf- fentlichung" -> "Veröffentlichung")
- Fußnotenzeichen im Fließtext werden entfernt ("Kafka.1", "Felice 6 , die")
- als Punkt gelesene Kommas werden repariert ("Einzelheiten. die" -> "Einzelheiten, die"),
als "©" gelesene Trennstriche am Zeilenrand entfernt ("Atmo- © sphäre")
- bekannte Kodierungsfehler einiger Verlags-PDFs werden ersetzt (" ¼ " -> " = ")
Jeder Chunk beginnt mit einer Kopfzeile aus Autor, Titel (aus dem Dateinamen) und
Abschnittspfad, z. B. "Lehmann, Hans-Thies: Der Buchstaebliche Koerper — 3. Schrift".
PyPDFLoader-Chunks tragen den Autor oft zufällig über den Kolumnentitel mit sich;
Docling sortiert Kolumnentitel aus und legt Überschriften in die Metadaten. Ohne
Kopfzeile fanden Fragen wie "nach Lehmann" die richtige Stelle deshalb schlechter.
Chunks (load): Fließtext wird pro Abschnitt zusammengefügt und mit demselben
Splitter wie bisher zerlegt, damit Vergleiche mit PyPDFLoader fair bleiben.
Fußnoten, Bildunterschriften und Tabellen werden als eigene Chunks indexiert,
Literaturverzeichnisse standardmäßig nicht. Metadaten je Chunk:
source Pfad des Original-PDFs (nicht der neu erkannten Kopie)
page Seite ab 0 im Original-PDF, wie bei PyPDFLoader (bei getrennten
Doppelseiten über <json>.pages.json zurückgerechnet)
half "L"/"R" bei getrennten Doppelseiten, sonst ""
element_type body | footnote | caption | table | reference
section Abschnittspfad aus der Überschriften-Hierarchie ("1. Nachlass › a) Situation 1924")
extractor "docling"
"""
import json
import re
import unicodedata
from dataclasses import dataclass, replace
from pathlib import Path
INDEX_REFERENCES = False # Literaturverzeichnis-Einträge mitindexieren?
LABEL_KIND = {
"title": "head", "section_header": "head", "text": "body", "paragraph": "body",
"list_item": "body", "footnote": "footnote", "caption": "caption", "formula": "body",
"code": "body", "reference": "reference", "document_index": "skip",
"page_header": "skip", "page_footer": "skip",
}
TERMINAL = tuple('.!?:…"\'“”‘’»«)]')
HYPHEN_KEEP = {"und", "oder", "bis", "sowie", "als", "wie", "noch", "bzw", "and", "or", "to"}
VOWEL = re.compile(r"[aeiouyäöüAEIOUYÄÖÜ]")
REAL_WORD = re.compile(r"[A-Za-zÄÖÜäöüß]{4,}")
NORMAL_TOKEN = re.compile(r"^[(„»\"']?[A-Za-zÄÖÜäöüß][a-zäöüß]{2,}[.,;:!?)»«\"“”']*$|^[(\[]?\d[\d:.,\-–]*[)\]]?[.,;:]?$")
# Kodierungsfehler aus Verlags-PDFs ohne Unicode-Zuordnung (siehe grobid_qa.py)
ENCODING_FIXES = [
(re.compile(r"(?<=\s)¼(?=\s)"), "="),
(re.compile(r"(?<=[\s(])À(?=\s?[\d.])"), "−"),
(re.compile(r"(?<=\w) Â (?=\w)"), " × "),
(re.compile(r"\bÓ (?=\d{4})"), "© "),
]
# Fußnotenzeichen am Wort ("Kafka1 ") oder nach Satzzeichen ("Kafka.1 ")
FNMARK_GLUED = re.compile(r"(?<=[a-zäöüß]{3})\d{1,3}(?=\s|$)")
FNMARK_AFTER_PUNCT = re.compile(r"(?<=[a-zäöüß][.,;:!?»“\"])\d{1,3}(?=\s|$)")
FNMARK_SPACED = re.compile(
r"(?<=[A-Za-zÄÖÜäöüß]{3}[.,;:!?»«\"”)]) (\d{1,3})(?: \d)? (?=[,;:]|[A-ZÄÖÜ„»])"
r"|(?<=[a-zäöüß]{3}) (\d{1,3})(?: \d)? (?=[,;:])")
# Abkürzungen, nach denen ein kleingeschriebenes Wort normal ist ("vgl. die", "Hrsg. von")
ABBREVIATIONS = {
"vgl", "bzw", "usw", "ebd", "sog", "ders", "dies", "hrsg", "hg", "anm", "aufl", "bd", "bde",
"nr", "verf", "übers", "eigtl", "wörtl", "bes", "ggf", "inkl", "evtl", "insb", "ca", "etc",
"kap", "abschn", "zit", "ff", "jh", "jhs", "dgl", "ugs", "lat", "griech", "frz", "engl",
"geb", "gest", "orig", "ausg", "hs", "hss", "fol", "masch", "diss", "phil",
}
PERIOD_BEFORE_LOWER = re.compile(r"\b([A-Za-zÄÖÜäöüß]*[a-zäöüß]{2})\. (?=[a-zäöüß]{2,}\b)")
def source_label(filename: str) -> str:
"""Autor und Titel aus Dateinamen wie 'Lehmann,_Hans-Thies_DerBuchstaeblicheKoerper_Zur….pdf'."""
stem = Path(filename).stem.strip(" ._")
tokens = [t for t in stem.split("_") if t]
authors, rest, i = [], [], 0
while i < len(tokens) and tokens[i].endswith(","): # "Nachname," "Vorname"
first = tokens[i + 1] if i + 1 < len(tokens) else ""
authors.append(f"{tokens[i].rstrip(',')}, {first}".strip(", "))
i += 2
rest = tokens[i:]
# Binnenmajuskeln trennen: "DerBuchstaeblicheKoerper" -> "Der Buchstaebliche Koerper"
title = " – ".join(re.sub(r"(?<=[a-zäöüß])(?=[A-ZÄÖÜ])", " ", t) for t in rest)
if not authors: # "Kafka_Die_Verwandlung_"
authors, title = [rest[0]] if rest else [], " ".join(rest[1:])
return "; ".join(authors) + (f": {title}" if title else "")
def chunk_header(source: str, section: str = "") -> str:
"""Kopfzeile, die jedem Chunk vorangestellt wird (Autor, Titel, Abschnitt)."""
return source_label(Path(source).name) + (f" — {section}" if section else "") + "\n\n"
def _repair_periods(text: str) -> str:
"""OCR liest Kommas oft als Punkte. Im Deutschen folgt auf einen Satzpunkt kein
kleingeschriebenes Wort, nach Abkürzungen schon ("Einzelheiten. die" -> "Einzelheiten, die")."""
return PERIOD_BEFORE_LOWER.sub(
lambda m: m.group(0) if m.group(1).lower() in ABBREVIATIONS else m.group(1) + ", ", text)
def period_anomalies_per_10k(text: str) -> float:
"""Wie oft ein Punkt vor einem kleingeschriebenen Wort steht (Abkürzungen ausgenommen)."""
hits = sum(m.group(1).lower() not in ABBREVIATIONS for m in PERIOD_BEFORE_LOWER.finditer(text))
return round(10000 * hits / max(1, len(text.split())), 1)
@dataclass
class Block:
kind: str # head, body, footnote, caption, table, reference
text: str
page: int | None # ab 1, wie Docling
section: str = ""
# Seitenwechsel innerhalb eines zusammengeführten Absatzes: (Zeichenposition, Seite)
page_breaks: tuple = ()
level: int = 1 # Ebene einer Überschrift (Docling, HeadingHierarchyOptions)
def _normalize(text: str) -> str:
text = unicodedata.normalize("NFC", text).replace("­", "")
text = " ".join(text.split())
# "©" ohne Jahreszahl ist ein falsch gelesener Trennstrich oder Randfleck ("Atmo- © sphäre")
text = re.sub(r"\s[©®](?!\s?\d{4})(?=\s)", "", text)
# Trennstrich aus Zeilenumbruch innerhalb eines Blocks
text = re.sub(r"(?<=[a-zäöüß])- (?=([a-zäöüß]+))",
lambda m: "- " if m.group(1) in HYPHEN_KEEP else "", text)
for pattern, repl in ENCODING_FIXES:
text = pattern.sub(repl, text)
return _repair_periods(text)
def is_garbage(text: str, kind: str = "body") -> bool:
"""OCR-Rauschen vom Buchrand oder aus dem Falz: kaum Buchstaben, keine echten Wörter.
Fußnoten sind oft nur Belege ("11 T S. 297.", "Vgl. Anm. 13.") und werden milder geprüft."""
nonspace = [c for c in text if not c.isspace()]
if not nonspace:
return True
letters = sum(c.isalpha() for c in nonspace)
if kind == "footnote":
return letters < 2 or letters / len(nonspace) < 0.25
if letters / len(nonspace) < 0.5:
return True
real = [w for w in REAL_WORD.findall(text) if VOWEL.search(w)]
if len(text) < 40 and not real:
return True
words = text.split()
if len(text) < 80 and sum(len(w) for w in words) / len(words) < 2.5:
return True
# lange Blöcke aus OCR-Salat (z. B. erkannte Handschrift-Faksimiles): kaum echte Wörter
return len(words) >= 8 and real_word_share(words) < 0.3
def real_word_share(words: list[str]) -> float:
"""Anteil 'echter' Wörter: mind. 4 Buchstaben, mit Vokal, überwiegend klein geschrieben.
Deutsche Prosa liegt bei etwa 0,5–0,6, OCR-Salat wie 'Bohran pn SE Hin A At' weit darunter."""
real = sum(bool(re.fullmatch(r"[„»(\"']?[A-ZÄÖÜa-zäöüß]?[a-zäöüß]{3,}[.,;:!?)«“\"']*", w))
and bool(VOWEL.search(w)) for w in words)
return real / max(1, len(words))
def strip_garbage_prefix(text: str) -> str:
"""Schneidet Zeichensalat vor einem echten Absatz ab: 'ET EST EEE "24 MEF = Re Doppler (1973:85) …'"""
tokens = text.split(" ")
i = 0
while i < len(tokens) and not NORMAL_TOKEN.match(tokens[i]):
i += 1
prefix = " ".join(tokens[:i])
if i >= 4 and re.search(r"[\"'=£®&>|~<{}]", prefix):
return " ".join(tokens[i:]).lstrip()
return text
def _strip_footnote_marks(text: str) -> str:
text = FNMARK_AFTER_PUNCT.sub("", FNMARK_GLUED.sub("", text))
return FNMARK_SPACED.sub(" ", text)
def _join(a: str, b: str) -> str:
"""Zwei Teile eines getrennten Absatzes verbinden, Trennstrich am Ende auflösen."""
if a.endswith("-") and b[:1].islower():
first = b.split(" ", 1)[0].strip(".,;:")
if first in HYPHEN_KEEP: # "Primär- und Sekundärtexte" bleibt
return a + " " + b
return a[:-1] + b # "Veröf-" + "fentlichung"
return a + " " + b
def read_blocks(path: Path) -> list[Block]:
"""Docling-JSON in Lesereihenfolge, ohne Seitenränder, Text normalisiert."""
doc = json.loads(Path(path).read_text(encoding="utf-8"))
blocks: list[Block] = []
def resolve(ref):
_, coll, idx = ref.split("/")
return coll, doc[coll][int(idx)]
def walk(node):
for child in node.get("children", []):
coll, item = resolve(child["$ref"])
prov = item.get("prov") or []
page = prov[0].get("page_no") if prov else None
if coll == "texts" and item.get("content_layer", "body") == "body":
kind = LABEL_KIND.get(item.get("label"), "body")
if kind != "skip":
raw = item.get("text", "")
text = _normalize(raw)
# Docling führt Absätze teils über Seiten hinweg; die Seitenwechsel
# stehen in prov (charspan bezogen auf den Rohtext, hier umgerechnet)
scale = len(text) / max(1, len(raw))
breaks = tuple((round(p["charspan"][0] * scale), p.get("page_no"))
for p in prov[1:] if p.get("charspan") and p.get("page_no") != page)
blocks.append(Block(kind, text, page, page_breaks=breaks, level=item.get("level") or 1))
elif coll == "tables" and item.get("content_layer", "body") == "body":
cells = (item.get("data") or {}).get("table_cells", [])
blocks.append(Block("table", _normalize(" ".join(c.get("text", "") for c in cells)), page))
walk(item)
walk(doc.get("body", {}))
return [b for b in blocks if b.text]
def clean_blocks(path: Path) -> list[Block]:
"""Bereinigte Blöcke mit Abschnittszuordnung, siehe Moduldoku."""
out: list[Block] = []
section, stack = "", [] # stack: (Ebene, Überschrift) der offenen Abschnitte
last_body = None # Index des letzten Fließtextblocks in out
for b in read_blocks(path):
if b.kind != "table":
b = replace(b, text=strip_garbage_prefix(b.text))
if is_garbage(b.text, b.kind):
continue
if b.kind == "head":
stack = [(lvl, txt) for lvl, txt in stack if lvl < b.level] + [(b.level, b.text)]
section = " › ".join(txt for _, txt in stack)
last_body = None # über Überschriften hinweg nie zusammenführen
out.append(replace(b, section=section))
continue
if b.kind == "body":
text = _strip_footnote_marks(b.text)
prev = out[last_body] if last_body is not None else None
if prev is not None and not prev.text.endswith(TERMINAL):
merged = _join(prev.text, text)
start = len(merged) - len(text)
breaks = prev.page_breaks + ((start, b.page),) + tuple((start + o, p) for o, p in b.page_breaks)
out[last_body] = replace(prev, text=merged, page_breaks=breaks)
continue
out.append(replace(b, text=text, section=section))
last_body = len(out) - 1
continue
out.append(replace(b, section=section))
return out
def load(json_path: Path, source: str, splitter, header: bool = True) -> list:
"""LangChain-Dokumente, gechunkt mit `splitter` (RecursiveCharacterTextSplitter).
header=True stellt jedem Chunk Autor, Titel und Abschnitt voran (chunk_header)."""
from langchain_core.documents import Document
blocks = clean_blocks(json_path)
# Getrennte Doppelseiten: neue Seite -> Seite im Original-PDF (split_spreads.py)
pages_file = Path(json_path).with_name(Path(json_path).stem + ".pages.json")
page_map = json.loads(pages_file.read_text(encoding="utf-8")) if pages_file.exists() else None
kinds = ["body", "footnote", "caption", "table"] + (["reference"] if INDEX_REFERENCES else [])
docs = []
for kind in kinds:
# Blöcke einer Art pro Abschnitt zusammenfassen, Seitenanfänge merken
groups: list[list[Block]] = []
for b in blocks:
if b.kind != kind:
continue
if groups and groups[-1][-1].section == b.section:
groups[-1].append(b)
else:
groups.append([b])
for group in groups:
text, starts = "", []
for b in group:
if text:
text += "\n\n"
starts.append((len(text), b.page))
starts += [(len(text) + offset, page) for offset, page in b.page_breaks]
text += b.text
def original_page(page):
"""Docling-Seite (ab 1) -> (Seite im Original-PDF ab 0, Hälfte)."""
if page_map and page and page <= len(page_map):
return page_map[page - 1]["original_page"], page_map[page - 1]["half"]
return ((page - 1) if page else None), ""
# Abschnitte des Texts mit ihrer Seite: [(Anfang, Ende, Seite), ...]
spans = [(s, e, p) for (s, p), (e, _) in zip(starts, starts[1:] + [(len(text), None)])]
cursor = 0
for chunk in splitter.split_text(text):
pos = text.find(chunk[:200], cursor)
pos = pos if pos >= 0 else max(text.find(chunk[:200]), 0)
cursor = pos + 1
# Seite, auf der der größte Teil des Chunks steht (nicht nur sein Anfang)
share = {}
for s, e, p in spans:
overlap = min(e, pos + len(chunk)) - max(s, pos)
if overlap > 0:
key = original_page(p)
share[key] = share.get(key, 0) + overlap
original, half = max(share, key=share.get) if share else original_page(group[0].page)
content = (chunk_header(source, group[0].section) if header else "") + chunk
docs.append(Document(page_content=content, metadata={
"source": source,
"page": original,
"half": half,
"element_type": kind,
"section": group[0].section,
"extractor": "docling",
}))
return docs
+13
View File
@@ -0,0 +1,13 @@
frage,korpus,erwartete_quelle,erwartete_seite,anmerkung
"Which established assessments inspired the mathematical word problems used in the study?",bilder,dok1.pdf,8,"TIMSS und NEPS, Abschnitt Testing Material"
"How long did participation in the entire online study take on average?",bilder,dok1.pdf,10,"ca. 29 Minuten, Abschnitt Procedure"
"How were the decorative pictures presented in the dynamic condition?",bilder,dok1.pdf,9,"Slideshow, ein Bild pro Segment"
"Did dynamic decorative pictures perform better than static decorative pictures?",bilder,dok1.pdf,12,"Ergebnisteil, kein signifikanter Unterschied"
"Which scientific article did the participants read in the concept map experiment?",begriffskarten,dok2.PDF,6,"Eerland et al. 2011, Eiffel Tower"
"How many concepts were finally selected for the concept map?",begriffskarten,dok2.PDF,8,"24 Konzepte, sieben Teilbereiche"
"In which file format does the CoMap tool store nodes and connections?",begriffskarten,dok2.PDF,7,"JSON-Datei mit Knotenpositionen"
"What are the two hypotheses about gradually built-up concept maps?",begriffskarten,dok2.PDF,5,"H1 Wissenskonstruktion, H2 Faktenwissen"
"How were participants of the first smart speaker experiment recruited?",sprachassistent,dok3.pdf,5,"Prolific, wohnhaft in Deutschland"
"Which scale was used to measure media credibility of the smart speaker?",sprachassistent,dok3.pdf,6,"Flanagin und Metzger 2000, fuenf Items"
"What does the realism heuristic say about modality and trust?",sprachassistent,dok3.pdf,3,"MAIN-Modell, Sundar 2008"
"What was the average age of participants in the replication experiment?",sprachassistent,dok3.pdf,8,"28.2 Jahre, Experiment 2"
1 frage korpus erwartete_quelle erwartete_seite anmerkung
2 Which established assessments inspired the mathematical word problems used in the study? bilder dok1.pdf 8 TIMSS und NEPS, Abschnitt Testing Material
3 How long did participation in the entire online study take on average? bilder dok1.pdf 10 ca. 29 Minuten, Abschnitt Procedure
4 How were the decorative pictures presented in the dynamic condition? bilder dok1.pdf 9 Slideshow, ein Bild pro Segment
5 Did dynamic decorative pictures perform better than static decorative pictures? bilder dok1.pdf 12 Ergebnisteil, kein signifikanter Unterschied
6 Which scientific article did the participants read in the concept map experiment? begriffskarten dok2.PDF 6 Eerland et al. 2011, Eiffel Tower
7 How many concepts were finally selected for the concept map? begriffskarten dok2.PDF 8 24 Konzepte, sieben Teilbereiche
8 In which file format does the CoMap tool store nodes and connections? begriffskarten dok2.PDF 7 JSON-Datei mit Knotenpositionen
9 What are the two hypotheses about gradually built-up concept maps? begriffskarten dok2.PDF 5 H1 Wissenskonstruktion, H2 Faktenwissen
10 How were participants of the first smart speaker experiment recruited? sprachassistent dok3.pdf 5 Prolific, wohnhaft in Deutschland
11 Which scale was used to measure media credibility of the smart speaker? sprachassistent dok3.pdf 6 Flanagin und Metzger 2000, fuenf Items
12 What does the realism heuristic say about modality and trust? sprachassistent dok3.pdf 3 MAIN-Modell, Sundar 2008
13 What was the average age of participants in the replication experiment? sprachassistent dok3.pdf 8 28.2 Jahre, Experiment 2
+44
View File
@@ -0,0 +1,44 @@
"frage","korpus","erwartete_quelle","erwartete_seite","anmerkung"
"Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?","sekundaer","Abraham,_Ulf_FRANZ KAFKA_ DIE VERWANDLUNG von ULF ABRAHAM.pdf","29","Kleist; signalisiert Begrenztheit der Figur"
"Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?","sekundaer","Bauer-Wabnegg,_Walter_MonsterUndMaschinenArtistenUndTechnikInFranzKafkasWerk.pdf","18","im Kino; Bezug auf E 157"
"Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?","sekundaer","Beicken,_Peter_FranzKafka.DieVerwandlung_ErlaeuterungenUndDokumente.pdf","18","Sophokles, ödipales Inzestmotiv und Blindheit"
"Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?","sekundaer","Beissner,_Friedrich_KafkasDarstellungDesTraumhftenInnerenLebens.pdf","8","Selbstanrede in einem Monolog"
"Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?","sekundaer","Beutner,_Barbara_DieBildspracheFranzKafkas.pdf","50","Freiheit verbunden mit Licht und Leben, Wunsch und Hoffnung Karls"
"Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?","sekundaer","Bezzel,_Christoph_NaturBeiKafka_StudienZurAesthetikDesPoetischenZeichens.pdf","4","Naturbild ist Zeichen, das Werk legt Welt aus"
"Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?","sekundaer","Binder,_Hartmut_ErzaehlstrategieInKafkasVerwandlung.pdf","8","Angst, Korrekturversuch, Misslingen"
"Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?","sekundaer","Binder,_Hartmut_Kafka-Komentar_DieVerwandlung.pdf","5","Schwarzkäfer (T 325), Mistkäfer"
"Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?","sekundaer","Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf","83","Kreisbahnen K.s um das Schloss; Strukturparallele"
"Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?","sekundaer","Binder,_Hartmut_KafkaHandbuch_Band2_CDieEinzelenenWerke.pdf","24","Solidarität zwischen den Geschwistern"
"Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?","sekundaer","Binder,_Hartmut_Kafkas_Verwandlung_EntstehungDeutungWirkung_Kapitel3.pdf","32","Tschechisch; auch Thomas Mann, Tod in Venedig"
"Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?","sekundaer","Binder,_Hartmut_Kafkas_Verwandlung_EntstehungDeutungWirkung_Kapitel4.pdf","35","nicht verifizierbare Spekulation, Voraussetzungen irrig"
"Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?","sekundaer","Binder,_Hartmut_MotivUndGestaltungBeiFranzKafka_Darstellung_DieVerwandlung.pdf","9","Ferne der Helden vom Ziel; Schloss als Fragment stimmiger"
"Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?","sekundaer","Binder,_Hartmut_RedensartenUndRedewendungenBeiKafka.pdf","12","sein Kreuz tragen; Dom-Kapitel"
"Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?","sekundaer","DeRentis,_Dina_Neudefinition des Unheimlichen.pdf","35","tief erschüttert, Coppola verfolgt sie bis in den Schlaf"
"Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?","sekundaer","Dirksen,_Jens_KafkaWowertlich_ZurKritischenAusgabeDerSchriftenTagebuecherBriefe.pdf","8","Holitscher, Soukup"
"Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?","sekundaer","Eschweiler,_Christian_KafkasErzaehlungenUndIhr VerborgenerHintergrund.pdf","12","Unwohlsein aus geschäftlichen Rücksichten überwinden; nichts im Kopf als das Geschäft"
"In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?","sekundaer","Fiechter,_HansPaul_KafkasFiktionalerRaum.pdf","10","Traum-Welt, Wach-Welt, Käfer-Körper"
"Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?","sekundaer","Fingerhut,_Karl-Heinz_DieFuktionDerTierfigurenImWerkeFranzKafkas.pdf","93","Forschungen eines Hundes; Wer Speise hat, behält sie"
"Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?","sekundaer","Gerhardt,_Marlis_DieSpracheKafkas_EineSemiotischeUntersuchung.pdf","22","können vorn, dürfen nicht nachweisbar; Textschicht stark beschädigt"
"Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?","sekundaer","Grosse,_Wilhelm_FranzKafka_DieVerwandlung_Lektuereschluessel.pdf","13","hilflos, dann Beschützerin, dann schläfrige Hilflosigkeit"
"Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?","sekundaer","HartNibbrig,_CriatsiaanL_JaUndNein_StudienZurKonstitutionVonnWertgefuegenInTexten.pdf","9","handlungstheoretische statt werttheoretische Begründung"
"Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?","sekundaer","Harzer,_Friedemann_Erzählte Verwandlung.pdf","65","9. November 1911, Theaterphantasie am Altstädter Ring; Standbild Karls IV."
"Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?","sekundaer","Hasselblatt,_Dieter_Zauber und Logik_Eine Kafka-Studie. .pdf","8","das Zeug; Gewöhnliches kommt in Ordnung"
"Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?","sekundaer","Heintz,_Guenter_FranzKafka_SprachreflexionAlsDichterischeEinbildungskraft.pdf","22","Über Sprache und Worte; wegen der Sprache lesen; Textschicht stark beschädigt"
"Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?","sekundaer","Hillmann,_Heinz_FranzKafka_DichtungstheorieUndDichtungsgestalt.pdf","34","Einbruch und Verdrängung; Es ist nichts"
"Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?","primaer","Kafka_Die_Verwandlung_.pdf","13","schlägt die Tür zu, kommt auf Fußspitzen zurück; Milchnapf"
"Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?","primaer","Kafka_Die_Verwandlung_.pdf","21","war das noch der Vater?; früher müde im Schlafrock"
"Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?","primaer","Kafka_Die_Verwandlung_.pdf","25","alter Mistkäfer; hebt einen Stuhl"
"Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?","sekundaer","Kittler,_Wolf_BriefOderBlick_DieSchreibsitutationDerFruehenTexteVonFranzKafka.pdf","4","Stange in baumelnder Bewegung; gelber Schädel"
"Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?","sekundaer","Kittler,_Wolf_Neumann,_Gerhard_KafkasDruckeZuLebzeiten.pdf","17","der heilige Drache, Atem in Richtung Peking"
"Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?","sekundaer","Kittler,_Wolf_SchreibmaschineSprechmaschinen_EffekteTechnischerMedienImWerkFranzkafkas.pdf","18","Therese, Schreibmaschinistin"
"Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?","sekundaer","Klatt,_Reinhard_BildUndStrukturInDerDichtungFranzKafka.pdf","28","Dasein als Verpflichtung zur Tat, niemals abbrechender Kampf"
"Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?","sekundaer","Kremer,_Detlef_Kafka_DieErtotikDesSchreibens.pdf","6","Pelzwerk, Peitschen, Stiefel, rosa Wunde"
"Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?","sekundaer","Krock,_Marianne_FranzKafkaVerwandlung_DeutungNachBrehmsThierleben.pdf","4","Gespenst, Schreckgestalt; unerlöste Seele, Vorhölle"
"Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?","sekundaer","Krock,_Marianne_Oberflaechen-UndTiefenschichtImWerkeKafkas.pdf","27","Himmelskette"
"Worauf verweisen die eckigen Schultern des Beters nach Lehmann?","sekundaer","Lehmann,_Hans-Thies_DerBuchstaeblicheKoerper_ZurSelbstinszenierungDerLiteraturBeiFranzKafka.pdf","12","Ecken des Papiers, Buch im Auslagefenster"
"Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?","sekundaer","Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf","3","Entfremdung, La belle et la bête, Kastration"
"Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?","sekundaer","Nekula,_Marek_FranzKafkasSprachen.pdf","42","trotzdem im Sinne von obwohl; obzwar"
"Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?","sekundaer","Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf","8","Affe Rotpeter; Selbsterschaffung des Menschen"
"Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?","sekundaer","Rajec,_Elizabeth_NamenUndIhreBedeutungenImWerkFranzKafkas-EinInterpretatorischerVersuch.pdf","20","Erlöser, Retter, Heiland"
"Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?","sekundaer","Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf","32","kein Naturbild, Distanzhaltung des Außenseiters"
"Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?","sekundaer","Trost,_Igor_Erzählen und Besprechen.pdf","3","Prager Deutsch mit Blick auf die Aussprache; deutsche Hochsprache, Abgrenzung von Kuchelböhmisch"
1 frage korpus erwartete_quelle erwartete_seite anmerkung
2 Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen? sekundaer Abraham,_Ulf_FRANZ KAFKA_ DIE VERWANDLUNG von ULF ABRAHAM.pdf 29 Kleist; signalisiert Begrenztheit der Figur
3 Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst? sekundaer Bauer-Wabnegg,_Walter_MonsterUndMaschinenArtistenUndTechnikInFranzKafkasWerk.pdf 18 im Kino; Bezug auf E 157
4 Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet? sekundaer Beicken,_Peter_FranzKafka.DieVerwandlung_ErlaeuterungenUndDokumente.pdf 18 Sophokles, ödipales Inzestmotiv und Blindheit
5 Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme? sekundaer Beissner,_Friedrich_KafkasDarstellungDesTraumhftenInnerenLebens.pdf 8 Selbstanrede in einem Monolog
6 Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht? sekundaer Beutner,_Barbara_DieBildspracheFranzKafkas.pdf 50 Freiheit verbunden mit Licht und Leben, Wunsch und Hoffnung Karls
7 Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus? sekundaer Bezzel,_Christoph_NaturBeiKafka_StudienZurAesthetikDesPoetischenZeichens.pdf 4 Naturbild ist Zeichen, das Werk legt Welt aus
8 Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht? sekundaer Binder,_Hartmut_ErzaehlstrategieInKafkasVerwandlung.pdf 8 Angst, Korrekturversuch, Misslingen
9 Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin? sekundaer Binder,_Hartmut_Kafka-Komentar_DieVerwandlung.pdf 5 Schwarzkäfer (T 325), Mistkäfer
10 Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten? sekundaer Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf 83 Kreisbahnen K.s um das Schloss; Strukturparallele
11 Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig? sekundaer Binder,_Hartmut_KafkaHandbuch_Band2_CDieEinzelenenWerke.pdf 24 Solidarität zwischen den Geschwistern
12 Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen? sekundaer Binder,_Hartmut_Kafkas_Verwandlung_EntstehungDeutungWirkung_Kapitel3.pdf 32 Tschechisch; auch Thomas Mann, Tod in Venedig
13 Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher? sekundaer Binder,_Hartmut_Kafkas_Verwandlung_EntstehungDeutungWirkung_Kapitel4.pdf 35 nicht verifizierbare Spekulation, Voraussetzungen irrig
14 Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess? sekundaer Binder,_Hartmut_MotivUndGestaltungBeiFranzKafka_Darstellung_DieVerwandlung.pdf 9 Ferne der Helden vom Ziel; Schloss als Fragment stimmiger
15 Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet? sekundaer Binder,_Hartmut_RedensartenUndRedewendungenBeiKafka.pdf 12 sein Kreuz tragen; Dom-Kapitel
16 Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius? sekundaer DeRentis,_Dina_Neudefinition des Unheimlichen.pdf 35 tief erschüttert, Coppola verfolgt sie bis in den Schlaf
17 Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht? sekundaer Dirksen,_Jens_KafkaWowertlich_ZurKritischenAusgabeDerSchriftenTagebuecherBriefe.pdf 8 Holitscher, Soukup
18 Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein? sekundaer Eschweiler,_Christian_KafkasErzaehlungenUndIhr VerborgenerHintergrund.pdf 12 Unwohlsein aus geschäftlichen Rücksichten überwinden; nichts im Kopf als das Geschäft
19 In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint? sekundaer Fiechter,_HansPaul_KafkasFiktionalerRaum.pdf 10 Traum-Welt, Wach-Welt, Käfer-Körper
20 Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur? sekundaer Fingerhut,_Karl-Heinz_DieFuktionDerTierfigurenImWerkeFranzKafkas.pdf 93 Forschungen eines Hundes; Wer Speise hat, behält sie
21 Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben? sekundaer Gerhardt,_Marlis_DieSpracheKafkas_EineSemiotischeUntersuchung.pdf 22 können vorn, dürfen nicht nachweisbar; Textschicht stark beschädigt
22 Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung? sekundaer Grosse,_Wilhelm_FranzKafka_DieVerwandlung_Lektuereschluessel.pdf 13 hilflos, dann Beschützerin, dann schläfrige Hilflosigkeit
23 Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten? sekundaer HartNibbrig,_CriatsiaanL_JaUndNein_StudienZurKonstitutionVonnWertgefuegenInTexten.pdf 9 handlungstheoretische statt werttheoretische Begründung
24 Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt? sekundaer Harzer,_Friedemann_Erzählte Verwandlung.pdf 65 9. November 1911, Theaterphantasie am Altstädter Ring; Standbild Karls IV.
25 Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen? sekundaer Hasselblatt,_Dieter_Zauber und Logik_Eine Kafka-Studie. .pdf 8 das Zeug; Gewöhnliches kommt in Ordnung
26 Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre? sekundaer Heintz,_Guenter_FranzKafka_SprachreflexionAlsDichterischeEinbildungskraft.pdf 22 Über Sprache und Worte; wegen der Sprache lesen; Textschicht stark beschädigt
27 Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten? sekundaer Hillmann,_Heinz_FranzKafka_DichtungstheorieUndDichtungsgestalt.pdf 34 Einbruch und Verdrängung; Es ist nichts
28 Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet? primaer Kafka_Die_Verwandlung_.pdf 13 schlägt die Tür zu, kommt auf Fußspitzen zurück; Milchnapf
29 Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht? primaer Kafka_Die_Verwandlung_.pdf 21 war das noch der Vater?; früher müde im Schlafrock
30 Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm? primaer Kafka_Die_Verwandlung_.pdf 25 alter Mistkäfer; hebt einen Stuhl
31 Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes? sekundaer Kittler,_Wolf_BriefOderBlick_DieSchreibsitutationDerFruehenTexteVonFranzKafka.pdf 4 Stange in baumelnder Bewegung; gelber Schädel
32 Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist? sekundaer Kittler,_Wolf_Neumann,_Gerhard_KafkasDruckeZuLebzeiten.pdf 17 der heilige Drache, Atem in Richtung Peking
33 Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer? sekundaer Kittler,_Wolf_SchreibmaschineSprechmaschinen_EffekteTechnischerMedienImWerkFranzkafkas.pdf 18 Therese, Schreibmaschinistin
34 Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen? sekundaer Klatt,_Reinhard_BildUndStrukturInDerDichtungFranzKafka.pdf 28 Dasein als Verpflichtung zur Tat, niemals abbrechender Kampf
35 Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder? sekundaer Kremer,_Detlef_Kafka_DieErtotikDesSchreibens.pdf 6 Pelzwerk, Peitschen, Stiefel, rosa Wunde
36 Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor? sekundaer Krock,_Marianne_FranzKafkaVerwandlung_DeutungNachBrehmsThierleben.pdf 4 Gespenst, Schreckgestalt; unerlöste Seele, Vorhölle
37 Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette? sekundaer Krock,_Marianne_Oberflaechen-UndTiefenschichtImWerkeKafkas.pdf 27 Himmelskette
38 Worauf verweisen die eckigen Schultern des Beters nach Lehmann? sekundaer Lehmann,_Hans-Thies_DerBuchstaeblicheKoerper_ZurSelbstinszenierungDerLiteraturBeiFranzKafka.pdf 12 Ecken des Papiers, Buch im Auslagefenster
39 Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen? sekundaer Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf 3 Entfremdung, La belle et la bête, Kastration
40 Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu? sekundaer Nekula,_Marek_FranzKafkasSprachen.pdf 42 trotzdem im Sinne von obwohl; obzwar
41 Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin? sekundaer Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf 8 Affe Rotpeter; Selbsterschaffung des Menschen
42 Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus? sekundaer Rajec,_Elizabeth_NamenUndIhreBedeutungenImWerkFranzKafkas-EinInterpretatorischerVersuch.pdf 20 Erlöser, Retter, Heiland
43 Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt? sekundaer Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf 32 kein Naturbild, Distanzhaltung des Außenseiters
44 Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich? sekundaer Trost,_Igor_Erzählen und Besprechen.pdf 3 Prager Deutsch mit Blick auf die Aussprache; deutsche Hochsprache, Abgrenzung von Kuchelböhmisch
+31
View File
@@ -0,0 +1,31 @@
frage;korpus;erwartete_quelle;erwartete_seite
Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?;sekundär;Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf;1
Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?;sekundär;Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf;11
Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?;sekundär;Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf;11
In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?;sekundär;Harzer,_Friedemann_Erzählte Verwandlung.pdf;7
Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?;sekundär;Hillmann,_Heinz_FranzKafka_DichtungstheorieUndDichtungsgestalt.pdf;11
Was kann man zu Bildformen in Kafkas Werken sagen?;sekundär;Hillmann,_Heinz_FranzKafka_DichtungstheorieUndDichtungsgestalt.pdf;15
Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?;sekundär;Krock,_Marianne_FranzKafkaVerwandlung_DeutungNachBrehmsThierleben.pdf;12
"In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in ""Die Verwandlung""?";sekundär;Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf;2
Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?;sekundär;Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf;2
"Was kann als ""letzte Sohnespflicht"" Gregors gedeutet werden?";sekundär;Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf;6
Wo erfahren die Eltern die Nachricht von Gregors Tod?;sekundär;Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf;6
Wie erklärt Binder die Etymologie des Familiennamens Kafka?;sekundär;Nekula,_Marek_FranzKafkasSprachen.pdf;8
Sprach Kafka Tschechisch?;sekundär;Nekula,_Marek_FranzKafkasSprachen.pdf;9
Wie heißen die beiden Nichten von Kafka?;sekundär;Nekula,_Marek_FranzKafkasSprachen.pdf;9
Welche Sprache spricht Kafka mit seinen Nichten?;sekundär;Nekula,_Marek_FranzKafkasSprachen.pdf;9
Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?;sekundär;Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf;3
"Was bedeutet das tschechische Wort ""Kafka"" auf Deutsch?";sekundär;Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf;2
Welche Art von Laden führte Kafkas Vater?;sekundär;Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf;2
Aus welchem sozialen Milieu stammte Kafkas Vater genau?;sekundär;Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf;2
Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?;sekundär;Rajec,_Elizabeth_NamenUndIhreBedeutungenImWerkFranzKafkas-EinInterpretatorischerVersuch.pdf;9
Wie interpretiert Emrich das Besondere an der Kafkaschen Form?;sekundär;Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf;8
"In welchem Band der Reihe ""Der jüngste Tag"" wurde ""Die Verwandlung"" veröffentlicht?";sekundär;Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf;55
"Seit wann lag ""Die Verwandlung"" abgeschlossen vor, ohne veröffentlicht zu sein?";sekundär;Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf;55
Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?;sekundär;Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf;58
Wie könnte man Kafkas ethischen Rigorismus korrigieren?;sekundär;Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf;58
Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?;sekundär;Trost,_Igor_Erzählen und Besprechen.pdf;1
"Wer ist der Protagonist von ""Die Verwandlung""?";primär;Kafka_Die_Verwandlung.pdf;2
In welche Art von Tier verwandelt sich Gregor Samsa?;primär;Kafka_Die_Verwandlung.pdf;2
Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?;primär;Kafka_Die_Verwandlung.pdf;30
Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?;primär;Kafka_Die_Verwandlung.pdf;32
1 frage korpus erwartete_quelle erwartete_seite
2 Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber? sekundär Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf 1
3 Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus? sekundär Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf 11
4 Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten? sekundär Binder,_Hartmut_KafkaHandbuch_Band2_AText_BAesthetik.pdf 11
5 In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen? sekundär Harzer,_Friedemann_Erzählte Verwandlung.pdf 7
6 Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen? sekundär Hillmann,_Heinz_FranzKafka_DichtungstheorieUndDichtungsgestalt.pdf 11
7 Was kann man zu Bildformen in Kafkas Werken sagen? sekundär Hillmann,_Heinz_FranzKafka_DichtungstheorieUndDichtungsgestalt.pdf 15
8 Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden? sekundär Krock,_Marianne_FranzKafkaVerwandlung_DeutungNachBrehmsThierleben.pdf 12
9 In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"? sekundär Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf 2
10 Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden? sekundär Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf 2
11 Was kann als "letzte Sohnespflicht" Gregors gedeutet werden? sekundär Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf 6
12 Wo erfahren die Eltern die Nachricht von Gregors Tod? sekundär Matz,_Wolfgang_DerSchlafDerVernunftGebiertUngeheuer_MotiveZuEinerLektuereVonKafkasVerwandlung.pdf 6
13 Wie erklärt Binder die Etymologie des Familiennamens Kafka? sekundär Nekula,_Marek_FranzKafkasSprachen.pdf 8
14 Sprach Kafka Tschechisch? sekundär Nekula,_Marek_FranzKafkasSprachen.pdf 9
15 Wie heißen die beiden Nichten von Kafka? sekundär Nekula,_Marek_FranzKafkasSprachen.pdf 9
16 Welche Sprache spricht Kafka mit seinen Nichten? sekundär Nekula,_Marek_FranzKafkasSprachen.pdf 9
17 Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt? sekundär Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf 3
18 Was bedeutet das tschechische Wort "Kafka" auf Deutsch? sekundär Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf 2
19 Welche Art von Laden führte Kafkas Vater? sekundär Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf 2
20 Aus welchem sozialen Milieu stammte Kafkas Vater genau? sekundär Neumann,_Gerhard_FranzKafka_DerNameDieSpracheUndDieOrdnungDerDinge.pdf 2
21 Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung? sekundär Rajec,_Elizabeth_NamenUndIhreBedeutungenImWerkFranzKafkas-EinInterpretatorischerVersuch.pdf 9
22 Wie interpretiert Emrich das Besondere an der Kafkaschen Form? sekundär Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf 8
23 In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht? sekundär Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf 55
24 Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein? sekundär Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf 55
25 Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten? sekundär Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf 58
26 Wie könnte man Kafkas ethischen Rigorismus korrigieren? sekundär Richter,_Helmut_FranzKafka_WerkUndEntwurf.pdf 58
27 Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen? sekundär Trost,_Igor_Erzählen und Besprechen.pdf 1
28 Wer ist der Protagonist von "Die Verwandlung"? primär Kafka_Die_Verwandlung.pdf 2
29 In welche Art von Tier verwandelt sich Gregor Samsa? primär Kafka_Die_Verwandlung.pdf 2
30 Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden? primär Kafka_Die_Verwandlung.pdf 30
31 Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung? primär Kafka_Die_Verwandlung.pdf 32
+70
View File
@@ -0,0 +1,70 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 11:25
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.67 (29 von 43)
Recall@10 0.77 (33 von 43)
Recall@20 0.79 (34 von 43)
MRR 0.49
Nach Korpus
primaer Recall@5 0.67 MRR 0.42 (3 Fragen)
sekundaer Recall@5 0.68 MRR 0.50 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 2 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 2 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 2 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 2 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 2 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 4 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 4 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 4 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 5 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 5 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 6 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 8 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 8 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 8 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 16 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang – sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
9 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+71
View File
@@ -0,0 +1,71 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:15
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.93 (40 von 43)
Recall@10 0.93 (40 von 43)
Recall@20 0.98 (42 von 43)
MRR 0.86
Nach Korpus
primaer Recall@5 0.33 MRR 0.17 (3 Fragen)
sekundaer Recall@5 0.97 MRR 0.91 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang 1 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang 1 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 1 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 1 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 2 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 2 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 11 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 13 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
1 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+71
View File
@@ -0,0 +1,71 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:36
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.72 (31 von 43)
Recall@10 0.77 (33 von 43)
Recall@20 0.81 (35 von 43)
MRR 0.55
Nach Korpus
primaer Recall@5 0.67 MRR 0.30 (3 Fragen)
sekundaer Recall@5 0.72 MRR 0.57 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 2 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 2 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 2 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 2 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 2 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 2 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 2 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 3 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 4 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 7 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 10 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 11 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 13 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
8 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+71
View File
@@ -0,0 +1,71 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:38
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.79 (34 von 43)
Recall@10 0.81 (35 von 43)
Recall@20 0.84 (36 von 43)
MRR 0.72
Nach Korpus
primaer Recall@5 0.33 MRR 0.19 (3 Fragen)
sekundaer Recall@5 0.82 MRR 0.76 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 1 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 2 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 2 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 6 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 14 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
7 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+67
View File
@@ -0,0 +1,67 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:36
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.84 (36 von 43)
Recall@10 0.91 (39 von 43)
Recall@20 1.00 (43 von 43)
MRR 0.70
Nach Korpus
primaer Recall@5 0.33 MRR 0.13 (3 Fragen)
sekundaer Recall@5 0.88 MRR 0.74 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 2 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 2 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 3 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 3 sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang 3 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 3 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 4 sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang 4 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 7 sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang 9 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 10 sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang 12 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 15 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 15 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 16 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
+71
View File
@@ -0,0 +1,71 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:41
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.93 (40 von 43)
Recall@10 0.93 (40 von 43)
Recall@20 0.98 (42 von 43)
MRR 0.86
Nach Korpus
primaer Recall@5 0.33 MRR 0.17 (3 Fragen)
sekundaer Recall@5 0.97 MRR 0.91 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang 1 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang 1 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 1 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 1 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 2 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 2 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 11 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 13 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
1 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+70
View File
@@ -0,0 +1,70 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 14:00
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_split
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.72 (31 von 43)
Recall@10 0.77 (33 von 43)
Recall@20 0.77 (33 von 43)
MRR 0.56
Nach Korpus
primaer Recall@5 0.67 MRR 0.42 (3 Fragen)
sekundaer Recall@5 0.72 MRR 0.57 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 2 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 2 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 2 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 2 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 3 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 4 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 4 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 5 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 7 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 9 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang – sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang – sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
10 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+70
View File
@@ -0,0 +1,70 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 11:43
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext an
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.51 (22 von 43)
Recall@10 0.53 (23 von 43)
Recall@20 0.58 (25 von 43)
MRR 0.34
Nach Korpus
primaer Recall@5 0.00 MRR 0.02 (3 Fragen)
sekundaer Recall@5 0.55 MRR 0.37 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 2 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 3 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 3 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 3 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 4 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 4 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 4 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 4 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 4 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 5 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 7 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 18 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 20 sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang – sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang – sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang – sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang – sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang – sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang – primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang – sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang – sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang – sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
18 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+58
View File
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:55
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker aus
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.50 (15 von 30)
Recall@10 0.60 (18 von 30)
Recall@20 0.67 (20 von 30)
MRR 0.41
Nach Korpus
primär Recall@5 0.00 MRR 0.03 (4 Fragen)
sekundär Recall@5 0.58 MRR 0.47 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 2 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 2 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 4 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 4 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 5 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 6 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 9 sekundär Sprach Kafka Tschechisch?
Rang 10 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang 13 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 19 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang – sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
10 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+58
View File
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:57
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.63 (19 von 30)
Recall@10 0.63 (19 von 30)
Recall@20 0.70 (21 von 30)
MRR 0.56
Nach Korpus
primär Recall@5 0.00 MRR 0.02 (4 Fragen)
sekundär Recall@5 0.73 MRR 0.64 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 1 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang 1 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 2 sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang 2 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 3 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 4 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 11 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang 19 sekundär Sprach Kafka Tschechisch?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
9 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+58
View File
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:56
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker aus
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.53 (16 von 30)
Recall@10 0.63 (19 von 30)
Recall@20 0.67 (20 von 30)
MRR 0.42
Nach Korpus
primär Recall@5 0.00 MRR 0.00 (4 Fragen)
sekundär Recall@5 0.62 MRR 0.49 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 1 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 3 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 4 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 4 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 5 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 5 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 8 sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang 8 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 9 sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang 11 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär Sprach Kafka Tschechisch?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
10 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:59
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_docling_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.70 (21 von 30)
Recall@10 0.70 (21 von 30)
Recall@20 0.77 (23 von 30)
MRR 0.63
Nach Korpus
primär Recall@5 0.00 MRR 0.02 (4 Fragen)
sekundär Recall@5 0.81 MRR 0.72 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 1 sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 1 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang 1 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 2 sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang 2 sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang 2 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 5 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 11 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang 18 sekundär Sprach Kafka Tschechisch?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
7 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+59
View File
@@ -0,0 +1,59 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 16:04
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Loader pypdf, Kopfzeilen aus
Query-Zusatztext aus
Suche dense, Reranker aus
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.40 (12 von 30)
Recall@10 0.57 (17 von 30)
Recall@20 0.60 (18 von 30)
MRR 0.38
Nach Korpus
primär Recall@5 0.00 MRR 0.00 (4 Fragen)
sekundär Recall@5 0.46 MRR 0.44 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 2 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 5 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 7 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 7 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 9 sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang 9 sekundär Sprach Kafka Tschechisch?
Rang 10 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 18 sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang – sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang – sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
12 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+59
View File
@@ -0,0 +1,59 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 16:15
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_2000
Embedding-Modell BAAI/bge-m3
Chunk-Größe 2000 (Overlap 400)
Loader pypdf, Kopfzeilen aus
Query-Zusatztext aus
Suche dense, Reranker aus
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.50 (15 von 30)
Recall@10 0.63 (19 von 30)
Recall@20 0.73 (22 von 30)
MRR 0.36
Nach Korpus
primär Recall@5 0.00 MRR 0.00 (4 Fragen)
sekundär Recall@5 0.58 MRR 0.42 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 2 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 2 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 2 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 2 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 3 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 3 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 4 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 4 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 6 sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang 9 sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang 9 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 10 sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang 12 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 14 sekundär Sprach Kafka Tschechisch?
Rang 14 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
8 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
@@ -0,0 +1,59 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 16:17
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_2000
Embedding-Modell BAAI/bge-m3
Chunk-Größe 2000 (Overlap 400)
Loader pypdf, Kopfzeilen aus
Query-Zusatztext aus
Suche hybrid, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.70 (21 von 30)
Recall@10 0.73 (22 von 30)
Recall@20 0.83 (25 von 30)
MRR 0.69
Nach Korpus
primär Recall@5 0.00 MRR 0.02 (4 Fragen)
sekundär Recall@5 0.81 MRR 0.79 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 1 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 1 sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 1 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang 1 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 5 sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang 7 sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang 11 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang 15 sekundär Sprach Kafka Tschechisch?
Rang 16 sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
5 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+58
View File
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:50
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker aus
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.37 (11 von 30)
Recall@10 0.50 (15 von 30)
Recall@20 0.53 (16 von 30)
MRR 0.35
Nach Korpus
primär Recall@5 0.00 MRR 0.03 (4 Fragen)
sekundär Recall@5 0.42 MRR 0.40 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 2 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 2 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 6 sekundär Sprach Kafka Tschechisch?
Rang 6 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 9 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 9 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang 17 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang – sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang – sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang – sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang – sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
14 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+58
View File
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:53
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.63 (19 von 30)
Recall@10 0.77 (23 von 30)
Recall@20 0.77 (23 von 30)
MRR 0.58
Nach Korpus
primär Recall@5 0.00 MRR 0.03 (4 Fragen)
sekundär Recall@5 0.73 MRR 0.66 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 2 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 2 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 2 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang 3 sekundär Sprach Kafka Tschechisch?
Rang 6 sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang 9 sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang 10 sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang 10 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
7 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+58
View File
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:51
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker aus
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.53 (16 von 30)
Recall@10 0.63 (19 von 30)
Recall@20 0.70 (21 von 30)
MRR 0.41
Nach Korpus
primär Recall@5 0.00 MRR 0.03 (4 Fragen)
sekundär Recall@5 0.62 MRR 0.47 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 2 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 3 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 4 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 4 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 4 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 5 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 7 sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang 9 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang 10 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 12 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 19 sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang – sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär Sprach Kafka Tschechisch?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
9 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+58
View File
@@ -0,0 +1,58 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:55
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 30
Ergebnis
Recall@5 0.73 (22 von 30)
Recall@10 0.77 (23 von 30)
Recall@20 0.80 (24 von 30)
MRR 0.60
Nach Korpus
primär Recall@5 0.00 MRR 0.03 (4 Fragen)
sekundär Recall@5 0.85 MRR 0.68 (26 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundär Wie stand Kafkas Familie seiner literarischen Tätigkeit nach Kafkas Tod gegenüber?
Rang 1 sekundär In welchem Band von Jacob Burckhardts Griechischer Kulturgeschichte gibt es ein eigenes Kapitel über Metamorphosen?
Rang 1 sekundär In welchem Zusammenhang stehen körperlicher Ekel und Selbstdemütigung in "Die Verwandlung"?
Rang 1 sekundär Was kann als "letzte Sohnespflicht" Gregors gedeutet werden?
Rang 1 sekundär Wie erklärt Binder die Etymologie des Familiennamens Kafka?
Rang 1 sekundär Wie heißen die beiden Nichten von Kafka?
Rang 1 sekundär Welche Sprache spricht Kafka mit seinen Nichten?
Rang 1 sekundär Inwiefern sind die Namen Kafka und Samsa aufeinander abgestimmt?
Rang 1 sekundär Was bedeutet das tschechische Wort "Kafka" auf Deutsch?
Rang 1 sekundär Welche Art von Laden führte Kafkas Vater?
Rang 1 sekundär Aus welchem sozialen Milieu stammte Kafkas Vater genau?
Rang 1 sekundär Welche Namen vergleicht Ingeborg Bachmann in ihrem Essay hinsichtlich der Namensgebung?
Rang 1 sekundär Wie interpretiert Emrich das Besondere an der Kafkaschen Form?
Rang 1 sekundär Wie könnte man Kafkas ethischen Rigorismus korrigieren?
Rang 1 sekundär Welche Erzählung von Franz Kafka ist der Gattung der Großstadterzählung beziehungsweise Großstadtnovelle zuzuordnen?
Rang 2 sekundär Wo erfahren die Eltern die Nachricht von Gregors Tod?
Rang 2 sekundär In welchem Band der Reihe "Der jüngste Tag" wurde "Die Verwandlung" veröffentlicht?
Rang 2 sekundär Seit wann lag "Die Verwandlung" abgeschlossen vor, ohne veröffentlicht zu sein?
Rang 3 sekundär Was kann man zu Formen der Gestik und Mimik in Kafkas Werken sagen?
Rang 3 sekundär Wie kann das Bild der eleganten Dame in Gregors Zimmer gedeutet werden?
Rang 3 sekundär Sprach Kafka Tschechisch?
Rang 5 sekundär Was kann man zu Bildformen in Kafkas Werken sagen?
Rang 9 primär In welche Art von Tier verwandelt sich Gregor Samsa?
Rang 11 sekundär Wie können die drei Untermieter der Familie Kafka aus einer biblischen Perspektive gedeutet werden?
Rang – sekundär Was sagen die Aussagen seines Vorgesetzten über Gregors berufliche Leistung aus?
Rang – sekundär Inwiefern steht Gregors Selbstwahrnehmung seiner beruflichen Leistung im Widerspruch zu den Aussagen seines Vorgesetzten?
Rang – sekundär Welche Art von Helden in einer Gesellschaft zeichnet Kafka in seinen Geschichten?
Rang – primär Wer ist der Protagonist von "Die Verwandlung"?
Rang – primär Unter welchen Umständen wird Gregor am Ende der Erzählung von seiner Familie aufgefunden?
Rang – primär Wie verändert sich Gregors Verhältnis zu seiner Familie im Verlauf der Erzählung?
6 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+70
View File
@@ -0,0 +1,70 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 11:44
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.67 (29 von 43)
Recall@10 0.74 (32 von 43)
Recall@20 0.74 (32 von 43)
MRR 0.51
Nach Korpus
primaer Recall@5 0.33 MRR 0.11 (3 Fragen)
sekundaer Recall@5 0.70 MRR 0.54 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 2 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 2 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 2 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 2 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 2 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 3 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 3 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 4 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 4 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang 5 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 5 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 6 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 7 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 8 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang – primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang – sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang – sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
11 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+67
View File
@@ -0,0 +1,67 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:29
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.95 (41 von 43)
Recall@10 0.98 (42 von 43)
Recall@20 1.00 (43 von 43)
MRR 0.84
Nach Korpus
primaer Recall@5 0.33 MRR 0.23 (3 Fragen)
sekundaer Recall@5 1.00 MRR 0.89 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang 1 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang 1 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 1 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 1 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 2 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 2 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 3 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 4 sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang 5 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 9 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 12 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
+71
View File
@@ -0,0 +1,71 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:29
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.67 (29 von 43)
Recall@10 0.72 (31 von 43)
Recall@20 0.81 (35 von 43)
MRR 0.56
Nach Korpus
primaer Recall@5 0.33 MRR 0.40 (3 Fragen)
sekundaer Recall@5 0.70 MRR 0.57 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 1 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 2 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 2 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 2 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 3 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 3 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 3 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 4 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 5 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 6 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 10 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 11 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 11 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 13 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 14 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang – sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
8 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+71
View File
@@ -0,0 +1,71 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:32
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche dense, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.81 (35 von 43)
Recall@10 0.84 (36 von 43)
Recall@20 0.88 (38 von 43)
MRR 0.71
Nach Korpus
primaer Recall@5 0.33 MRR 0.25 (3 Fragen)
sekundaer Recall@5 0.85 MRR 0.74 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 1 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 2 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 2 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 2 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 5 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 5 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang 6 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 11 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 15 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
5 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+71
View File
@@ -0,0 +1,71 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:30
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.81 (35 von 43)
Recall@10 0.93 (40 von 43)
Recall@20 0.98 (42 von 43)
MRR 0.65
Nach Korpus
primaer Recall@5 0.67 MRR 0.49 (3 Fragen)
sekundaer Recall@5 0.82 MRR 0.67 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 1 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 2 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 2 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 2 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 2 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 3 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 3 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 4 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 4 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 5 sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang 5 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 5 sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang 6 sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang 7 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 7 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 8 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 10 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 11 sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang 11 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
1 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+67
View File
@@ -0,0 +1,67 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 15:35
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf_hdr
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext aus
Suche hybrid, Reranker BAAI/bge-reranker-v2-m3
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.95 (41 von 43)
Recall@10 0.98 (42 von 43)
Recall@20 1.00 (43 von 43)
MRR 0.84
Nach Korpus
primaer Recall@5 0.33 MRR 0.23 (3 Fragen)
sekundaer Recall@5 1.00 MRR 0.89 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 1 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 1 sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang 1 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang 1 sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang 1 sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang 1 sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang 1 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang 1 sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang 1 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 1 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 2 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 2 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 3 sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 4 sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang 5 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 9 primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang 12 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
+70
View File
@@ -0,0 +1,70 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 09:50
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf
Embedding-Modell sentence-transformers/all-MiniLM-L6-v2
Chunk-Größe 2000 (Overlap 400)
Query-Zusatztext aus
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.23 (10 von 43)
Recall@10 0.35 (15 von 43)
Recall@20 0.42 (18 von 43)
MRR 0.19
Nach Korpus
primaer Recall@5 0.00 MRR 0.09 (3 Fragen)
sekundaer Recall@5 0.25 MRR 0.19 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 1 sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang 3 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 3 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 4 sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
Rang 5 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 6 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang 7 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 8 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 9 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 10 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 11 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 11 primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang 14 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang – sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang – sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang – sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang – sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang – sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang – sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang – sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang – sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang – sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang – sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang – primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang – sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang – sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang – sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang – sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang – sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
25 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+70
View File
@@ -0,0 +1,70 @@
====================================================================
Retrieval-Auswertung — 2026-10-07 11:43
====================================================================
Konfiguration
Vektorspeicher ./vectorstore_pypdf
Embedding-Modell BAAI/bge-m3
Chunk-Größe 1500 (Overlap 300)
Query-Zusatztext an
Seitentoleranz ±1
Fragen 43
Ergebnis
Recall@5 0.53 (23 von 43)
Recall@10 0.56 (24 von 43)
Recall@20 0.63 (27 von 43)
MRR 0.42
Nach Korpus
primaer Recall@5 0.00 MRR 0.03 (3 Fragen)
sekundaer Recall@5 0.57 MRR 0.45 (40 Fragen)
Einzelne Fragen (Rang = Position des ersten korrekten Treffers)
Rang 1 sekundaer Was bedeutet das Sonnenlicht, in dem Karl Roßmann bei der Ankunft in New York die Freiheitsstatue sieht?
Rang 1 sekundaer Ahmt das Naturbild im Amerika-Roman die Welt nach oder legt es sie aus?
Rang 1 sekundaer Welcher dreistufige Ablauf zeigt sich im Motiv des Kopfhebens, als Gregor aus dem Bett zu kommen versucht?
Rang 1 sekundaer Warum bestreitet Kudszus den abschließenden Charakter des Schlusskapitels im Prozess?
Rang 1 sekundaer Welche Quellen Kafkas zum Roman Der Verschollene erwähnt die Kritische Kafka-Ausgabe nicht?
Rang 1 sekundaer Welche gesellschaftlichen Haltungen entlarvt Kafka in den Reflexionen seiner Hundefigur?
Rang 1 sekundaer Welches modale Hilfsverb steht in Kafkas Romanen an der Spitze der Häufigkeitsrangliste, und welches fehlt in den Textproben?
Rang 1 sekundaer Wie unterscheiden sich ästhetische Texte von Pamphlet, Werbung oder Predigt im Umgang mit Werten?
Rang 1 sekundaer Welcher Tagebucheintrag belegt, dass die Irrealisierung in Beschreibung eines Kampfes einer Traumarbeit entspringt?
Rang 1 sekundaer Welches Buch von Schopenhauer empfahl Kafka Gustav Janouch, und womit begründete er die Lektüre?
Rang 1 sekundaer Welches Gegenbild bildet der Dicke zum Ich-Erzähler in Beschreibung eines Kampfes?
Rang 1 sekundaer Was bedeutet Larve nach Grimms Wörterbuch, und wie bezieht Krock das auf Gregor?
Rang 1 sekundaer Wofür steht die Uhrkette in der Tagebuchskizze über den Gast mit der Uhrkette?
Rang 1 sekundaer Wofür steht der Name des Bürgermeisters Salvatore im Jäger Gracchus?
Rang 1 sekundaer Wie bezeichnete Kafka nach Nekula sein eigenes Deutsch, und woran orientierte sich die Prager Oberschicht sprachlich?
Rang 2 sekundaer Welche Käferart nennt Kafka selbst für Gregor, und wie bezeichnet ihn die Bedienerin?
Rang 2 sekundaer Wie lässt sich das Kreisen der Krähen um den Schlossturm als Vorwegnahme des Romanverlaufs deuten?
Rang 2 sekundaer Aus welcher Sprache könnte die Wendung aus unruhigen Träumen erwachen im ersten Satz der Verwandlung stammen?
Rang 3 sekundaer Wie reagiert Clara in Hoffmanns Sandmann auf Nathanaels Brief über Coppelius?
Rang 3 sekundaer Wie wird das Naturerleben in Kafkas frühen Prosastücken wie Zerstreutes Hinausschaun dargestellt?
Rang 5 sekundaer In welche Bereiche wird die Wirklichkeit in der Verwandlung differenziert, sodass die Alltagswelt wie eine Art Traum erscheint?
Rang 5 sekundaer Welche Chiffren einer fetischisierten Lust kehren in Kafkas Texten immer wieder?
Rang 5 sekundaer Welche Konjunktion wird fälschlich als Merkmal des Prager Deutsch gewertet, und welche verwendet Julie Kafka parallel dazu?
Rang 10 sekundaer Welches immer wiederkehrende Schema bestimmt nach Hillmann den Aufbau von Kafkas Geschichten?
Rang 11 sekundaer Welche sprichwörtliche Redensart verwendet Kafka im Gespräch zwischen K. und dem Fabrikanten, und wo wird ihr Vorstellungsbereich entfaltet?
Rang 12 sekundaer Wo konnte man nach Kittler und Neumann sehen, wie einer einen lebenden Ochsen frisst?
Rang 13 primaer Mit welchen Worten ruft die Bedienerin Gregor zu sich, und wie droht sie ihm?
Rang – sekundaer Welche Funktion hat das In-Ohnmacht-Fallen der Mutter in der Verwandlung, und auf welchen Dichter wird dabei verwiesen?
Rang – sekundaer Wie wird im Kommentar die nachlassende Sehkraft Gregors mythologisch gedeutet?
Rang – sekundaer Warum ist der Umschlag in die zweite Person in der Erzählung Heimkehr nur eine scheinbare Ausnahme?
Rang – sekundaer Welche Ausgangskonstellation erweist sich in der Verwandlung im Erzählverlauf als nicht tragfähig?
Rang – sekundaer Wie beurteilt Binder Corngolds Deutung der Verwandlung als Geschichte einer verwandelten Metapher?
Rang – sekundaer Wie reagieren der Prokurist und die Mutter auf Gregors angebliches Unwohlsein?
Rang – sekundaer Wie verändert sich die Figur der Mutter im Verlauf der Verwandlung?
Rang – sekundaer Wie wird Gregor nach seinem Tod von der Bedienerin bezeichnet, und was bedeutet das für die Ordnung des Gewöhnlichen?
Rang – primaer Wie reagiert die Schwester, als sie Gregor am ersten Morgen nicht gleich in seinem Zimmer findet?
Rang – primaer Wie erlebt Gregor den Vater, als er ihn nach längerer Zeit in Uniform wiedersieht?
Rang – sekundaer Was steht auf der kleinen Säule am Dorfausgang, von der in Kafkas Text über den Kaiser die Rede ist?
Rang – sekundaer Welche Figur im Verschollenen hat den gleichen Beruf wie Kafkas spätere Verlobte Felice Bauer?
Rang – sekundaer Wie verhält sich bei Kafka der Anspruch der Aufgabe zum Ungenügen des Menschen?
Rang – sekundaer Worauf verweisen die eckigen Schultern des Beters nach Lehmann?
Rang – sekundaer Welche Deutungen der Verwandlung werden im Exkurs über ihren Gleichnischarakter erwogen?
Rang – sekundaer Welche Verwandlungsgeschichte nennt Neumann als beispielhaft, und was versucht Kafka darin?
16 Frage(n) ohne Treffer unter den ersten 20.
Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?
Ist das Dokument überhaupt im Index (rag.list_documents())?
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
# Messreihe: 4 Indizes (2000/400) x 3 Suchvarianten mit Reranker x 2 Fragensätze
cd "$(dirname "$0")/.."
P=/Users/Philipp/opt/anaconda3/envs/docling/bin/python
until [ -f vectorstore_docling_2000_hdr/index_info.json ] && [ -f vectorstore_docling_2000/index_info.json ] && [ -f vectorstore_pypdf_2000_hdr/index_info.json ]; do sleep 30; done
for GS in human synthetic; do
[ $GS = human ] && G=eval/goldset_kafka_human.csv || G=eval/goldset_kafka.csv
for I in pypdf_2000 pypdf_2000_hdr docling_2000 docling_2000_hdr; do
for M in dense hybrid union; do
tag="m2000_${GS}_${I}_${M}"
caffeinate -i $P eval_retrieval.py --goldset $G --index ./vectorstore_$I --report eval/kafka_${tag}.txt --mode $M > eval/eval_${tag}.log 2>&1
printf "%-10s %-18s %-7s " "$GS" "$I" "$M"
grep -E '^ (Recall@|MRR)' eval/kafka_${tag}.txt | awk '{printf "%s %s ", $1, $2}'; echo
done
done
done
echo FERTIG
+275
View File
@@ -0,0 +1,275 @@
#!/usr/bin/env python3
"""Bewertet die Retrieval-Qualität gegen einen Satz Referenzfragen. Kein LLM beteiligt.
Das Skript stellt jede Frage aus eval/goldset.csv an den Vektorspeicher und prüft,
ob die erwartete Fundstelle unter den ersten k Treffern auftaucht. Ergebnis sind
Recall@k und MRR, ausgegeben auf dem Bildschirm und in eval/baseline.txt.
Aus dem Projektordner starten: python eval_retrieval.py
Fragen, Index und Bericht lassen sich auf der Kommandozeile überschreiben, z. B.
python eval_retrieval.py --goldset eval/goldset_kafka.csv --index ./vectorstore_docling \
--report eval/kafka_docling.txt
"""
import argparse
import csv
import sys
import unicodedata
from datetime import datetime
from pathlib import Path
from local_rag import (
CHUNK_OVERLAP,
CHUNK_SIZE,
QUERY_ADDITIONAL_INSTRUCTIONS,
LocalRAG,
)
# --- Einstellungen -----------------------------------------------------------
# Datei mit den Referenzfragen (Spalten siehe eval/goldset.csv).
GOLDSET_PATH = Path("eval/goldset.csv")
# Wohin der Bericht geschrieben wird. Diese Datei gehört ins Repository.
REPORT_PATH = Path("eval/baseline.txt")
# Muss mit server.py übereinstimmen, sonst wird ein anderer Index gemessen.
VECTORSTORE_PATH = "./vectorstore_docling_hdr"
# Für diese k-Werte wird Recall@k berechnet.
K_VALUES = [5, 10, 20]
# Den Zusatztext aus local_rag.QUERY_ADDITIONAL_INSTRUCTIONS an jede Frage anhängen?
# Einmal mit True und einmal mit False laufen lassen zeigt, ob er tatsächlich hilft.
USE_QUERY_INSTRUCTIONS = False
# PyPDFLoader zählt Seiten ab 0, Menschen ab 1. Bei 1 bedeutet Seite 42 in der
# CSV also die Seite, die im PDF-Betrachter als 42 angezeigt wird.
PAGE_OFFSET = 1
# Ein Chunk kann über einen Seitenumbruch reichen: so viele Seiten Abweichung
# gelten noch als Treffer. 0 verlangt die exakte Seite.
PAGE_TOLERANCE = 1
RETRIEVAL_SETUP = "" # wird in main() gesetzt und im Bericht vermerkt
INDEX_INFO = {} # Einstellungen, mit denen der Index gebaut wurde (index_info.json)
# -----------------------------------------------------------------------------
def read_goldset(path):
"""Liest die Referenzfragen. Erwartet die Spalten frage, korpus,
erwartete_quelle, erwartete_seite, anmerkung (die letzten beiden optional)."""
if not path.exists():
sys.exit(
f"Keine Referenzfragen gefunden: {path}\n"
"Lege die Datei an (Vorlage liegt im Repository) und trage echte Fragen ein."
)
rows = []
with path.open(encoding="utf-8-sig", newline="") as handle:
# Deutsches Excel speichert CSV oft mit Semikolon statt Komma.
first_line = handle.readline()
delimiter = ";" if first_line.count(";") > first_line.count(",") else ","
handle.seek(0)
for line_no, row in enumerate(csv.DictReader(handle, delimiter=delimiter), start=2):
question = (row.get("frage") or "").strip()
source = (row.get("erwartete_quelle") or "").strip()
if not question or not source:
print(f" Zeile {line_no} übersprungen: frage oder erwartete_quelle fehlt")
continue
page_raw = (row.get("erwartete_seite") or "").strip()
page = None
if page_raw:
try:
page = int(page_raw)
except ValueError:
print(f" Zeile {line_no}: '{page_raw}' ist keine Seitenzahl, wird ignoriert")
rows.append({
"question": question,
"corpus": (row.get("korpus") or "ohne").strip() or "ohne",
"source": source,
"page": page,
})
if not rows:
sys.exit(f"{path} enthält keine verwertbaren Zeilen.")
return rows
def same_file(found, expected):
"""Gleiche Datei? Groß-/Kleinschreibung, Unicode-Form (macOS speichert Umlaute zerlegt)
und Unterstriche, Punkte oder Leerzeichen am Ende des Namens spielen keine Rolle."""
def norm(name):
stem = unicodedata.normalize("NFC", Path(name).stem.lower())
return stem.rstrip("_. ")
return norm(found) == norm(expected)
def is_hit(doc, expected_source, expected_page):
"""Passt dieser Treffer zur erwarteten Fundstelle? Dateiname ohne Pfad,
Groß-/Kleinschreibung egal; Seite nur, wenn eine angegeben wurde."""
if not same_file(doc.metadata.get("source", ""), expected_source):
return False
if expected_page is None:
return True
found_page = doc.metadata.get("page")
if found_page is None:
return False
return abs(int(found_page) - (expected_page - PAGE_OFFSET)) <= PAGE_TOLERANCE
def evaluate(rag, goldset, max_k):
"""Stellt jede Frage und merkt sich den Rang des ersten korrekten Treffers."""
results = []
for i, item in enumerate(goldset, start=1):
query = item["question"]
if USE_QUERY_INSTRUCTIONS:
query = f"{query}\n\n{QUERY_ADDITIONAL_INSTRUCTIONS}"
# Dieselbe Suche wie im Chat: Vektorsuche, ggf. Stichwortsuche und Reranking
docs = [doc for doc, _ in rag.retrieve(query, k=max_k)]
rank = None
for position, doc in enumerate(docs, start=1):
if is_hit(doc, item["source"], item["page"]):
rank = position
break
results.append({**item, "rank": rank})
marker = f"Rang {rank}" if rank else "nicht gefunden"
print(f" [{i}/{len(goldset)}] {marker:<15} {item['question'][:60]}")
return results
def recall_at(results, k):
"""Anteil der Fragen, deren Fundstelle unter den ersten k Treffern liegt."""
if not results:
return 0.0
found = sum(1 for r in results if r["rank"] is not None and r["rank"] <= k)
return found / len(results)
def mrr(results):
"""Mean Reciprocal Rank: belohnt Treffer weit oben. 1.0 = immer Platz 1."""
if not results:
return 0.0
return sum(1 / r["rank"] for r in results if r["rank"] is not None) / len(results)
def build_report(results, embedding_model):
"""Baut den Berichtstext, der auf dem Bildschirm und in der Datei landet."""
line = "=" * 68
out = [
line,
f"Retrieval-Auswertung — {datetime.now():%Y-%m-%d %H:%M}",
line,
"",
"Konfiguration",
f" Vektorspeicher {VECTORSTORE_PATH}",
f" Embedding-Modell {embedding_model}",
f" Chunk-Größe {INDEX_INFO.get('chunk_size', CHUNK_SIZE)} (Overlap {INDEX_INFO.get('chunk_overlap', CHUNK_OVERLAP)})",
f" Loader {INDEX_INFO.get('pdf_loader', '?')}, Kopfzeilen {'an' if INDEX_INFO.get('chunk_headers') else 'aus'}",
f" Query-Zusatztext {'an' if USE_QUERY_INSTRUCTIONS else 'aus'}",
f" Suche {RETRIEVAL_SETUP}",
f" Seitentoleranz ±{PAGE_TOLERANCE}",
f" Fragen {len(results)}",
"",
"Ergebnis",
]
for k in K_VALUES:
value = recall_at(results, k)
found = sum(1 for r in results if r["rank"] is not None and r["rank"] <= k)
out.append(f" Recall@{k:<12} {value:.2f} ({found} von {len(results)})")
out.append(f" MRR{'':<16} {mrr(results):.2f}")
corpora = sorted({r["corpus"] for r in results})
if len(corpora) > 1:
out += ["", "Nach Korpus"]
smallest_k = min(K_VALUES)
for corpus in corpora:
subset = [r for r in results if r["corpus"] == corpus]
wort = "Frage" if len(subset) == 1 else "Fragen"
out.append(
f" {corpus:<12} Recall@{smallest_k} {recall_at(subset, smallest_k):.2f}"
f" MRR {mrr(subset):.2f} ({len(subset)} {wort})"
)
out += ["", "Einzelne Fragen (Rang = Position des ersten korrekten Treffers)"]
for r in sorted(results, key=lambda r: (r["rank"] is None, r["rank"] or 0)):
rank = f"{r['rank']:>3}" if r["rank"] is not None else " –"
out.append(f" Rang {rank} {r['corpus']:<10} {r['question']}")
nowhere = [r for r in results if r["rank"] is None]
if nowhere:
out += [
"",
f"{len(nowhere)} Frage(n) ohne Treffer unter den ersten {max(K_VALUES)}.",
"Prüfen: Steht die Fundstelle wirklich dort? Stimmt der Dateiname?",
"Ist das Dokument überhaupt im Index (rag.list_documents())?",
]
out.append("")
return "\n".join(out)
def main():
global GOLDSET_PATH, VECTORSTORE_PATH, REPORT_PATH, USE_QUERY_INSTRUCTIONS
parser = argparse.ArgumentParser(description="Retrieval-Auswertung ohne LLM")
parser.add_argument("--goldset", type=Path, default=GOLDSET_PATH)
parser.add_argument("--index", default=VECTORSTORE_PATH)
parser.add_argument("--report", type=Path, default=REPORT_PATH)
parser.add_argument("--mode", choices=["dense", "hybrid", "union"], default=None,
help="Suchmodus, Standard aus local_rag.RETRIEVAL_MODE")
parser.add_argument("--no-rerank", action="store_true", help="Cross-Encoder abschalten")
parser.add_argument("--query-instructions", action="store_true", default=USE_QUERY_INSTRUCTIONS,
help="Zusatztext aus local_rag.QUERY_ADDITIONAL_INSTRUCTIONS anhängen")
args = parser.parse_args()
GOLDSET_PATH, VECTORSTORE_PATH, REPORT_PATH = args.goldset, args.index, args.report
USE_QUERY_INSTRUCTIONS = args.query_instructions
print(f"Lese Referenzfragen aus {GOLDSET_PATH} ...")
goldset = read_goldset(GOLDSET_PATH)
print(f"{len(goldset)} Frage(n) geladen.\n")
# Lädt Embeddings und Vektorspeicher genau wie die Chat-Anwendung, damit
# gemessen wird, was dort auch tatsächlich passiert. Es wird kein LLM
# aufgerufen — nur die Ähnlichkeitssuche im Vektorspeicher.
options = {}
if args.mode:
options["retrieval_mode"] = args.mode
if args.no_rerank:
options["reranker_model"] = None
rag = LocalRAG(vectorstore_path=VECTORSTORE_PATH, **options)
if rag.vectorstore is None:
sys.exit(
f"\nKein Vektorspeicher unter {VECTORSTORE_PATH} gefunden.\n"
"Erst den Index bauen (python add_pdfs.py), dann erneut auswerten."
)
print(f"\nStelle {len(goldset)} Frage(n) an den Vektorspeicher ...")
global RETRIEVAL_SETUP, INDEX_INFO
info_file = Path(VECTORSTORE_PATH) / "index_info.json"
if info_file.exists():
import json
INDEX_INFO = json.loads(info_file.read_text(encoding="utf-8"))
RETRIEVAL_SETUP = f"{rag.retrieval_mode}, Reranker {rag.reranker_model or 'aus'}"
results = evaluate(rag, goldset, max_k=max(K_VALUES))
report = build_report(results, rag.embeddings.model_name)
print("\n" + report)
REPORT_PATH.parent.mkdir(parents=True, exist_ok=True)
REPORT_PATH.write_text(report, encoding="utf-8")
print(f"Bericht gespeichert: {REPORT_PATH}")
if __name__ == "__main__":
main()
+109
View File
@@ -0,0 +1,109 @@
"""
PDFs mit Docling extrahieren, zum Vergleich mit GROBID und PyPDFLoader.
Vier Varianten:
docling nutzt die vorhandene Textschicht des PDFs (bei Scans: deren OCR)
docling_ocr erkennt jede Seite neu per OCR (Apple Vision über ocrmac, Deutsch),
ignoriert also die mitgelieferte, oft schlechte OCR-Schicht.
Achtung: lässt bei diesen Scans ganze Absätze aus.
docling_tess wie docling_ocr, aber mit Tesseract und dem deutschen Modell
aus tessdata/deu.traineddata (tessdata_best). Verliert ebenfalls Text:
Doclings eigener OCR-Ablauf ist bei diesen Doppelseiten-Scans unzuverlässig.
docling_ocrmypdf
Arbeitsteilung: OCRmyPDF (Tesseract, deutsch, mit Geraderichten) legt
eine neue Textschicht ins PDF (extract_out/ocrmypdf/), Docling liest
diese dann ohne eigene OCR und erkennt die Struktur.
Läuft in einer eigenen Umgebung, weil Docling PyTorch und Layout-Modelle mitbringt:
conda activate docling
python extract_docling.py # Variante docling_tess, Titel aus TITLES
python extract_docling.py docling # andere Variante
Ausgabe: extract_out/<variante>/<pdf>.json (verlustfrei, für grobid_qa.py)
und .md (zum Lesen). Vorhandene Ergebnisse werden übersprungen.
"""
import os
import subprocess
import sys
import time
from pathlib import Path
from docling.datamodel.base_models import InputFormat
from docling.datamodel.pipeline_options import (
OcrMacOptions, OcrMode, PdfPipelineOptions, TesseractCliOcrOptions)
from docling.document_converter import DocumentConverter, PdfFormatOption
PDF_DIR = Path("data")
OUT_ROOT = Path("extract_out")
TESSDATA = Path("tessdata").resolve() # enthält deu.traineddata
# Docling prüft die verfügbaren Tesseract-Sprachen ohne Rücksicht auf `path`,
# deshalb das Modellverzeichnis auch über die Umgebungsvariable bekanntmachen.
os.environ["TESSDATA_PREFIX"] = str(TESSDATA)
VARIANTS = ("docling", "docling_ocr", "docling_tess", "docling_ocrmypdf")
OCRMYPDF_DIR = OUT_ROOT / "ocrmypdf" # neu erkannte PDFs für docling_ocrmypdf
# Auswahl für den Vergleich: Dateinamen beginnen mit diesen Präfixen
TITLES = [
"Beissner,", # Aufsatz, viele Fußnoten
"Fiechter,", # GROBID: alles ein Absatz
"Trost,", # sehr schlechte OCR-Schicht
"Harzer,", # GROBID: Fließtext als Abbildung
"Binder,_Hartmut_KafkaHandbuch_Band2_AText", # großes Buch
]
def converter(variant: str) -> DocumentConverter:
opts = PdfPipelineOptions()
opts.do_table_structure = True
if variant == "docling_ocr":
opts.do_ocr = True
opts.ocr_options = OcrMacOptions(mode=OcrMode.FULL_PAGE, lang=["de-DE"])
elif variant == "docling_tess":
opts.do_ocr = True
opts.ocr_options = TesseractCliOcrOptions(mode=OcrMode.FULL_PAGE, lang=["deu"], path=str(TESSDATA))
else: # docling, docling_ocrmypdf: Textschicht lesen
opts.do_ocr = False
return DocumentConverter(format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)})
def ocrmypdf(pdf: Path) -> Path:
"""Legt mit OCRmyPDF eine neue deutsche Textschicht an (überspringt Vorhandenes)."""
OCRMYPDF_DIR.mkdir(parents=True, exist_ok=True)
target = OCRMYPDF_DIR / pdf.name
if not target.exists():
subprocess.run(
[str(Path(sys.executable).parent / "ocrmypdf"), "--force-ocr", "-l", "deu", "--deskew",
"--output-type", "pdf", "--jobs", "4", "--quiet", str(pdf), str(target)],
check=True)
return target
def main():
variant = sys.argv[1] if len(sys.argv) > 1 else "docling_tess"
if variant not in VARIANTS:
sys.exit(f"Variante muss eine von {', '.join(VARIANTS)} sein")
out_dir = OUT_ROOT / variant
out_dir.mkdir(parents=True, exist_ok=True)
pdfs = [p for p in sorted(PDF_DIR.glob("*.pdf")) if any(p.name.startswith(t) for t in TITLES)]
conv = converter(variant)
for i, pdf in enumerate(pdfs, 1):
target = out_dir / f"{pdf.stem}.json"
if target.exists():
print(f"[{i}/{len(pdfs)}] {pdf.name}: vorhanden, übersprungen", flush=True)
continue
print(f"[{i}/{len(pdfs)}] {pdf.name} ...", end=" ", flush=True)
t0 = time.time()
try:
source = ocrmypdf(pdf) if variant == "docling_ocrmypdf" else pdf
doc = conv.convert(source).document
except Exception as e: # ein kaputtes PDF soll den Lauf nicht abbrechen
print(f"FEHLER: {e}", flush=True)
continue
doc.save_as_json(target)
doc.save_as_markdown(out_dir / f"{pdf.stem}.md")
print(f"fertig ({time.time() - t0:.0f} s)", flush=True)
if __name__ == "__main__":
main()
+777
View File
@@ -0,0 +1,777 @@
"""
Qualitätsprüfung der GROBID-Ausgabe, ohne Goldstandard.
Vergleicht für jedes PDF den GROBID-Output (TEI-XML) mit dem Rohtext, den pypdf
aus derselben Datei liest, und sucht gezielt nach den Fehlern, die für RAG
schaden:
- Textverlust: Passagen, die im PDF stehen, im TEI aber nirgends auftauchen.
Verlorene Passagen werden sortiert: Vermerke/Metadaten, Tabellen,
Literaturangaben und pypdf-Lesefehler sind unkritisch und zählen
nicht in die Ampel — nur verlorener Fließtext.
- Verunreinigung: Lizenz-/Download-Vermerke, Kolumnentitel, Literaturangaben und
Fragmente im Fließtext
- Doppelte Absätze
- Fehlklassierung: Fließtext, der als Abbildung/Tabelle/Notiz einsortiert wurde
- Absatzerkennung: Fließtext in Riesenabsätzen (ganze Seiten oder Kapitel am Stück)
- Zerrissene Sätze, Trennstrich-Reste, Kodierungsfehler, OCR-Fehler,
Fußnotenzeichen im Text (am Wort klebend oder abgesetzt)
- Lesereihenfolge: stimmt die Absatzfolge mit der Reihenfolge im PDF überein?
- Fehlender Textlayer (Scans ohne OCR — dann kann GROBID nichts extrahieren)
Die Ampel-Schwellen unten sind Startwerte, kalibriert an drei englischsprachigen
Zeitschriftenartikeln und dem Kafka-Korpus (deutsche Monografien, OCR-Scans).
Wo pypdf selbst keinen brauchbaren Text liefert (Sperrsatz, verschluckte
Leerzeichen), werden die Seiten aus dem Abgleich ausgenommen; ist das bei zu
vielen Seiten der Fall, bleibt die Verlustmessung leer statt falsch.
Aufruf (in der Projektumgebung, weil pypdf gebraucht wird):
conda activate local_rag
python grobid_qa.py # PDFs aus data_test/
python grobid_qa.py data # PDFs aus einem anderen Ordner
Ausgabe: Übersicht im Terminal, grobid_out/<ordner>/qa/summary.csv (eine Zeile
pro PDF, für Vergleiche über die Zeit) und grobid_out/<ordner>/qa/<pdf>.md mit
Belegstellen. Bereits vorhandenes TEI wird wiederverwendet.
"""
import csv
import random
import re
import sys
import time
import unicodedata
import xml.etree.ElementTree as ET
from collections import Counter, defaultdict
from dataclasses import dataclass
from pathlib import Path
from pypdf import PdfReader
from grobid_test import GROBID_URL, process_pdf
# ---------- Einstellungen ----------
PDF_DIR = Path("data_test") # oder als Argument: python grobid_qa.py data
OUT_ROOT = Path("grobid_out") # TEI landet in grobid_out/<pdf-ordner>/, Berichte in .../qa/
REPROCESS = False # True: TEI immer neu von GROBID holen, auch wenn vorhanden
MAX_EXAMPLES = 8 # Belegstellen pro Kategorie im Bericht
SAMPLE_PARAGRAPHS = 5 # zufällige Fließtextabsätze zum Gegenlesen
SHINGLE = 5 # Wortfolgen dieser Länge dienen zum Abgleich PDF <-> TEI
MIN_LOST_RUN = 20 # ab so vielen Wörtern am Stück gilt eine Passage als verloren
GIANT_PARA = 3000 # Absätze über so vielen Zeichen gelten als nicht segmentiert
MAX_UNUSABLE_REF = 30 # ist pypdf auf mehr als so viel % der Seiten unlesbar, keine Verlustmessung
# Ampel: (gelb ab, rot ab). Bei "order" und "body_share" ist kleiner schlechter.
THRESHOLDS = {
"scan_pages_pct": (1, 10), # Seiten ohne nennenswerten Textlayer, %
"lost_pct": (2, 5), # Fließtext-Wörter des PDFs, die im TEI fehlen, %
"contam_per_100k": (2, 8), # Fremdtext-Funde je 100.000 Zeichen Fließtext
"duplicate_pct": (0.5, 2), # doppelt vorkommende Fließtextabsätze, %
"misfiled_pct": (1, 5), # Fließtext, der in Abbildungen/Notizen steckt, %
"giant_para_pct": (10, 30), # Fließtext in Riesenabsätzen (> GIANT_PARA Zeichen), %
"broken_per_100p": (2, 6), # zerrissene Sätze je 100 Absätze
"hyphen_per_10k": (2, 10), # Trennstrich-Reste je 10.000 Wörter
"encoding_per_10k": (0.2, 3), # Kodierungsfehler je 10.000 Zeichen
"fnmark_per_10k": (2, 10), # Fußnotenzeichen im Fließtext je 10.000 Wörter
"ocr_per_10k": (5, 20), # OCR-Fehlerbilder je 10.000 Wörter
"order": (0.97, 0.90), # Anteil korrekt geordneter Absatzpaare
}
LOWER_IS_WORSE = {"order"}
# Zwei getrennte Urteile: "GROBID" bewertet, was GROBID aus dem PDF macht;
# "PDF" bewertet Mängel der Vorlage, die kein Extraktor beheben kann (Scans ohne
# Textlayer, falsch kodierte Schriften, schlechte OCR). Kodierungs- und OCR-Fehler
# zählen nur dann zum PDF, wenn sie schon im pypdf-Rohtext stehen — sonst hat
# GROBID sie verursacht.
PDF_METRICS = {"scan_pages_pct"}
# ---------- TEI lesen ----------
NS = {"t": "http://www.tei-c.org/ns/1.0"}
T = "{http://www.tei-c.org/ns/1.0}"
@dataclass
class Segment:
kind: str # body, head, header, footnote, body_note, figure, table, back, reference
text: str
page: int | None
def text_of(el) -> str:
return " ".join("".join(el.itertext()).split())
def first_page(el) -> int | None:
coords = el.get("coords")
if not coords:
return None
try:
return int(coords.split(",")[0])
except ValueError:
return None
def read_tei(path: Path) -> tuple[list[Segment], dict]:
root = ET.parse(path).getroot()
segs: list[Segment] = []
info = {}
title = root.find(".//t:titleStmt/t:title", NS)
info["title"] = text_of(title) if title is not None else ""
info["authors"] = len(root.findall(".//t:sourceDesc//t:author/t:persName", NS))
for el in root.findall(".//t:teiHeader//t:title", NS):
segs.append(Segment("header", text_of(el), None))
for el in root.findall(".//t:profileDesc/t:abstract//t:p", NS):
segs.append(Segment("header", text_of(el), first_page(el)))
for el in root.findall(".//t:profileDesc/t:textClass//t:term", NS):
segs.append(Segment("header", text_of(el), None))
abstract = root.find(".//t:profileDesc/t:abstract", NS)
info["abstract_chars"] = len(text_of(abstract)) if abstract is not None else 0
body = root.find(".//t:text/t:body", NS)
info["footnote_refs"] = 0
if body is not None:
info["footnote_refs"] = len(body.findall(".//t:ref[@type='foot']", NS))
# Reihenfolge des Dokuments beibehalten
for el in body.iter():
tag = el.tag.replace(T, "")
if tag == "p" and not _inside(el, body, ("figure", "note")):
segs.append(Segment("body", text_of(el), first_page(el)))
elif tag == "head" and not _inside(el, body, ("figure",)):
segs.append(Segment("head", text_of(el), first_page(el)))
elif tag == "figure":
kind = "table" if el.get("type") == "table" else "figure"
segs.append(Segment(kind, text_of(el), first_page(el)))
elif tag == "note" and el.get("place") != "foot" and not _inside(el, body, ("figure",)):
segs.append(Segment("body_note", text_of(el), first_page(el)))
for el in root.findall(".//t:note[@place='foot']", NS):
segs.append(Segment("footnote", text_of(el), first_page(el)))
back = root.find(".//t:text/t:back", NS)
refs = []
if back is not None:
for div in back.findall("t:div", NS):
if div.get("type") == "references":
continue
for p in div.iter():
if p.tag in (T + "p", T + "head"):
segs.append(Segment("back", text_of(p), first_page(p)))
refs = back.findall(".//t:listBibl/t:biblStruct", NS)
for b in refs:
segs.append(Segment("reference", text_of(b), first_page(b)))
complete = 0
for b in refs:
has_title = any(text_of(t) for t in b.findall(".//t:title", NS))
has_author = b.find(".//t:author", NS) is not None
has_year = b.find(".//t:date", NS) is not None
complete += has_title and has_author and has_year
info["references"] = len(refs)
info["refs_complete_pct"] = round(100 * complete / len(refs), 1) if refs else None
return [s for s in segs if s.text], info
_parent_cache: dict = {}
def _inside(el, root, tags) -> bool:
"""Liegt el (innerhalb von root) unter einem der genannten Elemente?"""
parents = _parent_cache.get(id(root))
if parents is None:
parents = {c: p for p in root.iter() for c in p}
_parent_cache.clear()
_parent_cache[id(root)] = parents
p = parents.get(el)
while p is not None and p is not root:
if p.tag.replace(T, "") in tags:
return True
p = parents.get(p)
return False
# ---------- Docling-JSON und pypdf-Ausgangswert lesen ----------
# Docling-Labels -> Segmentarten der Prüfung
DOCLING_KIND = {
"title": "header", "section_header": "head", "text": "body", "paragraph": "body",
"list_item": "body", "footnote": "footnote", "caption": "figure", "formula": "figure",
"code": "figure", "reference": "reference", "document_index": "back",
"page_header": "furniture", "page_footer": "furniture",
}
def read_docling(path: Path) -> tuple[list[Segment], dict]:
"""Liest ein mit DoclingDocument.save_as_json gespeichertes Dokument in Lesereihenfolge."""
import json
doc = json.loads(path.read_text(encoding="utf-8"))
segs: list[Segment] = []
def resolve(ref: str) -> dict:
_, coll, idx = ref.split("/")
return doc[coll][int(idx)]
def page(item) -> int | None:
prov = item.get("prov") or []
return prov[0].get("page_no") if prov else None
def walk(node, layer):
for child in node.get("children", []):
item = resolve(child["$ref"])
coll = child["$ref"].split("/")[1]
if coll == "texts":
kind = DOCLING_KIND.get(item.get("label"), "body")
if layer == "furniture" or item.get("content_layer") == "furniture":
kind = "furniture"
segs.append(Segment(kind, " ".join(item.get("text", "").split()), page(item)))
elif coll == "tables":
cells = (item.get("data") or {}).get("table_cells", [])
segs.append(Segment("table", " ".join(c.get("text", "") for c in cells), page(item)))
walk(item, layer) # Gruppen, Bildunterschriften, verschachtelte Listen
walk(doc.get("body", {}), "body")
walk(doc.get("furniture", {}), "furniture")
title = next((s.text for s in segs if s.kind == "header"), "")
info = {"title": title, "authors": 0, "abstract_chars": 0, "footnote_refs": 0,
"references": sum(s.kind == "reference" for s in segs), "refs_complete_pct": None}
return [s for s in segs if s.text], info
def read_pypdf_baseline(pdf: Path) -> tuple[list[Segment], dict]:
"""Das, was der jetzige PyPDFLoader liefert: eine Einheit pro Seite, ohne Struktur."""
segs = [Segment("body", " ".join(p.split()), i + 1) for i, p in enumerate(read_pdf_pages(pdf))]
info = {"title": "", "authors": 0, "abstract_chars": 0, "footnote_refs": 0,
"references": 0, "refs_complete_pct": None}
return [s for s in segs if s.text], info
def read_docling_cleaned(path: Path) -> tuple[list[Segment], dict]:
"""Docling-JSON so, wie docling_loader.py es bereinigt an den Index gibt."""
from docling_loader import clean_blocks
kind = {"head": "head", "body": "body", "footnote": "footnote", "caption": "figure",
"table": "table", "reference": "reference"}
segs = [Segment(kind.get(b.kind, "body"), b.text, b.page) for b in clean_blocks(path)]
info = {"title": "", "authors": 0, "abstract_chars": 0, "footnote_refs": 0,
"references": sum(s.kind == "reference" for s in segs), "refs_complete_pct": None}
return segs, info
def read_segments(pdf: Path, out: Path | None, cleaned: bool = False) -> tuple[list[Segment], dict]:
"""Extraktor-Ausgabe einlesen: GROBID-TEI, Docling-JSON (roh oder wie vom Loader
bereinigt) oder (out=None) pypdf."""
if out is None:
return read_pypdf_baseline(pdf)
if out.name.endswith(".json"):
return read_docling_cleaned(out) if cleaned else read_docling(out)
return read_tei(out)
# ---------- PDF-Rohtext ----------
def read_pdf_pages(path: Path) -> list[str]:
# Getrennte Doppelseiten (split_spreads.py): pypdf ignoriert den Zuschnitt und
# läse auf jeder Hälfte die ganze Doppelseite, deshalb begrenzt über pdfium
path = Path(path)
if path.with_name(path.stem + ".pages.json").exists():
from split_spreads import bounded_page_texts
return bounded_page_texts(path)
reader = PdfReader(path)
pages = []
for page in reader.pages:
try:
pages.append(page.extract_text() or "")
except Exception:
pages.append("")
return pages
def boilerplate_lines(pages: list[str]) -> set[str]:
"""Zeilen, die oben/unten auf vielen Seiten wiederkehren (Kolumnentitel, Fußzeilen)."""
def norm(line):
return re.sub(r"\d+", "#", " ".join(line.split()).lower())
counts = Counter()
for text in pages:
lines = [l for l in text.splitlines() if l.strip()]
for line in set(lines[:3] + lines[-3:]):
counts[norm(line)] += 1
need = max(3, int(0.3 * len(pages)))
return {l for l, c in counts.items() if c >= need and len(l) > 3}
def strip_boilerplate(text: str, boiler: set[str]) -> str:
keep = []
for line in text.splitlines():
if re.sub(r"\d+", "#", " ".join(line.split()).lower()) not in boiler:
keep.append(line)
return "\n".join(keep)
# ---------- Normalisierung für den Abgleich ----------
def normalize(text: str, dehyphenate: bool = False) -> list[str]:
text = unicodedata.normalize("NFKC", text).replace("­", "")
if dehyphenate:
text = re.sub(r"(\w)-\s*\n\s*(?=[a-zäöüß])", r"\1", text)
return re.findall(r"[^\W_]+", text.lower())
def shingles(tokens: list[str]) -> list[tuple]:
return [tuple(tokens[i:i + SHINGLE]) for i in range(len(tokens) - SHINGLE + 1)]
# ---------- Prüfungen ----------
# URLs und DOIs stehen in wissenschaftlicher Prosa legitim, sie zählen nicht als Fremdtext.
BOILER_RE = re.compile(
r"downloaded (from|by)|heruntergeladen|ip address|©|\bÓ\s?\d{4}|"
r"all rights reserved|alle rechte vorbehalten|creative ?commons|distributed under|"
r"published with license|licensed under|\bissn\b|"
r"this article (is|was) published", re.I)
YEAR_RE = re.compile(r"\b(1[5-9]|20)\d{2}[a-z]?\b")
AUTHOR_RE = re.compile(r"[A-ZÄÖÜ][a-zäöüß]+,\s(?:[A-ZÄÖÜ]\.\s?)+")
TERMINAL_RE = re.compile(r"[.!?:;…\"'“”‘’»«)\]]$")
HYPHEN_RE = re.compile(r"\b([^\W\d_]{2,})- ([a-zäöüß]{2,})\b")
HYPHEN_OK = {"und", "oder", "bis", "sowie", "als", "wie", "noch", "and", "or", "to", "nor", "versus"}
FNMARK_RE = re.compile(r"\b[a-zäöüß]{3,}[.,;:]?\d{1,3}\b(?![.,]\d)")
# abgesetzte Fußnotenzeichen aus OCR: "an Felice 6 , die" oder "Schreibart 1 6 :"
FNMARK_SPACED_RE = re.compile(
r"\b([A-Za-zÄÖÜäöüß]{3,})[.,;:!?»«\"”)]? (\d{1,3})(?: \d)? (?=[,;:]|[A-ZÄÖÜ„»])")
FNMARK_SPACED_SKIP = {
"seite", "seiten", "band", "bd", "nr", "nummer", "kap", "kapitel", "abschnitt", "teil", "vers",
"zeile", "anm", "anmerkung", "heft", "abb", "tab", "tabelle", "punkt", "absatz", "satz", "page",
"vol", "chapter", "section", "figure", "fig", "table", "experiment", "study", "studie",
"hypothesis", "hypothese", "condition", "group", "gruppe", "item", "items", "grade", "grades",
"klasse", "wave", "phase", "step", "level", "model", "modell", "booklet", "segment", "aufl"}
# typische OCR-Fehlerbilder: Ziffer im Wort (erl10ben), Backslash (\Vort), Zeichen, die
# in Prosa nicht vorkommen (| ~ ^), Satzzeichensalat (-----,~.). Binnenmajuskeln (KaJkas)
# bleiben außen vor: zu viele Fehlalarme bei Namen und Werkzeugen (McNamara, CoMap).
OCR_RE = re.compile(
r"\b[a-zäöüß]+\d+[a-zäöüß]+\b|\\[A-Za-z]|[|~^]"
r"|(?![^\w\s]*(?:\.\.\.|…))(?![.…\-–—]{4,})[^\w\s]{4,}") # Auslassungspunkte zählen nicht
URL_RE = re.compile(r"https?://\S+|\b\S+\.(?:org|io|com|de|net)/\S*")
ENCODING_RE = re.compile(
"[�-ff-st\u0000-\u0008\u000b\u000c\u000e-\u001f]"
r"|Ã[\x80-\xbf]|â€|Â(?=\s|\w)|(?<=\s)[¼½¾](?=\s)|(?<=\w) ?Ó ?(?=\w)|(?<=[\s(])À(?=\s?[\d.])")
# Platzhalter, die pypdf für Glyphen ohne Unicode-Zuordnung ausgibt
PDF_GLYPH_RE = re.compile(r"/(?:C\d{1,3}|G\d{1,3}|g\d{1,3}|uni[0-9A-Fa-f]{4}|cid:?\d+)\b|\(cid:\d+\)")
def bibliography_like(t: str) -> bool:
"""Kurzer Absatz, der überwiegend aus Literaturangaben besteht.
Belegdichte Prosa ("vgl. Binder 1983, S. 170") soll nicht anschlagen."""
if len(t) > 800:
return False
years = len(YEAR_RE.findall(t))
authors = len(AUTHOR_RE.findall(t)) + len(re.findall(r"[A-ZÄÖÜ][a-zäöüß]+, [A-ZÄÖÜ][a-zäöüß]+[^:]{0,20}:", t))
return years >= 2 and authors >= 1 and years * 1000 / len(t) >= 5
def prose_like(t: str) -> bool:
"""Grob: sieht nach Fließtext aus (lang, Sätze, wenig Ziffern)."""
if len(t) < 300:
return False
digits = sum(c.isdigit() for c in t) / len(t)
sentences = len(re.findall(r"[a-zäöüß][.!?]\s+[A-ZÄÖÜ]", t))
return digits < 0.08 and sentences >= 2
META_RE = re.compile(
r"https?|orcid|doi|licen[cs]e|creative commons|copyright|correspondence|"
r"email|e mail|university|universität|institut|published|issn|isbn|"
r"received|accepted|keywords|supplement|journal|verlag|rights|contact|cite this|"
r"gmbh|research cent|department|faculty|fakultät|lehrstuhl|seminar für", re.I)
def classify_lost(tokens: list[str]) -> str:
"""Ordnet eine verlorene Passage ein; nur 'Fließtext' ist kritisch."""
n = len(tokens)
if sum(len(t) for t in tokens) / n < 2.5:
return "pypdf-Lesefehler (Sperrsatz)"
if sum(t.isdigit() or bool(re.fullmatch(r"[a-z]?\d+", t)) for t in tokens) / n > 0.25:
return "Zahlen/Tabelle"
text = " ".join(tokens)
if len(META_RE.findall(text)) >= 2:
return "Vermerk/Metadaten"
if len(YEAR_RE.findall(text)) >= 2 and re.search(r"\b(pp|ed|eds|hg|hrsg|vol|bd|s)\b", text):
return "Literaturangabe"
return "Fließtext"
def page_unusable(text: str) -> bool:
"""Liefert pypdf auf dieser Seite Unbrauchbares? (Sperrsatz, verschluckte Leerzeichen)"""
words = text.split()
if len(words) < 30:
return False
mean = sum(len(w) for w in words) / len(words)
long_share = sum(len(w) > 20 for w in words) / len(words)
return mean < 2.5 or mean > 9 or long_share > 0.08
def light(metric: str, value) -> str:
if value is None:
return "–"
yellow, red = THRESHOLDS[metric]
if metric in LOWER_IS_WORSE:
return "ROT" if value < red else "GELB" if value < yellow else "GRÜN"
return "ROT" if value >= red else "GELB" if value >= yellow else "GRÜN"
def check(pdf: Path, out: Path | None, cleaned: bool = False) -> tuple[dict, dict]:
"""Prüft die Ausgabe eines Extraktors (TEI, Docling-JSON oder None für pypdf) gegen das PDF.
cleaned=True prüft Docling-JSON nach der Bereinigung durch docling_loader.py."""
segs, info = read_segments(pdf, out, cleaned)
pages = read_pdf_pages(pdf)
boiler = boilerplate_lines(pages)
ex = defaultdict(list) # Belegstellen
m = {"datei": pdf.name, "titel": info["title"][:80], "seiten": len(pages)}
# 1. Textlayer
scan_pages = [i + 1 for i, p in enumerate(pages) if len(p.strip()) < 200]
m["scan_pages_pct"] = round(100 * len(scan_pages) / max(1, len(pages)), 1)
if scan_pages:
ex["Seiten ohne nennenswerten Textlayer (Scan? GROBID macht kein OCR)"] = [
f"Seite {', '.join(map(str, scan_pages[:40]))}"]
# 2. Abdeckung: welcher Teil des PDF-Texts findet sich wo im TEI wieder?
priority = ["body", "head", "header", "footnote", "body_note", "back", "table", "figure",
"reference", "furniture"]
tei_index: dict[tuple, str] = {}
for kind in reversed(priority): # höhere Priorität überschreibt
for s in segs:
if s.kind == kind:
for sh in shingles(normalize(s.text)):
tei_index[sh] = kind
unusable = [i + 1 for i, p in enumerate(pages) if page_unusable(p)]
m["ref_unusable_pct"] = round(100 * len(unusable) / max(1, len(pages)), 1)
if unusable:
ex["Seiten, auf denen pypdf keinen brauchbaren Vergleichstext liefert (nicht geprüft)"] = [
f"Seite {', '.join(map(str, unusable[:60]))}"]
pdf_tokens, token_page = [], []
for i, p in enumerate(pages):
if i + 1 in unusable:
continue
toks = normalize(strip_boilerplate(p, boiler), dehyphenate=True)
pdf_tokens += toks
token_page += [i + 1] * len(toks)
covered = [None] * len(pdf_tokens)
for i, sh in enumerate(shingles(pdf_tokens)):
kind = tei_index.get(sh)
if kind:
for j in range(i, i + SHINGLE):
if covered[j] is None or priority.index(kind) < priority.index(covered[j]):
covered[j] = kind
n = max(1, len(pdf_tokens))
share = Counter(c or "fehlt" for c in covered)
m["pdf_woerter"] = len(pdf_tokens)
m["missing_all_pct"] = round(100 * share["fehlt"] / n, 1)
m["body_share_pct"] = round(100 * (share["body"] + share["head"]) / n, 1)
m["verteilung"] = " ".join(f"{k}={100 * v / n:.0f}%" for k, v in share.most_common())
# verlorene Passagen am Stück
runs, start = [], None
for i, c in enumerate(covered + ["end"]):
if c is None and start is None:
start = i
elif c is not None and start is not None:
if i - start >= MIN_LOST_RUN:
runs.append((start, i))
start = None
runs.sort(key=lambda r: r[1] - r[0], reverse=True)
lost_by_class = Counter()
for a, b in runs:
cls = classify_lost(pdf_tokens[a:b])
lost_by_class[cls] += b - a
title = ("Verlorener FLIESSTEXT (kritisch)" if cls == "Fließtext"
else "Verlorene Passagen, unkritisch (Vermerke, Tabellen, Literatur, Lesefehler)")
if len(ex[title]) < MAX_EXAMPLES:
words = " ".join(pdf_tokens[a:b])
ex[title].append(f"[{cls}, S. {token_page[a]}, {b - a} Wörter] "
f"{words[:400]}{' …' if len(words) > 400 else ''}")
m["lost_pct"] = round(100 * lost_by_class["Fließtext"] / n, 2)
if m["ref_unusable_pct"] > MAX_UNUSABLE_REF or not pdf_tokens:
m["lost_pct"] = None # Referenz zu schlecht, lieber keine Aussage als eine falsche
m["lost_runs"] = len(runs)
m["verlust_nach_art"] = " ".join(f"{k}={100 * v / n:.1f}%" for k, v in lost_by_class.most_common())
# 3. Fließtext-Prüfungen
body = [s for s in segs if s.kind == "body"]
body_chars = sum(len(s.text) for s in body) or 1
body_words = sum(len(s.text.split()) for s in body) or 1
m["absaetze"] = len(body)
m["ueberschriften"] = sum(s.kind == "head" for s in segs)
m["fliesstext_zeichen"] = body_chars
# Gezählt werden Funde, nicht Absätze: bei schlechter Absatzerkennung (wenige
# Riesenabsätze) wäre ein Anteil der Absätze sonst bedeutungslos.
heads = [l for l in boiler if len(l) >= 15]
hits = []
for s in body:
norm_text = re.sub(r"\d+", "#", s.text.lower())
found = [("Vermerk", mt.start()) for mt in BOILER_RE.finditer(s.text)]
for h in heads:
pos = norm_text.find(h)
while pos >= 0:
found.append(("Kolumnentitel/Fußzeile", pos))
pos = norm_text.find(h, pos + 1)
last = -1000
for reason, pos in sorted(found, key=lambda x: x[1]):
if pos - last > 300: # ein Einschub trifft oft mehrere Muster
hits.append((reason, s, pos))
last = pos
if bibliography_like(s.text):
hits.append(("wie Literaturangabe", s, 0))
elif len(s.text) < 40:
hits.append(("Fragment", s, 0))
for reason, s, where in hits[:MAX_EXAMPLES * 3]:
a = max(0, where - 100)
ex["Fremdtext im Fließtext"].append(
f"[{reason}, S. {s.page}] {'…' if a else ''}{s.text[a:where + 200]}…")
m["contam_per_100k"] = round(100000 * len(hits) / body_chars, 1)
seen, dups = set(), 0
for s in body:
key = s.text.lower()
if len(key) > 60 and key in seen:
dups += 1
ex["Doppelte Absätze"].append(f"[S. {s.page}] {s.text[:200]}")
seen.add(key)
m["duplicate_pct"] = round(100 * dups / max(1, len(body)), 1)
# Fließtext, der als Abbildung, Tabelle oder Notiz einsortiert wurde
misfiled = 0
for s in segs:
if s.kind in ("figure", "body_note") and prose_like(s.text):
misfiled += len(s.text)
ex["Möglicher Fließtext in Abbildung/Notiz (fehlt dem Index)"].append(
f"[{s.kind}, S. {s.page}] {s.text[:300]}")
m["misfiled_chars"] = misfiled
m["misfiled_pct"] = round(100 * misfiled / (body_chars + misfiled), 1)
# Absatzerkennung: wie viel Fließtext steckt in Riesenabsätzen?
giant = [s for s in body if len(s.text) > GIANT_PARA]
m["giant_para_pct"] = round(100 * sum(len(s.text) for s in giant) / body_chars, 1)
m["laengster_absatz"] = max((len(s.text) for s in body), default=0)
for s in sorted(giant, key=lambda s: -len(s.text))[:MAX_EXAMPLES]:
ex[f"Riesenabsätze (> {GIANT_PARA} Zeichen, Absatzerkennung versagt)"].append(
f"[S. {s.page}, {len(s.text):,} Zeichen] {s.text[:200]} … {s.text[-120:]}".replace(",", "."))
# zerrissene Sätze: Absatz endet ohne Satzzeichen, nächster beginnt klein
broken = 0
for a, b in zip(body, body[1:]):
if not TERMINAL_RE.search(a.text) and b.text[:1].islower():
broken += 1
ex["Über Absatzgrenzen zerrissene Sätze"].append(
f"S. {a.page}→{b.page}: …{a.text[-120:]} ‖ {b.text[:120]}…")
m["broken_per_100p"] = round(100 * broken / max(1, len(body)), 1)
all_body = "\n".join(s.text for s in body)
hyph = [mt for mt in HYPHEN_RE.finditer(all_body) if mt.group(2) not in HYPHEN_OK]
m["hyphen_per_10k"] = round(10000 * len(hyph) / body_words, 1)
for mt in hyph[:MAX_EXAMPLES]:
ex["Trennstrich-Reste"].append(all_body[max(0, mt.start() - 40):mt.end() + 40])
enc = list(ENCODING_RE.finditer(all_body))
m["encoding_per_10k"] = round(10000 * len(enc) / body_chars, 1)
# Stecken dieselben Zeichen schon im PDF-Rohtext, liegt es an der Schriftkodierung
# des PDFs, nicht an GROBID — behebbar mit einer Ersetzungstabelle im Loader.
# pypdf zeigt dieselben Schriftmängel teils anders, als Glyphen-Platzhalter wie "/C22".
raw_text = "\n".join(pages)
raw_enc = len(ENCODING_RE.findall(raw_text)) + len(PDF_GLYPH_RE.findall(raw_text))
m["kodierung_im_pdf"] = "ja" if enc and raw_enc >= 0.5 * len(enc) else ("nein" if enc else "–")
for mt in enc[:MAX_EXAMPLES]:
ctx = all_body[max(0, mt.start() - 40):mt.end() + 40]
ex["Kodierungsfehler"].append(f"{mt.group()!r}: {ctx}")
fnm = list(FNMARK_RE.finditer(all_body))
fns = [mt for mt in FNMARK_SPACED_RE.finditer(all_body)
if mt.group(1).lower() not in FNMARK_SPACED_SKIP]
m["fnmark_per_10k"] = round(10000 * (len(fnm) + len(fns)) / body_words, 1)
for mt in fnm[:MAX_EXAMPLES // 2] + fns[:MAX_EXAMPLES // 2]:
ex["Fußnotenzeichen im Fließtext (verfälscht Suche, Fußnote nicht getrennt)"].append(
all_body[max(0, mt.start() - 40):mt.end() + 40])
ocr = list(OCR_RE.finditer(URL_RE.sub(" ", all_body)))
m["ocr_per_10k"] = round(10000 * len(ocr) / body_words, 1)
raw_ocr = len(OCR_RE.findall(URL_RE.sub(" ", raw_text)))
raw_words = len(raw_text.split()) or 1
# gleiche oder höhere Dichte im Rohtext -> die OCR-Schicht des PDFs ist schuld
m["ocr_im_pdf"] = ("ja" if ocr and raw_ocr / raw_words >= 0.5 * len(ocr) / body_words
else "nein" if ocr else "–")
for mt in ocr[:MAX_EXAMPLES]:
ctx = URL_RE.sub(" ", all_body)
ex["OCR-Fehlerbilder"].append(f"{mt.group()!r}: {ctx[max(0, mt.start() - 40):mt.end() + 40]}")
# 4. Lesereihenfolge: Position jedes Absatzes im PDF-Text
pdf_pos: dict[tuple, int] = {}
dup = set()
for i, sh in enumerate(shingles(pdf_tokens)):
if sh in pdf_pos:
dup.add(sh)
pdf_pos.setdefault(sh, i)
positions = []
for s in body:
for sh in shingles(normalize(s.text))[:30]:
if sh in pdf_pos and sh not in dup:
positions.append((pdf_pos[sh], s))
break
pairs = list(zip(positions, positions[1:]))
ordered = sum(a[0] < b[0] for a, b in pairs)
m["order"] = round(ordered / len(pairs), 3) if pairs else None
for a, b in pairs:
if a[0] > b[0] and len(ex["Absätze außerhalb der PDF-Reihenfolge"]) < MAX_EXAMPLES:
ex["Absätze außerhalb der PDF-Reihenfolge"].append(
f"S. {a[1].page} „{a[1].text[:80]}…“ steht vor S. {b[1].page} „{b[1].text[:80]}…“")
m["absaetze_ohne_pdf_fundstelle"] = len(body) - len(positions)
# 5. Struktur
m["fussnoten"] = sum(s.kind == "footnote" for s in segs)
m["fussnoten_verweise"] = info["footnote_refs"]
m["literaturangaben"] = info["references"]
m["literatur_vollstaendig_pct"] = info["refs_complete_pct"]
m["autoren"] = info["authors"]
m["abstract_zeichen"] = info["abstract_chars"]
if m["fussnoten_verweise"] and not m["fussnoten"]:
ex["Strukturhinweise"].append(
f"{m['fussnoten_verweise']} Fußnotenverweise im Text, aber keine Fußnote erkannt")
if not info["title"]:
ex["Strukturhinweise"].append("Kein Titel erkannt")
if m["ueberschriften"] == 0:
ex["Strukturhinweise"].append("Keine Zwischenüberschriften erkannt")
# Stichprobe zum Gegenlesen
rnd = random.Random(pdf.name)
for s in rnd.sample(body, min(SAMPLE_PARAGRAPHS, len(body))):
ex["Stichprobe: zufällige Fließtextabsätze zum Gegenlesen"].append(f"[S. {s.page}] {s.text}")
m["ampel"] = {k: light(k, m[k]) for k in THRESHOLDS if k in m}
m["ursache"] = {k: "PDF" if k in PDF_METRICS else "Extraktor" for k in m["ampel"]}
if m["kodierung_im_pdf"] == "ja":
m["ursache"]["encoding_per_10k"] = "PDF"
if m["ocr_im_pdf"] == "ja":
m["ursache"]["ocr_per_10k"] = "PDF"
m["grobid"] = worst(l for k, l in m["ampel"].items() if m["ursache"][k] == "Extraktor")
m["pdf"] = worst(l for k, l in m["ampel"].items() if m["ursache"][k] == "PDF")
return m, ex
def worst(lights) -> str:
lights = list(lights)
return "ROT" if "ROT" in lights else "GELB" if "GELB" in lights else "GRÜN"
# ---------- Berichte ----------
LABELS = {
"scan_pages_pct": "Seiten ohne Textlayer (%)",
"lost_pct": "Verlorener Fließtext ggü. PDF (%)",
"giant_para_pct": f"Fließtext in Riesenabsätzen > {GIANT_PARA} Zeichen (%)",
"contam_per_100k": "Fremdtext-Funde je 100k Zeichen",
"duplicate_pct": "Doppelte Absätze (%)",
"misfiled_pct": "Fließtext in Abbildung/Notiz (%)",
"broken_per_100p": "Zerrissene Sätze je 100 Absätze",
"hyphen_per_10k": "Trennstrich-Reste je 10k Wörter",
"encoding_per_10k": "Kodierungsfehler je 10k Zeichen",
"fnmark_per_10k": "Fußnotenzeichen im Fließtext je 10k Wörter",
"ocr_per_10k": "OCR-Fehlerbilder je 10k Wörter",
"order": "Lesereihenfolge (Anteil geordnet)",
}
def write_report(m: dict, ex: dict, path: Path):
out = [f"# QA: {m['datei']}", "", f"**{m['titel']}**", "",
f"Extraktor: **{m['grobid']}** · PDF-Mängel: **{m['pdf']}** · {m['seiten']} Seiten · {m['absaetze']} Absätze · "
f"{m['ueberschriften']} Überschriften · {m['fliesstext_zeichen']:,} Zeichen Fließtext".replace(",", "."),
"", "| Prüfung | Wert | Ampel | zählt zu | gelb ab | rot ab |", "|---|---|---|---|---|---|"]
for k, label in LABELS.items():
y, r = THRESHOLDS[k]
out.append(f"| {label} | {m[k]} | {m['ampel'][k]} | {m['ursache'][k]} | {y} | {r} |")
out += ["", f"Verteilung der PDF-Wörter auf TEI-Bereiche: {m['verteilung']}", "",
f"Nicht wiedergefunden insgesamt: {m['missing_all_pct']} %, davon in Passagen "
f"≥ {MIN_LOST_RUN} Wörter: {m['verlust_nach_art'] or '–'}", "",
f"Fußnoten: {m['fussnoten']} (Verweise im Text: {m['fussnoten_verweise']}) · "
f"Literaturangaben: {m['literaturangaben']} (vollständig: {m['literatur_vollstaendig_pct']} %) · "
f"Autoren: {m['autoren']} · Abstract: {m['abstract_zeichen']} Zeichen · "
f"Absätze ohne PDF-Fundstelle: {m['absaetze_ohne_pdf_fundstelle']} · "
f"Kodierungsfehler schon im PDF angelegt: {m['kodierung_im_pdf']} · "
f"OCR-Fehler schon im PDF angelegt: {m['ocr_im_pdf']} · "
f"Längster Absatz: {m['laengster_absatz']} Zeichen · "
f"Seiten ohne brauchbaren pypdf-Vergleichstext: {m['ref_unusable_pct']} %"
+ (" → Verlustmessung ausgesetzt" if m["lost_pct"] is None else ""), ""]
for title, items in ex.items():
out += [f"## {title}", ""]
out += [f"- {i}" for i in items[:MAX_EXAMPLES if 'Stichprobe' not in title else None]]
out.append("")
path.write_text("\n".join(out), encoding="utf-8")
def tei_is_current(tei: Path) -> bool:
"""TEI vorhanden und mit den Optionen aus grobid_test.py erzeugt (Koordinaten, Rohzitate)?"""
if not tei.exists():
return False
data = tei.read_bytes()
return re.search(rb"<p [^>]*coords=", data) is not None and (
b"raw_reference" in data or b"<listBibl/>" in data or b"listBibl" not in data)
def main():
pdf_dir = Path(sys.argv[1]) if len(sys.argv) > 1 else PDF_DIR
tei_dir = OUT_ROOT / pdf_dir.name
report_dir = tei_dir / "qa"
report_dir.mkdir(parents=True, exist_ok=True)
pdfs = sorted(pdf_dir.glob("*.pdf"))
if not pdfs:
raise SystemExit(f"Keine PDFs in {pdf_dir}/")
rows, failed = [], []
for i, pdf in enumerate(pdfs, 1):
tei = tei_dir / f"{pdf.stem}.tei.xml"
t0 = time.time()
try:
if REPROCESS or not tei_is_current(tei):
print(f"[{i}/{len(pdfs)}] {pdf.name}: GROBID ...", end=" ", flush=True)
tei.write_bytes(process_pdf(pdf))
else:
print(f"[{i}/{len(pdfs)}] {pdf.name}: vorhandenes TEI ...", end=" ", flush=True)
m, ex = check(pdf, tei)
except Exception as e: # ein kaputtes PDF soll den Lauf nicht abbrechen
print(f"FEHLER: {e}", flush=True)
failed.append((pdf.name, str(e)))
continue
write_report(m, ex, report_dir / f"{pdf.stem}.md")
rows.append(m)
print(f"GROBID {m['grobid']}, PDF {m['pdf']} ({time.time() - t0:.0f} s)", flush=True)
if not rows:
raise SystemExit("Kein PDF erfolgreich geprüft.")
cols = ["datei", "grobid", "pdf", "seiten", *LABELS, "body_share_pct", "absaetze", "ueberschriften",
"fussnoten", "fussnoten_verweise", "literaturangaben", "literatur_vollstaendig_pct",
"absaetze_ohne_pdf_fundstelle", "kodierung_im_pdf", "ocr_im_pdf", "laengster_absatz",
"ref_unusable_pct", "misfiled_chars", "missing_all_pct", "verlust_nach_art", "lost_runs", "titel"]
with open(report_dir / "summary.csv", "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=cols, extrasaction="ignore", delimiter=";")
w.writeheader()
w.writerows(rows)
w.writerows({"datei": name, "grobid": "FEHLER", "titel": err[:200]} for name, err in failed)
short = {"scan_pages_pct": "Scan%", "lost_pct": "Verlust%", "giant_para_pct": "Riesen%",
"contam_per_100k": "Fremd", "misfiled_pct": "Fehlabl%", "duplicate_pct": "Doppelt",
"broken_per_100p": "Zerriss", "hyphen_per_10k": "Trenn", "encoding_per_10k": "Kodier",
"ocr_per_10k": "OCR", "fnmark_per_10k": "FN-Zch", "order": "Reihenf"}
name_w = min(40, max(len(r["datei"]) for r in rows))
print("\n" + "Datei".ljust(name_w), "GROBID".ljust(6), "PDF".ljust(5), *(v.rjust(8) for v in short.values()))
for r in rows:
cells = [f"{'P:' if r['ursache'][k] == 'PDF' and r['ampel'][k] != 'GRÜN' else ''}{r[k]}"
f"{'!' if r['ampel'][k] == 'ROT' else '?' if r['ampel'][k] == 'GELB' else ''}"
for k in short]
print(r["datei"][:name_w].ljust(name_w), r["grobid"].ljust(6), r["pdf"].ljust(5),
*(c.rjust(8) for c in cells))
print("\n? = gelb, ! = rot, P: = Mangel der PDF-Vorlage, nicht von GROBID.")
for name, err in failed:
print(f"FEHLER bei {name}: {err}")
print("Details und Belegstellen:", report_dir)
if __name__ == "__main__":
main()
+121
View File
@@ -0,0 +1,121 @@
"""
Testlauf: PDFs aus data_test/ durch GROBID schicken und das Ergebnis begutachten.
Voraussetzung: GROBID-Container läuft (z. B. auf http://localhost:8070).
Schreibt pro PDF eine TEI-XML-Datei und eine lesbare Textfassung nach grobid_out/
und gibt eine kurze Übersicht aus, was GROBID als Fließtext, Fußnote und
Literaturangabe erkannt hat.
Nur Standardbibliothek, kein zusätzliches Paket nötig.
"""
import sys
import uuid
import urllib.request
import xml.etree.ElementTree as ET
from pathlib import Path
GROBID_URL = "http://localhost:8070"
INPUT_DIR = Path("data_test")
OUTPUT_DIR = Path("grobid_out") / INPUT_DIR.name
TEI = {"tei": "http://www.tei-c.org/ns/1.0"}
def process_pdf(pdf_path: Path) -> bytes:
"""Schickt ein PDF an processFulltextDocument und liefert TEI-XML zurück."""
boundary = uuid.uuid4().hex
fields = [
("consolidateHeader", "0"), # keine Online-Abfrage (CrossRef) für Metadaten
("consolidateCitations", "0"),
("segmentSentences", "0"),
("includeRawCitations", "1"), # Literaturangaben zusätzlich im Originalwortlaut
]
# Seitenkoordinaten an diese Elemente hängen (Format "seite,x,y,b,h;..."),
# damit sich jedem Absatz eine Seitenzahl zuordnen lässt.
fields += [("teiCoordinates", el) for el in ("head", "p", "note", "figure", "biblStruct")]
body = b""
for name, value in fields:
body += (f"--{boundary}\r\nContent-Disposition: form-data; name=\"{name}\"\r\n\r\n"
f"{value}\r\n").encode()
body += (f"--{boundary}\r\nContent-Disposition: form-data; name=\"input\"; "
f"filename=\"{pdf_path.name}\"\r\nContent-Type: application/pdf\r\n\r\n").encode()
body += pdf_path.read_bytes() + f"\r\n--{boundary}--\r\n".encode()
req = urllib.request.Request(
f"{GROBID_URL}/api/processFulltextDocument",
data=body,
headers={"Content-Type": f"multipart/form-data; boundary={boundary}"},
)
# ganze Monografien brauchen deutlich länger als Zeitschriftenartikel
with urllib.request.urlopen(req, timeout=1800) as resp:
return resp.read()
def text_of(el) -> str:
return " ".join("".join(el.itertext()).split())
def summarize(tei_bytes: bytes) -> tuple[dict, str]:
"""Zählt die erkannten Elemente und baut eine lesbare Textfassung."""
root = ET.fromstring(tei_bytes)
title = root.find(".//tei:titleStmt/tei:title", TEI)
authors = list(dict.fromkeys( # GROBID listet Autoren gelegentlich doppelt
" ".join(s.strip() for s in p.itertext() if s.strip())
for p in root.findall(".//tei:sourceDesc//tei:author/tei:persName", TEI)))
abstract = root.find(".//tei:profileDesc/tei:abstract", TEI)
body = root.find(".//tei:text/tei:body", TEI)
divs = body.findall("tei:div", TEI) if body is not None else []
paras = body.findall(".//tei:p", TEI) if body is not None else []
notes = root.findall(".//tei:note[@place='foot']", TEI)
refs = root.findall(".//tei:back//tei:listBibl/tei:biblStruct", TEI)
stats = {
"titel": text_of(title) if title is not None else "",
"autoren": ", ".join(authors),
"abschnitte": len(divs),
"absaetze": len(paras),
"zeichen_fliesstext": sum(len(text_of(p)) for p in paras),
"fussnoten": len(notes),
"literaturangaben": len(refs),
}
out = [f"# {stats['titel']}", f"Autoren: {stats['autoren']}", ""]
if abstract is not None and text_of(abstract):
out += ["## Abstract", text_of(abstract), ""]
for div in divs:
head = div.find("tei:head", TEI)
if head is not None:
out += [f"## {text_of(head)}"]
for p in div.findall("tei:p", TEI):
out += [text_of(p), ""]
out += ["", "=" * 70, f"## Fußnoten ({len(notes)})"]
for n in notes:
out += [f"[{n.get('n', '?')}] {text_of(n)}"]
out += ["", "=" * 70, f"## Literaturverzeichnis ({len(refs)})"]
for r in refs:
out += [f"- {text_of(r)}"]
return stats, "\n".join(out)
def main():
try:
urllib.request.urlopen(f"{GROBID_URL}/api/isalive", timeout=5)
except Exception as e:
sys.exit(f"GROBID unter {GROBID_URL} nicht erreichbar: {e}")
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
for pdf in sorted(INPUT_DIR.glob("*.pdf")):
print(f"\n{pdf.name} ...", flush=True)
tei = process_pdf(pdf)
(OUTPUT_DIR / f"{pdf.stem}.tei.xml").write_bytes(tei)
stats, text = summarize(tei)
(OUTPUT_DIR / f"{pdf.stem}.txt").write_text(text, encoding="utf-8")
for k, v in stats.items():
print(f" {k:<20} {v}")
print(f"\nErgebnisse in {OUTPUT_DIR}/")
if __name__ == "__main__":
main()
+179 -14
View File
@@ -2,9 +2,13 @@
Local RAG setup with LangChain, Ollama/OpenAI, and FAISS Local RAG setup with LangChain, Ollama/OpenAI, and FAISS
Minimal dependencies, simple code Minimal dependencies, simple code
""" """
import json
import os import os
import re
from pathlib import Path from pathlib import Path
import numpy as np
from langchain_community.document_loaders import PyPDFLoader, TextLoader from langchain_community.document_loaders import PyPDFLoader, TextLoader
from langchain_community.vectorstores import FAISS from langchain_community.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings from langchain_huggingface import HuggingFaceEmbeddings
@@ -12,17 +16,52 @@ from langchain_ollama import ChatOllama
from langchain_openai import ChatOpenAI from langchain_openai import ChatOpenAI
from langchain_text_splitters import RecursiveCharacterTextSplitter from langchain_text_splitters import RecursiveCharacterTextSplitter
import docling_loader
# Pipeline tuning: used when adding documents (rebuild index after changing). # Pipeline tuning: used when adding documents (rebuild index after changing).
CHUNK_SIZE = 2000 # Characters, not tokens: 1500 characters of German prose are roughly 400 tokens.
CHUNK_OVERLAP = 400 CHUNK_SIZE = 1500
CHUNK_OVERLAP = 300
# Multilingual embedding model with a long input window (8192 tokens), so chunks
# are never truncated. Changing it requires rebuilding the index.
EMBEDDING_MODEL = "BAAI/bge-m3"
# How PDFs are read: "pypdf" (PyPDFLoader, one unit per page) or "docling"
# (structured output from prepare_corpus.py, cleaned by docling_loader.py).
PDF_LOADER = "pypdf"
# Where prepare_corpus.py put the Docling output for the PDFs being indexed.
PROCESSED_DIR = "corpus_processed/data_split"
# Prefix every chunk with author and title from the file name (Docling: plus section),
# so questions like "according to Lehmann ..." can match. See docling_loader.chunk_header.
CHUNK_HEADERS = True
# Retrieval / query tuning # Retrieval / query tuning
RETRIEVAL_K = 5 RETRIEVAL_K = 5
# Hybrid search: besides the vector search, a keyword search (BM25 on German word stems)
# finds names, titles and quotations that embeddings blur.
# "dense" vector search alone
# "hybrid" both lists merged with Reciprocal Rank Fusion, which also sets the order
# "union" BM25 only adds candidates (BM25_CANDIDATES) to the vector candidates;
# the order comes from the vector search or, if enabled, from the reranker
RETRIEVAL_MODE = "hybrid"
CANDIDATES_K = 50 # candidates per search before merging and reranking
BM25_CANDIDATES = 20 # extra keyword candidates in "union" mode
RRF_K = 60 # standard constant of Reciprocal Rank Fusion
# Cross-encoder that rereads question and candidate together and re-sorts the candidates.
# None switches reranking off.
RERANKER_MODEL = "BAAI/bge-reranker-v2-m3"
# Appended to the embedding query for FAISS (does not affect the answer-generation prompt). # Appended to the embedding query for FAISS (does not affect the answer-generation prompt).
QUERY_ADDITIONAL_INSTRUCTIONS = ( # Left empty on purpose: embedding models do not follow instructions, and on the Kafka
"Do not return a list of references but prioritize meaningful text from abstracts, results and discussion sections." # corpus the former text ("Do not return a list of references ...") lowered Recall@5
) # from 0.67 to 0.53 (eval_retrieval.py --query-instructions). Measure before re-enabling.
QUERY_ADDITIONAL_INSTRUCTIONS = ""
# Written next to every index; an index built with another embedding model is not loaded.
INDEX_INFO_FILE = "index_info.json"
# Opening instructions for the final answer LLM call only. The rest (history, context, question) # Opening instructions for the final answer LLM call only. The rest (history, context, question)
# is assembled in query_with_history. # is assembled in query_with_history.
@@ -38,23 +77,41 @@ class LocalRAG:
ollama_model="gpt-oss:20b", ollama_model="gpt-oss:20b",
openai_model="gpt-5-mini", openai_model="gpt-5-mini",
ollama_base_url="http://localhost:11434", ollama_base_url="http://localhost:11434",
pdf_loader=PDF_LOADER,
processed_dir=PROCESSED_DIR,
retrieval_mode=RETRIEVAL_MODE,
reranker_model=RERANKER_MODEL,
chunk_size=CHUNK_SIZE,
chunk_overlap=CHUNK_OVERLAP,
chunk_headers=CHUNK_HEADERS,
): ):
"""Initialize local RAG system. llm_provider: 'ollama' or 'openai'.""" """Initialize local RAG system. llm_provider: 'ollama' or 'openai'.
pdf_loader: 'pypdf' or 'docling' (see PDF_LOADER)."""
self.vectorstore_path = vectorstore_path self.vectorstore_path = vectorstore_path
self.llm_provider = llm_provider self.llm_provider = llm_provider
self.pdf_loader = pdf_loader
self.processed_dir = Path(processed_dir)
self.chunk_size, self.chunk_overlap, self.chunk_headers = chunk_size, chunk_overlap, chunk_headers
self.retrieval_mode = retrieval_mode
self.reranker_model = reranker_model
self._bm25 = None # built on first use from the documents in the index
self._reranker = None # loaded on first use
# Embeddings # Embeddings
print("Loading embeddings model...") print("Loading embeddings model...")
# Normalized vectors: FAISS's L2 distance then ranks exactly like cosine similarity.
self.embeddings = HuggingFaceEmbeddings( self.embeddings = HuggingFaceEmbeddings(
model_name="sentence-transformers/all-MiniLM-L6-v2" model_name=EMBEDDING_MODEL,
encode_kwargs={"normalize_embeddings": True},
query_encode_kwargs={"normalize_embeddings": True},
) )
# Text splitter (used when adding documents; tune CHUNK_SIZE / CHUNK_OVERLAP above) # Text splitter (used when adding documents; tune CHUNK_SIZE / CHUNK_OVERLAP above)
self.text_splitter = RecursiveCharacterTextSplitter( self.text_splitter = RecursiveCharacterTextSplitter(
chunk_size=CHUNK_SIZE, chunk_size=chunk_size,
chunk_overlap=CHUNK_OVERLAP, chunk_overlap=chunk_overlap,
) )
print(f"Text splitter: chunk_size={CHUNK_SIZE}, chunk_overlap={CHUNK_OVERLAP}") print(f"Text splitter: chunk_size={chunk_size}, chunk_overlap={chunk_overlap}")
# LLM (Ollama or OpenAI) # LLM (Ollama or OpenAI)
if llm_provider == "openai": if llm_provider == "openai":
@@ -76,10 +133,41 @@ class LocalRAG:
self.vectorstore = None self.vectorstore = None
self._load_vectorstore() self._load_vectorstore()
def _index_info(self):
"""Settings that must match between the index on disk and this code."""
return {
"embedding_model": EMBEDDING_MODEL,
"chunk_size": self.chunk_size,
"chunk_overlap": self.chunk_overlap,
"pdf_loader": self.pdf_loader,
"chunk_headers": self.chunk_headers,
}
def _index_matches(self, index_file):
"""False if the index was built with a different embedding model than EMBEDDING_MODEL."""
info_file = os.path.join(self.vectorstore_path, INDEX_INFO_FILE)
if os.path.exists(info_file):
with open(info_file, encoding="utf-8") as f:
built_with = json.load(f).get("embedding_model")
if built_with != EMBEDDING_MODEL:
print(f"Index in {self.vectorstore_path} was built with {built_with}, "
f"but EMBEDDING_MODEL is {EMBEDDING_MODEL}. Rebuild it (add_pdfs.py).")
return False
return True
# Older index without info file: at least the vector dimensions must agree
import faiss
index_dim = faiss.read_index(index_file).d
model_dim = len(self.embeddings.embed_query("dimension check"))
if index_dim != model_dim:
print(f"Index in {self.vectorstore_path} has {index_dim} dimensions, {EMBEDDING_MODEL} "
f"produces {model_dim}. It was built with another model; rebuild it (add_pdfs.py).")
return False
return True
def _load_vectorstore(self): def _load_vectorstore(self):
"""Load existing vector store if available""" """Load existing vector store if available"""
index_file = os.path.join(self.vectorstore_path, "index.faiss") index_file = os.path.join(self.vectorstore_path, "index.faiss")
if os.path.exists(index_file): if os.path.exists(index_file) and self._index_matches(index_file):
try: try:
self.vectorstore = FAISS.load_local( self.vectorstore = FAISS.load_local(
self.vectorstore_path, self.vectorstore_path,
@@ -102,6 +190,17 @@ class LocalRAG:
print(f"Warning: {file_path} not found, skipping") print(f"Warning: {file_path} not found, skipping")
continue continue
# Docling: already structured and cleaned, the loader returns finished chunks
if path.suffix.lower() == '.pdf' and self.pdf_loader == "docling":
processed = self.processed_dir / f"{path.stem}.json"
if processed.exists():
chunks = docling_loader.load(processed, str(path), self.text_splitter,
header=self.chunk_headers)
all_docs.extend(chunks)
print(f" - {path.name}: {len(chunks)} chunks (docling)")
continue
print(f"Warning: no Docling output for {path.name} in {self.processed_dir}, using PyPDFLoader")
# Load document # Load document
if path.suffix.lower() == '.pdf': if path.suffix.lower() == '.pdf':
loader = PyPDFLoader(str(path)) loader = PyPDFLoader(str(path))
@@ -113,6 +212,10 @@ class LocalRAG:
docs = loader.load() docs = loader.load()
chunks = self.text_splitter.split_documents(docs) chunks = self.text_splitter.split_documents(docs)
if self.chunk_headers:
header = docling_loader.chunk_header(str(path))
for chunk in chunks:
chunk.page_content = header + chunk.page_content
all_docs.extend(chunks) all_docs.extend(chunks)
print(f" - {path.name}: {len(chunks)} chunks") print(f" - {path.name}: {len(chunks)} chunks")
@@ -131,6 +234,8 @@ class LocalRAG:
# Save # Save
os.makedirs(self.vectorstore_path, exist_ok=True) os.makedirs(self.vectorstore_path, exist_ok=True)
self.vectorstore.save_local(self.vectorstore_path) self.vectorstore.save_local(self.vectorstore_path)
with open(os.path.join(self.vectorstore_path, INDEX_INFO_FILE), "w", encoding="utf-8") as f:
json.dump(self._index_info(), f, indent=2)
print(f"Vector store saved to {self.vectorstore_path}") print(f"Vector store saved to {self.vectorstore_path}")
def list_documents(self): def list_documents(self):
@@ -204,6 +309,64 @@ class LocalRAG:
for doc, score in docs_with_scores for doc, score in docs_with_scores
] ]
# ---------- retrieval: vector search, keyword search, reranking ----------
_stemmer = None
@classmethod
def _bm25_tokens(cls, text):
"""Lowercase German word stems, so "ohnmächtigen" matches "Ohnmacht"."""
if cls._stemmer is None:
import snowballstemmer
cls._stemmer = snowballstemmer.stemmer("german")
return cls._stemmer.stemWords(re.findall(r"[a-zäöüß0-9]{2,}", text.lower()))
def _bm25_index(self):
if self._bm25 is None:
from rank_bm25 import BM25Okapi
docs = list(self.vectorstore.docstore._dict.values())
self._bm25 = (BM25Okapi([self._bm25_tokens(d.page_content) for d in docs]), docs)
return self._bm25
def retrieve(self, query, k=None):
"""The k best chunks for a query as [(Document, score)].
dense: vector search only (score = L2 distance, lower is better)
hybrid: vector search + BM25, merged by Reciprocal Rank Fusion (higher is better)
union: vector candidates plus BM25_CANDIDATES keyword candidates, vector order first
With a reranker, the merged candidates are re-sorted by the cross-encoder
(score = relevance, higher is better)."""
k = k or RETRIEVAL_K
pool = max(k, CANDIDATES_K) if (self.retrieval_mode != "dense" or self.reranker_model) else k
dense = self.vectorstore.similarity_search_with_score(query, k=pool)
key = lambda doc: (doc.metadata.get("source"), doc.metadata.get("page"), doc.page_content[:200])
if self.retrieval_mode in ("hybrid", "union"):
bm25, docs = self._bm25_index()
scores = bm25.get_scores(self._bm25_tokens(query))
keyword = [docs[i] for i in np.argsort(scores)[::-1][:pool]]
if self.retrieval_mode == "union":
seen = {key(d) for d, _ in dense}
extra = [d for d in keyword if key(d) not in seen][:BM25_CANDIDATES]
candidates = [[d, s] for d, s in dense] + [[d, float("nan")] for d in extra]
elif self.retrieval_mode == "hybrid":
fused = {}
for ranking in ([d for d, _ in dense], keyword):
for rank, doc in enumerate(ranking):
entry = fused.setdefault(key(doc), [doc, 0.0])
entry[1] += 1 / (RRF_K + rank + 1)
candidates = sorted(fused.values(), key=lambda e: e[1], reverse=True)[:pool]
else:
candidates = [[d, s] for d, s in dense]
if self.reranker_model and candidates:
if self._reranker is None:
from sentence_transformers import CrossEncoder
print(f"Loading reranker {self.reranker_model}...")
self._reranker = CrossEncoder(self.reranker_model, max_length=1024)
relevance = self._reranker.predict([(query, d.page_content) for d, _ in candidates])
candidates = [[d, float(r)] for (d, _), r in zip(candidates, relevance)]
candidates.sort(key=lambda e: e[1], reverse=True)
return [(d, s) for d, s in candidates[:k]]
def query(self, question, k=None): def query(self, question, k=None):
"""Query the RAG system (no conversation history). Returns dict with 'answer' and 'retrieved'.""" """Query the RAG system (no conversation history). Returns dict with 'answer' and 'retrieved'."""
if k is None: if k is None:
@@ -243,12 +406,14 @@ Standalone question:"""
search_query = (rephrase_response.content if hasattr(rephrase_response, "content") else str(rephrase_response)).strip() or question search_query = (rephrase_response.content if hasattr(rephrase_response, "content") else str(rephrase_response)).strip() or question
print(f"[RAG] Standalone search query (rephrased): {search_query!r}") print(f"[RAG] Standalone search query (rephrased): {search_query!r}")
retrieval_query = f"{search_query}\n\n{QUERY_ADDITIONAL_INSTRUCTIONS}" retrieval_query = search_query
if QUERY_ADDITIONAL_INSTRUCTIONS:
retrieval_query += f"\n\n{QUERY_ADDITIONAL_INSTRUCTIONS}"
print(f"[RAG] Search query: {search_query!r}") print(f"[RAG] Search query: {search_query!r}")
print(f"[RAG] Retrieval query sent to vector store: {retrieval_query!r}") print(f"[RAG] Retrieval query sent to vector store: {retrieval_query!r}")
# 2) Retrieve documents with scores (FAISS: L2 distance, lower = more similar) # 2) Retrieve documents with scores (see retrieve() for what the score means)
docs_with_scores = self.vectorstore.similarity_search_with_score(retrieval_query, k=k) docs_with_scores = self.retrieve(retrieval_query, k=k)
docs = [doc for doc, _ in docs_with_scores] docs = [doc for doc, _ in docs_with_scores]
retrieved = self._docs_scores_to_retrieved(docs_with_scores) retrieved = self._docs_scores_to_retrieved(docs_with_scores)
if docs_with_scores: if docs_with_scores:
+211
View File
@@ -0,0 +1,211 @@
"""
Bereitet ein PDF-Korpus für den Index auf: bessere OCR, wo nötig, dann Docling.
Für jedes PDF:
0. Doppelseiten-Scans (Querformat) in Einzelseiten trennen (split_spreads.py),
damit OCR und Layout-Erkennung nie über den Falz lesen.
1. Textschicht prüfen (pypdf): fehlt sie oder ist die mitgelieferte OCR schlecht,
wird mit OCRmyPDF (Tesseract, deutsches Modell aus tessdata/) eine neue angelegt —
und nur behalten, wenn sie nachweislich besser ist (OCR-Fehler plus als Punkt
gelesene Kommas). Gute Textschichten bleiben unangetastet.
2. Docling liest die (alte oder neue) Textschicht ohne eigene OCR und erkennt
Absätze, Überschriften, Fußnoten, Seitenränder. Doclings eigene OCR verliert
bei diesen Scans Text und wird deshalb nicht benutzt.
3. Qualitätsprüfung mit grobid_qa.py auf dem bereinigten Ergebnis (wie es der
Loader liefert), festgehalten in manifest.csv.
Läuft in der Docling-Umgebung:
conda activate docling
python prepare_corpus.py # PDFs aus data/
python prepare_corpus.py data_test # anderer Ordner
python prepare_corpus.py data --only "Trost;Harzer" # nur Titel mit diesen Anfängen
python prepare_corpus.py data --no-split # Doppelseiten nicht trennen
python prepare_corpus.py data --layout egret_large --no-headings # Docling-Varianten
Ausgabe in corpus_processed/<ordner>_split/ (bzw. <ordner>/ mit --no-split):
split/<pdf>.pdf getrennte Fassung + .pages.json (Seite im Original je neuer Seite)
<pdf>.json Docling-Dokument (Eingabe für docling_loader.py)
ocr/<pdf>.pdf neu erkannte PDFs (nur wo nötig)
qa/<pdf>.md QA-Bericht mit Belegstellen
manifest.csv Entscheidung, Kennzahlen und QA-Urteil pro PDF
Bereits Verarbeitetes wird übersprungen; zum Neuaufbau den Ordner löschen.
"""
import argparse
import csv
import os
import shutil
import subprocess
import sys
import time
from pathlib import Path
from docling.datamodel.base_models import InputFormat
from docling.datamodel import pipeline_options as po
from docling.datamodel.pipeline_options import PdfPipelineOptions
from docling.document_converter import DocumentConverter, PdfFormatOption
import grobid_qa as qa
import split_spreads
from docling_loader import period_anomalies_per_10k
PDF_DIR = Path("data")
OUT_ROOT = Path("corpus_processed")
TESSDATA = Path("tessdata").resolve() # deu.traineddata + configs
os.environ["TESSDATA_PREFIX"] = str(TESSDATA)
# Ab wann die mitgelieferte Textschicht als schlecht gilt und neu erkannt wird.
# Kalibriert am Vergleich von fünf Titeln: Harzer (13.6) blieb mit alter Schicht
# besser, Beissner (47), Binder (39), Fiechter (63), Trost (264) wurden deutlich besser.
REOCR_OCR_PER_10K = 20 # OCR-Fehlerbilder je 10.000 Wörter im pypdf-Rohtext
REOCR_UNUSABLE_PCT = 10 # Seiten, auf denen pypdf nur Zeichensalat liefert, %
REOCR_SCAN_PCT = 1 # Seiten ganz ohne Textschicht, %
# Layout-Modelle von Docling; heron ist der Standard
LAYOUT_MODELS = {
"heron": po.DOCLING_LAYOUT_HERON,
"egret_medium": po.DOCLING_LAYOUT_EGRET_MEDIUM,
"egret_large": po.DOCLING_LAYOUT_EGRET_LARGE,
"egret_xlarge": po.DOCLING_LAYOUT_EGRET_XLARGE,
}
MANIFEST_COLS = [
"datei", "seiten", "getrennt", "entscheidung", "grund", "roh_ocr_per_10k", "roh_unbrauchbar_pct",
"roh_scan_pct", "roh_punkt_per_10k", "neu_ocr_per_10k", "neu_punkt_per_10k", "sekunden", "qa_urteil", "absaetze", "fussnoten", "giant_para_pct",
"lost_pct", "ocr_per_10k", "fnmark_per_10k", "contam_per_100k", "broken_per_100p", "order",
]
def assess_text_layer(pdf: Path) -> dict:
"""Kennzahlen der vorhandenen Textschicht, wie pypdf sie liest."""
pages = qa.read_pdf_pages(pdf)
raw = "\n".join(pages)
words = len(raw.split()) or 1
ocr = len(qa.OCR_RE.findall(qa.URL_RE.sub(" ", raw)))
return {
"seiten": len(pages),
"roh_ocr_per_10k": round(10000 * ocr / words, 1),
"roh_punkt_per_10k": period_anomalies_per_10k(" ".join(raw.split())),
"roh_unbrauchbar_pct": round(100 * sum(qa.page_unusable(p) for p in pages) / max(1, len(pages)), 1),
"roh_scan_pct": round(100 * sum(len(p.strip()) < 200 for p in pages) / max(1, len(pages)), 1),
}
def decide(m: dict) -> tuple[str, str]:
reasons = []
if m["roh_scan_pct"] >= REOCR_SCAN_PCT:
reasons.append(f"{m['roh_scan_pct']} % Seiten ohne Textschicht")
if m["roh_ocr_per_10k"] >= REOCR_OCR_PER_10K:
reasons.append(f"OCR-Fehler {m['roh_ocr_per_10k']} je 10k Wörter")
if m["roh_unbrauchbar_pct"] >= REOCR_UNUSABLE_PCT:
reasons.append(f"{m['roh_unbrauchbar_pct']} % unlesbare Seiten")
return ("neu erkannt", "; ".join(reasons)) if reasons else ("Textschicht behalten", "")
def layer_score(ocr_per_10k: float, punkt_per_10k: float) -> float:
"""Kleiner ist besser. Zerstörte Wörter schaden der Suche mehr als falsche Satzzeichen."""
return ocr_per_10k + 0.5 * punkt_per_10k
def ocrmypdf(pdf: Path, target: Path):
target.parent.mkdir(parents=True, exist_ok=True)
subprocess.run(
[str(Path(sys.executable).parent / "ocrmypdf"), "--force-ocr", "-l", "deu", "--deskew",
"--output-type", "pdf", "--jobs", "4", "--quiet", str(pdf), str(target)],
check=True)
def read_manifest(path: Path) -> dict:
if not path.exists():
return {}
with open(path, encoding="utf-8") as f:
return {r["datei"]: r for r in csv.DictReader(f, delimiter=";")}
def write_manifest(path: Path, rows: dict):
with open(path, "w", newline="", encoding="utf-8") as f:
w = csv.DictWriter(f, fieldnames=MANIFEST_COLS, extrasaction="ignore", delimiter=";")
w.writeheader()
w.writerows(rows[k] for k in sorted(rows))
def main():
parser = argparse.ArgumentParser(description="Korpus für den Index aufbereiten")
parser.add_argument("pdf_dir", nargs="?", type=Path, default=PDF_DIR)
parser.add_argument("--only", default="", help="Anfänge von Dateinamen, durch ; getrennt")
parser.add_argument("--no-split", action="store_true", help="Doppelseiten nicht trennen")
parser.add_argument("--layout", default="heron", choices=LAYOUT_MODELS, help="Docling-Layout-Modell")
parser.add_argument("--no-headings", action="store_true",
help="keine Überschriften-Hierarchie (alle Überschriften auf Ebene 1)")
args = parser.parse_args()
pdf_dir, split = args.pdf_dir, not args.no_split
# Getrennte und neu erkannte PDFs liegen im Basisordner und werden von allen
# Docling-Varianten geteilt; Varianten bekommen einen eigenen Unterordner.
base_dir = OUT_ROOT / (pdf_dir.name + ("_split" if split else ""))
variant = "_".join(([args.layout] if args.layout != "heron" else []) + (["flat"] if args.no_headings else []))
out_dir = base_dir / "varianten" / variant if variant else base_dir
(out_dir / "qa").mkdir(parents=True, exist_ok=True)
manifest_path = out_dir / "manifest.csv"
manifest = read_manifest(manifest_path)
opts = PdfPipelineOptions()
opts.do_ocr = False # Textschicht lesen, OCR macht OCRmyPDF
opts.do_table_structure = True
opts.layout_options = po.LayoutOptions(model_spec=LAYOUT_MODELS[args.layout])
opts.heading_hierarchy_options = po.HeadingHierarchyOptions(enabled=not args.no_headings)
conv = DocumentConverter(format_options={InputFormat.PDF: PdfFormatOption(pipeline_options=opts)})
pdfs = sorted(pdf_dir.glob("*.pdf"))
if args.only:
prefixes = [p.strip() for p in args.only.split(";") if p.strip()]
pdfs = [p for p in pdfs if any(p.name.startswith(x) for x in prefixes)]
for i, pdf in enumerate(pdfs, 1):
target = out_dir / f"{pdf.stem}.json"
if target.exists() and pdf.name in manifest:
print(f"[{i}/{len(pdfs)}] {pdf.name}: vorhanden, übersprungen", flush=True)
continue
print(f"[{i}/{len(pdfs)}] {pdf.name} ...", end=" ", flush=True)
t0 = time.time()
try:
work, row = pdf, {"datei": pdf.name, "getrennt": "nein"}
if split and split_spreads.is_spread_pdf(pdf):
work = base_dir / "split" / pdf.name
if not work.exists():
split_spreads.split_pdf(pdf, work)
row["getrennt"] = "ja"
row.update(assess_text_layer(work))
row["entscheidung"], row["grund"] = decide(row)
source = work
if row["entscheidung"] == "neu erkannt":
candidate = base_dir / "ocr" / pdf.name
if not candidate.exists():
ocrmypdf(work, candidate)
if row["getrennt"] == "ja": # Seitenzuordnung gilt auch für die OCR-Fassung
shutil.copy(split_spreads.pages_json(work), split_spreads.pages_json(candidate))
new = assess_text_layer(candidate)
row["neu_ocr_per_10k"], row["neu_punkt_per_10k"] = new["roh_ocr_per_10k"], new["roh_punkt_per_10k"]
if layer_score(new["roh_ocr_per_10k"], new["roh_punkt_per_10k"]) < \
layer_score(row["roh_ocr_per_10k"], row["roh_punkt_per_10k"]):
source = candidate
else:
row["entscheidung"] = "Textschicht behalten"
row["grund"] += "; neue OCR nicht besser"
conv.convert(source).document.save_as_json(target)
if row["getrennt"] == "ja": # für docling_loader.py: Seiten im Original
shutil.copy(split_spreads.pages_json(work), out_dir / f"{pdf.stem}.pages.json")
# QA auf dem bereinigten Ergebnis; Referenz ist die tatsächlich gelesene Textschicht
m, ex = qa.check(source, target, cleaned=True)
qa.write_report(m, ex, out_dir / "qa" / f"{pdf.stem}.md")
except Exception as e: # ein kaputtes PDF soll den Lauf nicht abbrechen
print(f"FEHLER: {e}", flush=True)
continue
row.update({k: m.get(k) for k in MANIFEST_COLS if k in m})
row["qa_urteil"] = m["grobid"]
row["sekunden"] = round(time.time() - t0)
manifest[pdf.name] = row
write_manifest(manifest_path, manifest)
print(f"{row['entscheidung']}, QA {row['qa_urteil']} ({row['sekunden']} s)", flush=True)
if __name__ == "__main__":
main()
+2
View File
@@ -10,3 +10,5 @@ pypdf
numpy<2.0 numpy<2.0
fastapi fastapi
uvicorn[standard] uvicorn[standard]
rank_bm25
snowballstemmer
+3 -1
View File
@@ -13,7 +13,9 @@ from local_rag import LocalRAG
OLLAMA_MODEL = "gpt-oss:20b" OLLAMA_MODEL = "gpt-oss:20b"
OPENAI_MODEL = "gpt-5-mini" OPENAI_MODEL = "gpt-5-mini"
VECTORSTORE_PATH = "./vectorstore" # Kafka corpus: Docling pipeline (split double pages) + chunk headers, bge-m3, hybrid search + reranker
# built with: add_pdfs.py --data data --index ./vectorstore_docling_hdr --loader docling
VECTORSTORE_PATH = "./vectorstore_docling_hdr"
# Dual RAG instances for on-the-fly provider switching # Dual RAG instances for on-the-fly provider switching
rag_ollama = LocalRAG( rag_ollama = LocalRAG(
+102
View File
@@ -0,0 +1,102 @@
"""
Trennt Doppelseiten-Scans in Einzelseiten, bevor OCR und Docling sie lesen.
Bei den meisten Titeln im Kafka-Korpus liegen zwei Buchseiten auf einer PDF-Seite
(Querformat). Tesseract liest dann stellenweise quer über den Falz, Doclings
Layout-Modell trennt Absätze an der Seitenmitte. Hier wird jede Querformat-Seite
in zwei Seiten zugeschnitten: Der Falz wird im Bild gesucht, als die senkrechte
Spalte in der Seitenmitte mit den wenigsten Hell-Dunkel-Wechseln (Text erzeugt
viele, Innenrand und Falzschatten kaum welche). Bildinhalt und Textschicht bleiben
unverändert, nur der sichtbare Seitenbereich (MediaBox/CropBox) wird gesetzt —
Docling und OCRmyPDF halten sich daran.
Neben jedes getrennte PDF wird <pdf>.pages.json geschrieben: zu jeder neuen Seite
die Seite im Original-PDF (ab 0) und die Hälfte ("L", "R" oder "" für ungeteilt).
docling_loader.py rechnet Seitenangaben damit auf das Original zurück.
Benutzt von prepare_corpus.py; läuft in der Docling-Umgebung (pypdfium2, numpy).
"""
import json
from pathlib import Path
import numpy as np
import pypdfium2 as pdfium
from pypdf import PdfReader, PdfWriter
from pypdf.generic import RectangleObject
LANDSCAPE_RATIO = 1.15 # Breite/Höhe, ab der eine Seite als Doppelseite gilt
SEARCH_BAND = (0.35, 0.65) # Falz wird in diesem Bereich der Seitenbreite gesucht
RENDER_SCALE = 0.5 # grobes Vorschaubild genügt für die Falzsuche
def find_gutter(page: pdfium.PdfPage) -> float:
"""Lage des Falzes als Anteil der Seitenbreite (0..1)."""
gray = np.asarray(page.render(scale=RENDER_SCALE).to_pil().convert("L"), dtype=np.int16)
h, w = gray.shape
band = gray[int(h * 0.08):int(h * 0.92)] # Ober- und Unterkante meiden
transitions = (np.abs(np.diff(band, axis=0)) > 40).sum(axis=0).astype(float)
k = max(3, w // 100)
smooth = np.convolve(transitions, np.ones(k) / k, mode="same")
lo, hi = int(w * SEARCH_BAND[0]), int(w * SEARCH_BAND[1])
window = smooth[lo:hi]
# Mitte des längsten Abschnitts nahe dem Minimum: der leere Streifen zwischen den Seiten
quiet = window <= window.min() + 0.15 * (np.median(window) - window.min())
best, start, run = (0, 0), None, []
for i, q in enumerate(list(quiet) + [False]):
if q and start is None:
start = i
elif not q and start is not None:
best = max(best, (i - start, start))
start = None
length, first = best
return (lo + first + length / 2) / w
def split_pdf(src: Path, dst: Path) -> list[dict]:
"""Schreibt das getrennte PDF nach dst und liefert die Seitenzuordnung."""
reader = PdfReader(src)
preview = pdfium.PdfDocument(str(src))
writer = PdfWriter()
mapping = []
for i, page in enumerate(reader.pages):
box = page.mediabox
left, bottom, right, top = (float(v) for v in (box.left, box.bottom, box.right, box.top))
rotated = int(page.get("/Rotate", 0) or 0) % 180 != 0
if rotated or (right - left) <= LANDSCAPE_RATIO * (top - bottom):
writer.add_page(page)
mapping.append({"original_page": i, "half": ""})
continue
x = left + find_gutter(preview[i]) * (right - left)
for half, (x0, x1) in (("L", (left, x)), ("R", (x, right))):
new = writer.add_page(page)
rect = RectangleObject([x0, bottom, x1, top])
new.mediabox = rect
new.cropbox = rect
mapping.append({"original_page": i, "half": half, "gutter_x": round(x, 1)})
dst.parent.mkdir(parents=True, exist_ok=True)
with open(dst, "wb") as f:
writer.write(f)
pages_json(dst).write_text(json.dumps(mapping, indent=1), encoding="utf-8")
return mapping
def pages_json(pdf: Path) -> Path:
return pdf.with_name(pdf.stem + ".pages.json")
def is_spread_pdf(pdf: Path, min_share: float = 0.5) -> bool:
"""Liegt bei mindestens min_share der Seiten Querformat vor?"""
pages = PdfReader(pdf).pages
land = sum(float(p.mediabox.width) > LANDSCAPE_RATIO * float(p.mediabox.height) for p in pages)
return land >= min_share * len(pages)
def bounded_page_texts(pdf: Path) -> list[str]:
"""Text jeder Seite, auf den sichtbaren Bereich begrenzt (pypdf ignoriert den Zuschnitt)."""
doc = pdfium.PdfDocument(str(pdf))
texts = []
for page in doc:
left, bottom, right, top = page.get_cropbox()
texts.append(page.get_textpage().get_text_bounded(left, bottom, right, top))
return texts