Ablauf der Filterung

Neun Stufen von der Frage zur Evidenzobjektmenge, und was sie am Kármán-Nachlass messen

Veröffentlichungsdatum

8. Oktober 2026

TippDer Ablauf in einem Satz

Die Partitur famulus_constraint_raum_agent bestimmt den Evidenzraum, liest aus der DTD die möglichen Attribute, nimmt den Kontext als Teilbaum auf, klassifiziert die Frage, führt beides zum Suchbaum zusammen, kompiliert ihn zu einer DuckDB-Anweisung, führt sie aus, bewertet die Menge, fragt gegebenenfalls nach, und beweist, dass kein Brief ohne Grund fehlt.

Die Stufen

Stufe Operation was sie liefert
1 raum famulus:evidenz_filter_raum Collection, Objektzahlen, Gate gate:fx, Lücken der Bereitschaft
2 schemabaum …_filter_schemabaum die Blätter, die die Klassifikation verwenden darf
3 kontextbaum …_filter_kontextbaum der Suchbaum der Vorfrage dieser Sitzung, der stehende Baum aus dem Gedächtnis der Collection
4 fragebaum …_filter_fragebaum ein Modellaufruf über den Blättern, dann der Fast Index je Wert
5 merge …_filter_merge Suchbaum; Ersetzungen, gestrichene Blätter, Widersprüche
6 sql …_filter_sql eine Anweisung, ohne Semikolon
7 ausfuehren …_filter_ausfuehren Briefe, Evidenzobjekte mit Text, Trichter, Verbuchung jedes Briefs, Projektion
8 bewerten …_filter_bewerten brauchbar, zu groß, unspezifisch, leer oder widersprüchlich; die Rückfrage
9 beweis …_filter_beweis Zertifikat famulus-raum/1

Die Evaluatoren der Partitur prüfen, dass jedes Textblatt die Unbekannt-Regel des Textes trägt und kein Kopf- oder Inhaltsblatt sie trägt, dass jeder Cluster ein positives Blatt hat, dass die Anweisung eine ist und dass der Trichter von Ebene zu Ebene nicht wächst.

Kontext: eine Frage, die an die vorige anschließt

Welche Briefe schrieb Ludwig Prandtl? Dann: Welches Thema hat ein Brief dieser Person an Theodor von Kármán? Die zweite Frage legt nur den Empfänger neu fest und fragt nach dem Thema; dass „diese Person“ Prandtl ist, steht im Kontextbaum, nicht in der Frage. Die Klassifikation meldet bezug_vorfrage, und der Merge nimmt das Blatt der Vorfrage auf:

Frage Kontext Blätter (Quelle) Briefe häufigste Themen
Welche Briefe schrieb Ludwig Prandtl? — kopf/korrespondenz/absender = Ludwig Prandtl (frage) 6.908 —
Welches Thema hat ein Brief dieser Person an Theodor von Kármán? sitzung kopf/korrespondenz/absender = Ludwig Prandtl (kontext); kopf/korrespondenz/empfaenger = Theodor von Kármán (frage) 6.873 Aerodynamics and Fluid Mechanics (3), Turbulence (3), Boundary layers (2)
Welches Thema hat ein Brief dieser Person an Theodor von Kármán? (verfeinert: nur verzeichneter Absender) — — 5 Aerodynamics and Fluid Mechanics (3), Turbulence (3), Boundary layers (2)

Bindet die neue Frage dasselbe Blatt neu (und von Einstein?), ersetzt ihr Wert den des Kontexts, und die Ersetzung steht im Ergebnis. Der Kontextbaum kommt aus dem Protokoll der Sitzung (~/Library/Application Support/famulus-proof/suchbaeume.jsonl) oder aus dem Schlüssel kontextbaum im agent_context.json der Collection. Die Textchronik der Anfragen von ai-query hält Fragen, keine Bäume, und ist keine Quelle.

Die Anweisung

Der Suchbaum wird eine einzige DuckDB-Anweisung (evidenzraum/theorie.py). Ihre CTEs folgen den Ebenen; hier für Kármán und Hitler:

WITH
b0 AS (...),            -- Kopfblatt Absender: erfüllt / verletzt / unentschieden je Brief (fx_post)
o1_hit AS (...),        -- Textblatt Nennung: Objekte mit P007304 (NER) oder einer Namensform (Wortform, Fußnote)
o1_ss AS (...),         -- die Sätze dieser Treffer
o1_v AS (...),          -- jedes Namensvorkommen mit seinen Faktoren und seiner Gebrauchsklasse
o1_k AS (...),          -- je Satz: eine akzeptierte Klasse? nur offene? nur Eponym, Beiwort, Zitat?
o1 AS (...),            -- Entscheidung je Objekt mit Grund („Gebrauch person: Handelnder“)
b1 AS (...),            -- Entscheidung je Brief: Treffer, sonst verletzt, ohne Text unentschieden (fx_deck)
k0 AS (...),            -- Briefe nach dem Kopf
i0 AS (...),            -- davon nach dem Inhalt
t0 AS (...),            -- davon nach dem Text
ob0 AS (...),           -- die Evidenzobjekte in diesen Briefen (fx_enthalten)
raum, obj, entscheid,   -- Vereinigung der Cluster, Objekte, alle Entscheidungen
zaehl, erste, buchung,  -- Zählung je Blatt, erstes ausschließendes Blatt, Verbuchung jedes Briefs
trichter, objtext       -- Briefe je Ebene, Texte der Objekte
SELECT briefe, n_objekte, tragend, objekte, zaehlung, trichter, gruende, buchung, projektion …
WITH b0 AS MATERIALIZED (
  SELECT l.traeger_id,
         CASE WHEN h.traeger_id IS NOT NULL THEN 'erfuellt' WHEN k.traeger_id IS NULL THEN 'unentschieden'
              ELSE 'verletzt' END AS e,
         CASE WHEN h.traeger_id IS NOT NULL THEN 'verzeichnet: ' || 'Theodor von Kármán'
              WHEN k.traeger_id IS NULL THEN 'nicht verzeichnet'
              ELSE 'verzeichnet ist ein anderer Wert als ' || 'Theodor von Kármán' END AS w
  FROM fx_traeger l LEFT JOIN (SELECT DISTINCT substr(obj_id, 3) AS traeger_id FROM fx_post WHERE attr = 'person' AND rolle = 'sender' AND art = 'brief' AND schluessel IN ('P000224')) h USING (traeger_id) LEFT JOIN (SELECT DISTINCT substr(obj_id, 3) AS traeger_id FROM fx_post WHERE attr = 'person' AND rolle = 'sender' AND art = 'brief') k USING (traeger_id)),
o1_hit AS MATERIALIZED (
  SELECT obj_id, kanal FROM fx_post
      WHERE attr = 'person' AND rolle = 'genannt' AND art IN ('satz', 'fussnote') AND schluessel IN ('P007304')
  UNION ALL
  SELECT obj_id, 'lemma' AS kanal FROM fx_post
      WHERE attr = 'lemma' AND rolle = 'text' AND art IN ('satz', 'fussnote') AND schluessel IN ('Hitler|de', 'Hitler|en', 'HITLER|en')
  UNION ALL
  SELECT obj_id, art AS kanal FROM fx_post
      WHERE attr = 'form' AND rolle = 'text' AND art IN ('fussnote') AND schluessel IN ('hitler')
),
o1_hh AS (SELECT obj_id, list(DISTINCT kanal ORDER BY kanal) AS kanaele FROM o1_hit GROUP BY obj_id),
o1_ss AS (SELECT split_part(substr(obj_id, 3), '#', 1) AS doc_id, CAST(split_part(split_part(substr(obj_id, 3), '#', 2), '.', 1) AS BIGINT) AS pidx, CAST(split_part(split_part(substr(obj_id, 3), '#', 2), '.', 2) AS BIGINT) AS sent_idx FROM o1_hh WHERE starts_with(obj_id, 'S:')),
o1_v AS (
  SELECT 'S:' || doc_id || '#' || pidx || '.' || sent_idx AS obj_id,
         CASE WHEN (FRE) THEN 'andere' WHEN (ZIT) OR (LIT) THEN 'zitat' WHEN (BEI) THEN 'beiwort' WHEN (KOM) OR (DOP) THEN 'eponym' WHEN (POS) THEN 'urheber' WHEN (ZIEL) OR (ARG) OR (NAM) OR (KOO) OR (NER) THEN 'person' ELSE 'offen' END AS klasse, CASE WHEN (FRE) THEN 'andere: Registerlink andere Person' WHEN (ZIT) THEN 'zitat: Zitatumgebung' WHEN (LIT) THEN 'zitat: Titelangabe' WHEN (BEI) THEN 'beiwort: Beiwort' WHEN (KOM) THEN 'eponym: Kompositumglied' WHEN (DOP) THEN 'eponym: Doppelname' WHEN (POS) THEN 'urheber: Urheber' WHEN (ZIEL) THEN 'person: Registerlink Ziel' WHEN (ARG) THEN 'person: Handelnder' WHEN (NAM) THEN 'person: Namensgefüge' WHEN (KOO) THEN 'person: Reihung' WHEN (NER) THEN 'person: Personenspanne' ELSE 'keine Bedingung trifft zu' END AS bedingung
  FROM (WITH g_ks AS MATERIALIZED (SELECT DISTINCT doc_id, pidx, sent_idx FROM (SELECT * FROM o1_ss)),
  g_tk AS MATERIALIZED (SELECT t.*, s.lang FROM stz.stz_tokens t JOIN g_ks USING (doc_id, pidx, sent_idx) JOIN stz.stz_sentences s USING (doc_id, pidx, sent_idx)),
  g_fe AS MATERIALIZED (SELECT DISTINCT x.doc_id, x.pidx, x.sent_idx FROM stz.stz_feats x JOIN g_ks USING (doc_id, pidx, sent_idx)
                        WHERE x.feat = 'VerbForm' AND x.val = 'Fin'),
  g_en AS MATERIALIZED (SELECT x.* FROM stz.stz_entities x JOIN g_ks USING (doc_id, pidx, sent_idx) WHERE x.ent_type IN ('PER', 'PERSON')),
  g_m AS MATERIALIZED (
    SELECT m.doc_id, m.pidx, m.sent_idx, e.ind_id, e.canonical
    FROM fq_mentions m JOIN g_ks USING (doc_id, pidx, sent_idx) JOIN fq_entities e ON e.ent_id = m.ent_id
    WHERE e.ind_id IS NOT NULL AND e.kind = 'person'),
  g_nk AS MATERIALIZED (
    SELECT DISTINCT x.canonical, f.lemma || '|' || f.lang AS lk
    FROM (SELECT DISTINCT canonical FROM g_m) x
    JOIN fx_lemma_form f ON strip_accents(f.form) = strip_accents(lower(trim(split_part(x.canonical, ',', 1))))),
  g_reg AS (
    SELECT m.doc_id, m.pidx, m.sent_idx, bool_or(list_contains(['P007304']::VARCHAR[], m.ind_id)) AS ziel,
           list(DISTINCT nk.lk) FILTER (WHERE nk.lk IS NOT NULL AND NOT list_contains(['P007304']::VARCHAR[], m.ind_id))
             AS andere_klassen
    FROM g_m m LEFT JOIN g_nk nk USING (canonical) GROUP BY ALL)
SELECT t.doc_id, t.pidx, t.sent_idx, t.tid, t.form, t.lemma, t.lang, t.upos, t.deprel, h.lemma AS kopf,
         coalesce(t.upos = 'PROPN', false) AS EIG,
         coalesce((t.deprel = 'amod' OR t.upos = 'ADJ'), false) AS BEI,
         coalesce((t.deprel = 'compound' AND (h.upos NOT IN ('PROPN', 'PRON') OR (h.upos = 'PROPN' AND h.deprel IN ('compound', 'flat', 'flat:name') AND hh.upos IN ('NOUN', 'VERB')))), false) AS KOM,
         coalesce((regexp_matches(t.form, '^[[:upper:]][[:alpha:]]+-[[:upper:]]') OR regexp_matches(t.form, '-$') OR EXISTS (SELECT 1 FROM g_tk x JOIN g_tk y ON y.doc_id = x.doc_id AND y.pidx = x.pidx AND y.sent_idx = x.sent_idx WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx AND x.form IN ('-', '–') AND ((x.tid = t.tid + 1 AND y.tid = t.tid + 2) OR (x.tid = t.tid - 1 AND y.tid = t.tid - 2)) AND y.upos = 'PROPN')), false) AS DOP,
         coalesce((t.deprel LIKE 'nmod%' AND h.upos = 'NOUN'), false) AS POS,
         coalesce((h.upos IN ('VERB', 'AUX') AND (t.deprel LIKE 'nsubj%' OR t.deprel IN ('obj', 'iobj') OR t.deprel LIKE 'obl%')), false) AS ARG,
         coalesce((t.deprel IN ('flat', 'flat:name', 'appos') OR EXISTS (SELECT 1 FROM g_tk x WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx AND x.head = t.tid AND x.deprel IN ('flat', 'flat:name', 'appos'))), false) AS NAM,
         coalesce((t.deprel = 'conj' AND h.upos IN ('PROPN', 'PRON') OR EXISTS (SELECT 1 FROM g_tk x WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx AND x.head = t.tid AND x.deprel = 'conj' AND x.upos IN ('PROPN', 'PRON'))), false) AS KOO,
         coalesce(EXISTS (SELECT 1 FROM g_en x WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx AND list_contains(string_split(trim(regexp_replace(lower(strip_accents(x.text)), '[^a-z0-9]+', ' ', 'g')), ' '), string_split(trim(regexp_replace(lower(strip_accents(t.form)), '[^a-z0-9]+', ' ', 'g')), ' ')[1])), false) AS NER,
         coalesce((NOT EXISTS (SELECT 1 FROM g_fe x WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx) AND EXISTS (SELECT 1 FROM g_tk x WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx AND x.upos = 'NUM')), false) AS ZIT,
         coalesce((NOT EXISTS (SELECT 1 FROM g_fe x WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx) AND EXISTS (SELECT 1 FROM g_tk x WHERE x.doc_id = t.doc_id AND x.pidx = t.pidx AND x.sent_idx = t.sent_idx AND x.upos = 'PUNCT' AND x.form = ':' AND x.tid > t.tid AND x.tid <= t.tid + 8)), false) AS LIT,
         coalesce(coalesce(g_reg.ziel, false), false) AS ZIEL,
         coalesce((NOT coalesce(g_reg.ziel, false) AND list_contains(coalesce(g_reg.andere_klassen, []), t.lemma || '|' || coalesce(t.lang, ''))), false) AS FRE,
         coalesce(t.deprel = 'root', false) AS WUR
  FROM g_tk t
  LEFT JOIN g_tk h ON h.doc_id = t.doc_id AND h.pidx = t.pidx AND h.sent_idx = t.sent_idx AND h.tid = t.head
  LEFT JOIN g_tk hh ON hh.doc_id = h.doc_id AND hh.pidx = h.pidx AND hh.sent_idx = h.sent_idx AND hh.tid = h.head
  LEFT JOIN g_reg ON g_reg.doc_id = t.doc_id AND g_reg.pidx = t.pidx AND g_reg.sent_idx = t.sent_idx
  WHERE list_contains(['Hitler|de', 'Hitler|en', 'HITLER|en']::VARCHAR[], t.lemma || '|' || coalesce(t.lang, '')))),
o1_k AS (
  SELECT obj_id, bool_or(list_contains(['person', 'urheber']::VARCHAR[], klasse)) AS ok, bool_or(klasse = 'offen') AS offen,
         string_agg(DISTINCT klasse, ', ' ORDER BY klasse) AS klassen,
         min(bedingung) FILTER (WHERE list_contains(['person', 'urheber']::VARCHAR[], klasse)) AS bed
  FROM o1_v GROUP BY obj_id),
o1 AS MATERIALIZED (
  SELECT h.obj_id, h.kanaele,
         CASE WHEN k.ok THEN 'erfuellt' WHEN k.offen THEN 'unentschieden'
              WHEN k.obj_id IS NOT NULL THEN 'verletzt'
              WHEN list_contains(h.kanaele, 'ner_linked') THEN 'erfuellt'
              ELSE 'unentschieden' END AS e,
         CASE WHEN k.ok THEN 'Gebrauch ' || k.bed
              WHEN k.offen THEN 'Gebrauch nicht entschieden'
              WHEN k.obj_id IS NOT NULL THEN 'Name nur als ' || k.klassen
              WHEN list_contains(h.kanaele, 'ner_linked') THEN 'Registerverknüpfung ohne Wortvorkommen'
              WHEN starts_with(h.obj_id, 'S:') THEN 'Lemmatreffer ohne lesbares Vorkommen'
              ELSE 'Objekt ohne Satzanalyse' END AS w
  FROM o1_hh h LEFT JOIN o1_k k USING (obj_id)),
b1 AS MATERIALIZED (
  SELECT l.traeger_id,
         CASE WHEN p.n_ok > 0 THEN 'erfuellt' WHEN p.n_uns > 0 THEN 'unentschieden'
              WHEN d.traeger_id IS NULL THEN 'unentschieden' ELSE 'verletzt' END AS e,
         CASE WHEN p.n_ok > 0 THEN 'Treffer in ' || p.n_ok || ' Objekt(en)'
              WHEN p.n_uns > 0 THEN 'nur unsichere Lesung in ' || p.n_uns || ' Objekt(en)'
              WHEN d.traeger_id IS NULL THEN 'kein Text der Objektarten ' || 'satz, fussnote'
              WHEN p.n_aus > 0 THEN p.w_aus
              ELSE 'kein Treffer im Text' END AS w
  FROM fx_traeger l
  LEFT JOIN (SELECT fb.traeger_id,
                    count(*) FILTER (WHERE o.e = 'erfuellt') AS n_ok,
                    count(*) FILTER (WHERE o.e = 'unentschieden') AS n_uns,
                    count(*) FILTER (WHERE o.e = 'verletzt') AS n_aus,
                    min(o.w) FILTER (WHERE o.e = 'verletzt') AS w_aus
             FROM o1 o JOIN fx_enthalten fb USING (obj_id) GROUP BY fb.traeger_id) p USING (traeger_id)
  LEFT JOIN (SELECT DISTINCT traeger_id FROM fx_deck WHERE art IN ('satz', 'fussnote')) d USING (traeger_id)),
k0 AS MATERIALIZED (SELECT traeger_id FROM fx_traeger JOIN b0 x0 USING (traeger_id) WHERE x0.e <> 'verletzt'),
i0 AS MATERIALIZED (SELECT traeger_id FROM k0),
t0 AS MATERIALIZED (SELECT traeger_id FROM i0 JOIN b1 x0 USING (traeger_id) WHERE x0.e <> 'verletzt'),
ob0 AS (
  SELECT o.obj_id, fb.traeger_id, o.kanaele FROM (SELECT obj_id, kanaele FROM o1 WHERE e <> 'verletzt') o JOIN fx_enthalten fb USING (obj_id) JOIN t0 USING (traeger_id)),
raum AS MATERIALIZED (SELECT DISTINCT traeger_id FROM (SELECT traeger_id FROM t0)),
obj AS MATERIALIZED (SELECT obj_id, list(DISTINCT traeger_id ORDER BY traeger_id) AS traeger, list_distinct(flatten(list(kanaele))) AS kanaele, list(DISTINCT cluster) AS cluster FROM (SELECT obj_id, traeger_id, kanaele, 0 AS cluster FROM ob0) GROUP BY obj_id),
entscheid AS (SELECT traeger_id, 'b1' AS knoten, 'kopf' AS ebene, 0 AS rang, e, w, false AS negiert, false AS streng FROM b0 UNION ALL SELECT traeger_id, 'b2' AS knoten, 'text' AS ebene, 2001 AS rang, e, w, false AS negiert, false AS streng FROM b1),
zaehl AS (
  SELECT knoten, any_value(ebene) AS ebene, min(rang) AS rang,
         count(*) FILTER (WHERE e = 'erfuellt') AS erfuellt,
         count(*) FILTER (WHERE e = 'unentschieden') AS unentschieden,
         count(*) FILTER (WHERE e = 'verletzt') AS verletzt
  FROM entscheid GROUP BY knoten),
erste AS (
  SELECT traeger_id, arg_min(struct_pack(knoten := knoten, w := w), rang) AS erste
  FROM entscheid WHERE (NOT negiert AND e = 'verletzt') OR (negiert AND e = 'erfuellt')
                     OR (streng AND e = 'unentschieden')
  GROUP BY traeger_id),
buchung AS (
  SELECT l.traeger_id,
         CASE WHEN r.traeger_id IS NOT NULL THEN 'raum' ELSE 'aus' END AS raum,
         CASE WHEN r.traeger_id IS NOT NULL THEN NULL
              ELSE coalesce(e.erste.knoten, 'einheit') END AS ausgeschlossen_durch,
         CASE WHEN r.traeger_id IS NOT NULL THEN NULL
              ELSE coalesce(e.erste.w, 'kein Objekt erfüllt die Textblätter gemeinsam (Einheit Satz)') END AS grund
  FROM fx_traeger l LEFT JOIN raum r USING (traeger_id) LEFT JOIN erste e USING (traeger_id)),
trichter AS (SELECT 0 AS cluster, (SELECT count(*) FROM fx_traeger) AS alle, (SELECT count(*) FROM k0) AS kopf,
                   (SELECT count(*) FROM i0) AS inhalt, (SELECT count(*) FROM t0) AS text,
                   (SELECT count(DISTINCT obj_id) FROM ob0) AS objekte),
objsel AS MATERIALIZED (SELECT * FROM obj ORDER BY obj_id LIMIT 300),
objtext AS (
  SELECT o.obj_id, 'satz' AS art, o.traeger, o.kanaele, s.text
  FROM (SELECT *, split_part(substr(obj_id, 3), '#', 1) AS doc_id, CAST(split_part(split_part(substr(obj_id, 3), '#', 2), '.', 1) AS BIGINT) AS pidx, CAST(split_part(split_part(substr(obj_id, 3), '#', 2), '.', 2) AS BIGINT) AS sent_idx FROM objsel WHERE starts_with(obj_id, 'S:')) o
  JOIN stz.stz_sentences s ON s.doc_id = o.doc_id AND s.pidx = o.pidx AND s.sent_idx = o.sent_idx
  UNION ALL
  SELECT o.obj_id, 'absatz', o.traeger, o.kanaele, a.text
  FROM (SELECT *, split_part(substr(obj_id, 3), '#', 1) AS doc_id, CAST(split_part(substr(obj_id, 3), '#', 2) AS BIGINT) AS pidx, CAST(split_part(substr(obj_id, 3), '#', 3) AS BIGINT) AS par_idx FROM objsel WHERE starts_with(obj_id, 'A:')) o
  JOIN fq_paragraphs a ON a.doc_id = o.doc_id AND a.pidx = o.pidx AND a.par_idx = o.par_idx
  UNION ALL
  SELECT o.obj_id, 'fussnote', o.traeger, o.kanaele, f.text
  FROM objsel o JOIN (SELECT obj_id, src_rowid FROM fx_objekt WHERE art = 'fussnote') x USING (obj_id)
  JOIN fq_footnotes f ON f.rowid = x.src_rowid
  UNION ALL
  SELECT o.obj_id, x.art, o.traeger, o.kanaele, NULL
  FROM objsel o JOIN (SELECT obj_id, art FROM fx_objekt WHERE art IN ('brief', 'seite', 'abbildung', 'dokument')) x
    USING (obj_id))
SELECT
  (SELECT list(traeger_id ORDER BY traeger_id) FROM raum) AS traeger_raum,
  (SELECT count(*) FROM obj) AS n_objekte,
  (SELECT list(DISTINCT l ORDER BY l) FROM (SELECT unnest(traeger) AS l FROM obj)) AS tragend,
  (SELECT list(struct_pack(art := art, n := n) ORDER BY art)
     FROM (SELECT x.art, count(*) AS n FROM obj JOIN fx_objekt x USING (obj_id) GROUP BY x.art)) AS objektarten,
  (SELECT list(struct_pack(obj_id := obj_id, art := art, traeger := traeger, kanaele := kanaele, text := text)
               ORDER BY obj_id) FROM objtext) AS objekte,
  (SELECT list(struct_pack(knoten := knoten, ebene := ebene, erfuellt := erfuellt,
                           unentschieden := unentschieden, verletzt := verletzt) ORDER BY rang) FROM zaehl) AS zaehlung,
  (SELECT list(struct_pack(cluster := cluster, alle := alle, kopf := kopf, inhalt := inhalt, text := text,
                           objekte := objekte) ORDER BY cluster) FROM trichter) AS trichter,
  (SELECT list(struct_pack(traeger_id := traeger_id, knoten := knoten, e := e, w := w) ORDER BY traeger_id, rang)
     FROM entscheid WHERE traeger_id IN (SELECT traeger_id FROM raum ORDER BY traeger_id LIMIT 2000)) AS gruende,
  (SELECT list(struct_pack(traeger_id := traeger_id, raum := raum, ausgeschlossen_durch := ausgeschlossen_durch,
                           grund := grund) ORDER BY traeger_id) FROM buchung) AS buchung,
  [] AS projektion_zaehlung,
  [] AS projektion

Die Entscheidungen jedes Blatts werden für alle Briefe gerechnet, weil der Raumbeweis jeden Brief verbuchen muss; die Einschränkung selbst geht Ebene für Ebene (k0 → i0 → t0), und die Evidenzobjekte werden nur in den verbliebenen Briefen gesucht.

Bewertung und Verfeinerung

Eine Menge ist zu groß, wenn sie mehr Objekte ihrer Art enthält als die Karte zulässt (Briefe 150, Dokumente 5, Sätze 400), unspezifisch, wenn ein positives Blatt weniger als 5 % der Träger ausschließt, leer ohne Träger oder ohne ein einziges Evidenzobjekt (dann bleiben nur unentschiedene Träger: Briefe an Maupertuis aus Berlin, 1754 oder 1755 hat 4 solche und kein Objekt), widersprüchlich, wenn der Merge einen Widerspruch fand. Die Rückfrage nennt dann den Hebel, der die Menge am stärksten teilt:

  • Lässt ein Blatt mehr als die Hälfte der Menge nur als unentschieden stehen, fragt sie zuerst, ob diese Briefe bleiben sollen. Welche Briefe schrieb Prandtl an Kármán zwischen 1930 und 1933? behält 5.949 Briefe; 5.945 davon nur, weil ihr Absender nicht verzeichnet ist. Die Antwort „nur verzeichnete“ macht das Blatt streng: 4 Briefe, alle Goldbriefe darunter.
  • Sonst schlägt sie das Unterattribut mit der größten Entropie über der Menge vor (Oberthema, Jahrzehnt, Sprache, Absendeort, Absender, Empfänger), mit den Zählungen je Wert aus derselben Anweisung. Ein Attribut, das nicht einschränken kann, wird nicht angeboten.

Die Antwort wird ein Blatt des Suchbaums (quelle: verfeinerung), und die Stufen 5 bis 9 laufen neu.

Der Raumbeweis

Das Zertifikat famulus-raum/1 sagt: jeder Träger des Körpers liegt im Raum, oder ein Blatt des Suchbaums hat ihn mit Grund ausgeschlossen (verletzt, bei einem negierten Blatt erfüllt, bei einem strengen unentschieden), oder kein Objekt erfüllt die Textblätter gemeinsam (Einheit Satz); die Facetten fx (Gate) und schemabaum (geprüfte Karte) gelten. Der Körper ist der Bau der Evidenztabellen (collection@fx-…, aus der Gate-Zeile). Der Prover (famulus_proof.entails) leitet daraus RaumVollstaendig(B@version, Frage) ab. Ein Brief, den nichts verbucht, wird genannt, und der Beweis hält nicht.

Ob die Blätter angemessen sind, zeigt der Beweis nicht. Das misst der Recall gegen Gold. Die Goldzeilen haben den Status vorgeschlagen und zählen erst, wenn der Eigentümer sie bestätigt.

Messung

Am 8. Oktober 2026 auf neun Collections, je Frage ein Kalt- und ein Warmlauf, berichtet der Warmlauf. Die Klassifikation ist als JSON vorgegeben, damit die Messung nicht vom Modell abhängt. „Objekte“ sind die Evidenzobjekte: in einer Textfrage die Sätze, Absätze und Fußnoten, die die Textblätter erfüllen; in einer Kopffrage die Träger, die jedes Blatt erfüllen. „tragend“ sind die Träger, die ein Evidenzobjekt enthalten; der Raum hält daneben die unentschiedenen. „Baum“ vergleicht die tragenden Briefe mit dem Raum des Entscheidungsbaums vom 4. Oktober (reports/data/e*-nachher.json). Recall gegen Gold gibt es nur im Kármán-Nachlass; die Goldzeilen von Hevelius, Euler und Pirckheimer sind leer (nicht_erntbar: es gibt keine Briefdatensätze, aus denen sie zu ernten wären), Wäfler, Kepler und Copernicus haben Probefragen ohne Gold.

Kármán-Nachlass — Träger: Briefe; Gold vorgeschlagen.

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Theodor von Kármán, nennt Adolf Hitler 15.976 → 9.413 → 9.413 → 175 1 1 1, gleich 1,0 / 1,0 151 ms brauchbar —
von Theodor von Kármán, nennt Alexander Weinstein 15.976 → 9.413 → 9.413 → 176 2 2 6, verschieden — 134 ms brauchbar —
von Theodor von Kármán, nennt Albert Einstein 15.976 → 9.413 → 9.413 → 208 40 34 44, verschieden 0,793 / 0,793 152 ms brauchbar —
von Theodor von Kármán, nennt Ludwig Prandtl 15.976 → 9.413 → 9.413 → 392 542 218 279, verschieden 0,87 / 0,87 194 ms zu groß 156, Recall 0,864
von Ludwig Prandtl, an Theodor von Kármán, 1930/1933 15.976 → 5.949 → 5.949 → 5.949 2 2 — 1,0 / 1,0 75 ms zu groß 4, Recall 1,0
von Theodor von Kármán, deutsch, aus Pasadena 15.976 → 1.809 → 1.809 → 1.809 18 18 — 1,0 / 1,0 81 ms zu groß 18, Recall 1,0
von Theodor von Kármán, 1933 15.976 → 6.621 → 6.621 → 6.621 22 22 — 1,0 / 1,0 71 ms zu groß 48, Recall 1,0
an Albert Einstein 15.976 → 9.077 → 9.077 → 9.077 1 1 — 1,0 / 1,0 63 ms zu groß 1, Recall 1,0

Hevelius — Träger: Briefe; kein Gold (die Goldzeilen sind nicht erntbar).

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Johannes Hevelius, an Pierre Des Noyers 2.970 → 494 → 494 → 494 101 101 — — 44 ms zu groß 102
von Pierre Des Noyers, an Johannes Hevelius, 1665 2.970 → 352 → 352 → 352 9 9 — — 45 ms zu groß 10
von Johannes Hevelius, fr 2.970 → 1.193 → 1.193 → 1.193 76 76 — — 46 ms zu groß 103
an Ismaël Boulliau, la 2.970 → 437 → 437 → 437 2 2 — — 43 ms zu groß 40
von Stanisław Lubieniecki, an Johannes Hevelius 2.970 → 456 → 456 → 456 63 63 — — 44 ms zu groß 63
von Henry Oldenburg, an Johannes Hevelius, 1665/1666 2.970 → 359 → 359 → 359 6 6 — — 44 ms zu groß 6
an Johannes Hevelius, 1679 2.970 → 396 → 396 → 396 56 56 — — 44 ms zu groß 57

Euler, Briefwechsel (IVA) — Träger: Briefe; kein Gold (die Goldzeilen sind nicht erntbar).

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Leonhard Euler, an Christian Goldbach 3.393 → 123 → 123 → 123 114 114 — — 44 ms brauchbar —
von Christian Goldbach, an Leonhard Euler 3.393 → 105 → 105 → 105 100 100 — — 44 ms brauchbar —
von Leonhard Euler, aus Berlin 3.393 → 985 → 985 → 985 780 780 — — 47 ms zu groß —
an Maupertuis, aus Berlin, 1754/1755 3.393 → 4 → 4 → 4 0 0 — — 43 ms leer 1
von Daniel Bernoulli, an Leonhard Euler 3.393 → 99 → 99 → 99 94 94 — — 44 ms brauchbar —
aus Petersburg, 1748 3.393 → 75 → 75 → 75 20 20 — — 42 ms brauchbar —
von Leonhard Euler, an Friedrich II. 3.393 → 67 → 67 → 67 50 50 — — 43 ms brauchbar —

Pirckheimer — Träger: Briefe; kein Gold (die Goldzeilen sind nicht erntbar).

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Desiderius Erasmus, an Willibald Pirckheimer 354 → 18 → 18 → 18 18 18 — — 38 ms brauchbar —
von Willibald Pirckheimer, an Desiderius Erasmus 354 → 3 → 3 → 3 3 3 — — 36 ms brauchbar —
aus Nürnberg, 1525 354 → 39 → 39 → 39 36 36 — — 37 ms brauchbar —
von Klara Pirckheimer, an Willibald Pirckheimer 354 → 20 → 20 → 20 20 20 — — 38 ms brauchbar —
an Willibald Pirckheimer, aus Basel 354 → 29 → 29 → 29 26 26 — — 38 ms brauchbar —
an Willibald Pirckheimer, 1524/1526 (offen: absender) 354 → 231 → 231 → 231 231 231 — — 38 ms zu groß —

Wäfler, Primärquellen — Träger: Dokumente; Probefragen, kein Gold.

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Markus Wäfler, book 15 → 5 → 5 → 5 4 4 — — 37 ms brauchbar —
nennt Hamidiya 15 → 15 → 15 → 14 443 14 — — 53 ms zu groß —
1975, Wort Siegel 15 → 10 → 10 → 4 210 4 — — 51 ms brauchbar —
diaserie 15 → 5 → 5 → 5 4 4 — — 36 ms brauchbar —

Wäfler, Sekundärliteratur — Träger: Dokumente; Probefragen, kein Gold.

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
nennt Markus Wäfler 15 → 15 → 15 → 7 41 7 — — 51 ms brauchbar —
von Mirjo Salvini, englisch 15 → 1 → 1 → 1 1 1 — — 44 ms brauchbar —

Kepler, Gesammelte Werke — Träger: Dokumente; Probefragen, kein Gold.

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Johannes Kepler, Buch 27 → 27 → 27 → 27 26 26 — — 40 ms zu groß —
nennt Tycho Brahe 27 → 27 → 27 → 25 1.415 25 — — 70 ms zu groß —
Wort Mars 27 → 27 → 27 → 26 3.101 26 — — 65 ms zu groß —

Kepler, Sekundärliteratur — Träger: Dokumente; Probefragen, kein Gold.

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Max Caspar, Buch 18 → 1 → 1 → 1 1 1 — — 36 ms brauchbar —
englisch 18 → 11 → 11 → 11 11 11 — — 36 ms zu groß —

Copernicus, Primärquellen — Träger: Dokumente; Probefragen, kein Gold.

Suchbaum Träger: alle → Kopf → Inhalt → Text Objekte tragend Baum Recall Raum / tragend SQL Urteil verfeinert
von Georg Joachim Rheticus 10 → 1 → 1 → 1 1 1 — — 36 ms brauchbar —
Wort revolutio 10 → 10 → 10 → 10 417 10 — — 52 ms zu groß —

Die Klassifikation durch das Modell, gesondert geprüft:

Die Klassifikation durch das Modell wurde in dieser Messung nicht geprüft.

Die Partitur über den Orchestrator (ExecutionHarness), Hitler-Frage im Kármán-Nachlass:

Kein Lauf über den Orchestrator in der Messung.

Die Stufe fragebaum enthält die Bindung der Namen an das Register (Fast Index); ohne Vorgabe kommt die Antwortzeit des Modellaufrufs der Klassifikation hinzu.

Ausführen

cd development/famulus-proof
.venv/bin/python scripts/build_fq_index.py karman-nachlass hevelius eulerOO-IVA pirckheimer waefler-prim waefler-sec kepler-prim kepler-sec copernicus-prim
.venv/bin/python scripts/pilot_constraint.py                     # Messung, reports/data/filter-pilot.json
../famulus-notebook/.venv/bin/python scripts/pilot_constraint.py --modell --orchestrator
.venv/bin/python scripts/docs_evidenzobjekte.py                  # Tabellen dieser Seiten
.venv/bin/python -c "from famulus_proof.evidenzraum import lauf; \
  st = lauf.filtern('In welchen Briefen erwähnt Theodor von Kármán die Person Adolf Hitler?', \
  collections=['karman-nachlass']); print(st['filter_ausfuehren']['trichter'])"