@cachly-dev/mcp-server 0.10.123 → 0.10.125
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/src/aussetzer.d.ts +67 -0
- package/dist/src/aussetzer.js +82 -0
- package/dist/src/aussetzer.js.map +1 -0
- package/dist/src/autor-abzeichen.d.ts +41 -0
- package/dist/src/autor-abzeichen.js +56 -0
- package/dist/src/autor-abzeichen.js.map +1 -0
- package/dist/src/autor.d.ts +101 -0
- package/dist/src/autor.js +136 -0
- package/dist/src/autor.js.map +1 -0
- package/dist/src/bauabdruck-daten.generiert.json +5 -0
- package/dist/src/bauabdruck.d.ts +38 -0
- package/dist/src/bauabdruck.js +93 -0
- package/dist/src/bauabdruck.js.map +1 -0
- package/dist/src/bedeutung.d.ts +175 -0
- package/dist/src/bedeutung.js +254 -0
- package/dist/src/bedeutung.js.map +1 -0
- package/dist/src/brain-export.d.ts +24 -0
- package/dist/src/brain-export.js +31 -1
- package/dist/src/brain-export.js.map +1 -1
- package/dist/src/deutsch.d.ts +98 -0
- package/dist/src/deutsch.js +223 -0
- package/dist/src/deutsch.js.map +1 -0
- package/dist/src/eingaenge-nachruesten.d.ts +41 -0
- package/dist/src/eingaenge-nachruesten.js +290 -0
- package/dist/src/eingaenge-nachruesten.js.map +1 -0
- package/dist/src/eingaenge.d.ts +135 -0
- package/dist/src/eingaenge.js +228 -0
- package/dist/src/eingaenge.js.map +1 -0
- package/dist/src/geliefert.d.ts +63 -0
- package/dist/src/geliefert.js +114 -0
- package/dist/src/geliefert.js.map +1 -0
- package/dist/src/handlers/advanced.js +7 -3
- package/dist/src/handlers/advanced.js.map +1 -1
- package/dist/src/handlers/brain.d.ts +19 -0
- package/dist/src/handlers/brain.js +492 -48
- package/dist/src/handlers/brain.js.map +1 -1
- package/dist/src/handlers/instances.d.ts +24 -0
- package/dist/src/handlers/instances.js +49 -1
- package/dist/src/handlers/instances.js.map +1 -1
- package/dist/src/handlers/team.js +105 -0
- package/dist/src/handlers/team.js.map +1 -1
- package/dist/src/index.d.ts +20 -0
- package/dist/src/index.js +282 -36
- package/dist/src/index.js.map +1 -1
- package/dist/src/lesson-preview.d.ts +81 -0
- package/dist/src/lesson-preview.js +133 -0
- package/dist/src/lesson-preview.js.map +1 -0
- package/dist/src/rangfolge.d.ts +169 -0
- package/dist/src/rangfolge.js +214 -0
- package/dist/src/rangfolge.js.map +1 -0
- package/dist/src/raumkorrektur.d.ts +100 -0
- package/dist/src/raumkorrektur.js +197 -0
- package/dist/src/raumkorrektur.js.map +1 -0
- package/dist/src/recall-tiefe.d.ts +48 -0
- package/dist/src/recall-tiefe.js +42 -0
- package/dist/src/recall-tiefe.js.map +1 -0
- package/dist/src/rerank.d.ts +0 -14
- package/dist/src/rerank.js +39 -2
- package/dist/src/rerank.js.map +1 -1
- package/dist/src/search.d.ts +56 -17
- package/dist/src/search.js +335 -73
- package/dist/src/search.js.map +1 -1
- package/dist/src/seltenheitsbestand.d.ts +53 -0
- package/dist/src/seltenheitsbestand.js +92 -0
- package/dist/src/seltenheitsbestand.js.map +1 -0
- package/dist/src/spuren.d.ts +97 -0
- package/dist/src/spuren.js +150 -0
- package/dist/src/spuren.js.map +1 -0
- package/dist/src/tools.js +1 -1
- package/dist/src/tools.js.map +1 -1
- package/dist/src/vorspann.d.ts +73 -0
- package/dist/src/vorspann.js +146 -0
- package/dist/src/vorspann.js.map +1 -0
- package/dist/src/werkzeug-nutzung.d.ts +62 -0
- package/dist/src/werkzeug-nutzung.js +72 -0
- package/dist/src/werkzeug-nutzung.js.map +1 -0
- package/dist/src/wertbeitrag.d.ts +57 -0
- package/dist/src/wertbeitrag.js +73 -0
- package/dist/src/wertbeitrag.js.map +1 -0
- package/glama.json +1 -1
- package/package.json +12 -4
- package/server.json +2 -2
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bauabdruck.js","sourceRoot":"","sources":["../../src/bauabdruck.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bedeutungsabgleich: die Frage findet die Lektion auch ohne gemeinsame Wörter.
|
|
3
|
+
*
|
|
4
|
+
* ── Warum es das gibt ────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* Gemessen am 19.08.2026 an 499 echten Lektionen mit 100 Fragen in Alltags-
|
|
7
|
+
* sprache:
|
|
8
|
+
*
|
|
9
|
+
* Verfahren Platz 1 Top 3 MRR
|
|
10
|
+
* Wörter (bisher) 21 % 33 % 29,3 %
|
|
11
|
+
* Bedeutung (bge-m3) 40 % 60 % 51,4 %
|
|
12
|
+
*
|
|
13
|
+
* Der Wortabgleich war ausgereizt: nach drei Reparaturen brachten weitere
|
|
14
|
+
* Eingriffe je eine Frage. Der Grund liegt in der Aufgabe, nicht in der
|
|
15
|
+
* Umsetzung. Eine Frage beschreibt ein SYMPTOM, die Lektion eine URSACHE.
|
|
16
|
+
* "Der Deploy hängt beim Bauen" und "No space left on device" teilen kein Wort
|
|
17
|
+
* und meinen dasselbe. Kein Wörterbuch schließt diese Lücke.
|
|
18
|
+
*
|
|
19
|
+
* ── Und es ist SCHNELLER, nicht langsamer ───────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* Das war die Überraschung. Am selben Tag gemessen:
|
|
22
|
+
*
|
|
23
|
+
* Wortabgleich (heute, 499 Lektionen) 788 ms (Mitte)
|
|
24
|
+
* Einbettung der Frage (Netzaufruf) 282 ms
|
|
25
|
+
* Vergleich gegen 494 Vektoren 0 ms (14 ms im schlechtesten Fall)
|
|
26
|
+
*
|
|
27
|
+
* Der Wortabgleich baut seinen Index bei JEDER Frage neu: alle Datensätze
|
|
28
|
+
* holen, zerlegen, Worthäufigkeiten und Bigramme rechnen. Das wächst linear
|
|
29
|
+
* mit dem Bestand — gemessen 1,6 ms je Lektion und Frage:
|
|
30
|
+
*
|
|
31
|
+
* Lektionen 50 100 250 499 998 1996
|
|
32
|
+
* Wartezeit 75ms 156ms 408ms 792ms 1601ms 3185ms
|
|
33
|
+
*
|
|
34
|
+
* Bei 2000 Lektionen sind das 3,2 Sekunden. Die automatische Einblendung hat
|
|
35
|
+
* ein 3-Sekunden-Budget — das erklärt, warum sie nichts liefert
|
|
36
|
+
* (cachly:ambient-recall-blendet-nichts-ein).
|
|
37
|
+
*
|
|
38
|
+
* Der Bedeutungsabgleich kostet dagegen konstant einen Netzaufruf. Er wird
|
|
39
|
+
* nicht langsamer, wenn der Bestand wächst.
|
|
40
|
+
*
|
|
41
|
+
* ── Was das Modul NICHT tut ─────────────────────────────────────────────────
|
|
42
|
+
*
|
|
43
|
+
* Es ersetzt den Wortabgleich nicht. Zwei Gründe:
|
|
44
|
+
*
|
|
45
|
+
* 1. Ohne Netz gibt es keine Einbettung. Der Wortabgleich läuft immer.
|
|
46
|
+
* 2. Gemischt sind die ersten zehn Treffer besser als bei beiden einzeln
|
|
47
|
+
* (71 % gegen 68 und 47). Wörter finden das wörtlich Genannte, Bedeutung
|
|
48
|
+
* das sinngemäß Gemeinte.
|
|
49
|
+
*/
|
|
50
|
+
import type { Redis } from 'ioredis';
|
|
51
|
+
/** Wo ein Lektionsvektor liegt. Ein Schlüssel je Lektion, neben der Lektion. */
|
|
52
|
+
export declare const VEKTOR_PRAEFIX = "cachly:lesson:vec:";
|
|
53
|
+
/**
|
|
54
|
+
* Wo der Vektor des THEMENNAMENS liegt.
|
|
55
|
+
*
|
|
56
|
+
* Warum getrennt vom Volltextvektor: der Name ist kurz und frageähnlich, der
|
|
57
|
+
* Volltext ist 1376 Zeichen lang. Eine 60-Zeichen-Frage gegen 60 Zeichen zu
|
|
58
|
+
* halten ist symmetrisch, gegen 1376 nicht — der Sortierer bewertet beide
|
|
59
|
+
* deshalb als eigene Merkmale (`naeheText` 1,0 und `naeheThema` 0,6).
|
|
60
|
+
*
|
|
61
|
+
* Gemessen am 19.08.2026: der Name allein findet nur 26 %, im Verbund trägt er
|
|
62
|
+
* verlässlich. Bis zum 20.08. gab es diese Vektoren nur im Messstand
|
|
63
|
+
* (korpus-gross.sicht-c.json), im Produkt nicht — der Sortierer aus
|
|
64
|
+
* rangfolge.ts war deshalb auch gar nicht verdrahtet.
|
|
65
|
+
*/
|
|
66
|
+
export declare const NAME_VEKTOR_PRAEFIX = "cachly:lesson:vecname:";
|
|
67
|
+
/** Der Text, aus dem der Namensvektor gebildet wird: Trennzeichen zu Wörtern. */
|
|
68
|
+
export declare function textFuerNamensVektor(topic: string): string;
|
|
69
|
+
/**
|
|
70
|
+
* Vektoren werden als base64 gespeichert, nicht als JSON-Zahlenliste.
|
|
71
|
+
*
|
|
72
|
+
* 1024 Zahlen als JSON sind rund 20 KB je Lektion, als Float32 sind es 4 KB.
|
|
73
|
+
* Bei 500 Lektionen ist das der Unterschied zwischen 10 MB und 2 MB, die bei
|
|
74
|
+
* jedem kalten Start über die Leitung gehen.
|
|
75
|
+
*/
|
|
76
|
+
export declare function packe(vektor: number[]): string;
|
|
77
|
+
export declare function entpacke(s: string): number[] | null;
|
|
78
|
+
/**
|
|
79
|
+
* Kosinus-Ähnlichkeit zweier Vektoren.
|
|
80
|
+
*
|
|
81
|
+
* Die Länge wird bei JEDEM Aufruf neu gerechnet statt einmal vorher. Das ist
|
|
82
|
+
* absichtlich: gemessen liegt der gesamte Vergleich gegen 494 Vektoren bei
|
|
83
|
+
* 0 ms. Eine Vorberechnung wäre eine zweite Wahrheit über denselben Vektor —
|
|
84
|
+
* und der Gewinn wäre nicht messbar.
|
|
85
|
+
*/
|
|
86
|
+
export declare function kosinus(a: number[], b: number[]): number;
|
|
87
|
+
/**
|
|
88
|
+
* Der Text, aus dem der Vektor einer Lektion gebildet wird.
|
|
89
|
+
*
|
|
90
|
+
* Bewusst NICHT das rohe JSON: Feldnamen, Zeitstempel und Prüfspuren tragen
|
|
91
|
+
* keine Bedeutung, verbrauchen aber vom Fenster des Einbettungsmodells. Der
|
|
92
|
+
* Dienst kürzt bei rund 2000 Zeichen — gemessen liefern 2000, 4000 und 6000
|
|
93
|
+
* Zeichen denselben Vektor. Was hinten steht, existiert für ihn nicht.
|
|
94
|
+
*/
|
|
95
|
+
export declare function textFuerVektor(lektion: Record<string, unknown>): string;
|
|
96
|
+
/**
|
|
97
|
+
* Ein Vektorbestand, der im Arbeitsspeicher bleibt.
|
|
98
|
+
*
|
|
99
|
+
* Warum: 494 Vektoren sind rund 2 MB. Die bei jeder Frage aus dem Speicher zu
|
|
100
|
+
* holen wäre langsamer als der Wortabgleich, den wir gerade ersetzen — der
|
|
101
|
+
* Gewinn wäre weg, bevor er anfängt.
|
|
102
|
+
*
|
|
103
|
+
* Der MCP-Server läuft lange, also wird einmal geladen und danach nur
|
|
104
|
+
* nachgetragen. Die Prüfung "hat sich etwas geändert" läuft über die ANZAHL
|
|
105
|
+
* der Schlüssel, nicht über ihren Inhalt: eine geänderte Lektion bekommt beim
|
|
106
|
+
* Lernen sofort ihren neuen Vektor geschrieben, und dieser Bestand holt ihn
|
|
107
|
+
* beim nächsten Nachladen.
|
|
108
|
+
*/
|
|
109
|
+
export declare class Vektorbestand {
|
|
110
|
+
private readonly frischeMs;
|
|
111
|
+
private readonly praefix;
|
|
112
|
+
private vektoren;
|
|
113
|
+
private geladen;
|
|
114
|
+
/**
|
|
115
|
+
* @param frischeMs wie lange ein geladener Bestand als frisch gilt
|
|
116
|
+
* @param praefix welche Sicht — Volltext (Standard) oder Themenname
|
|
117
|
+
*
|
|
118
|
+
* Der Präfix ist ein Parameter und keine zweite Klasse: die Mechanik ist
|
|
119
|
+
* identisch (scannen, in Blöcken holen, entpacken, Gelöschtes vergessen).
|
|
120
|
+
* Zwei Klassen dafür wären zwei Orte, an denen dieselbe Änderung nachgezogen
|
|
121
|
+
* werden müsste.
|
|
122
|
+
*/
|
|
123
|
+
constructor(frischeMs?: number, praefix?: string);
|
|
124
|
+
/** Die Nähe einer Frage zu EINER Lektion. -2, wenn sie keinen Vektor hat. */
|
|
125
|
+
naehe(frage: number[], topic: string): number;
|
|
126
|
+
/**
|
|
127
|
+
* Der Vektor selbst — für die Rückkopplung, die aus den besten Treffern
|
|
128
|
+
* einen angereicherten Fragevektor baut (`reichereAn`). `null`, wenn die
|
|
129
|
+
* Lektion keinen hat; der Aufrufer filtert das weg.
|
|
130
|
+
*/
|
|
131
|
+
rohvektor(topic: string): number[] | null;
|
|
132
|
+
get groesse(): number;
|
|
133
|
+
/** Lädt fehlende Vektoren nach. Tut nichts, wenn kürzlich geladen wurde. */
|
|
134
|
+
aktualisiere(redis: Redis, jetzt?: number): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Die ähnlichsten Lektionen zu einem Fragevektor.
|
|
137
|
+
*
|
|
138
|
+
* Zurück kommen die THEMEN, nicht die Schlüssel — der Aufrufer arbeitet mit
|
|
139
|
+
* Themen und soll das Schlüsselformat nicht kennen müssen.
|
|
140
|
+
*/
|
|
141
|
+
aehnlichste(frage: number[], anzahl: number): Array<{
|
|
142
|
+
topic: string;
|
|
143
|
+
naehe: number;
|
|
144
|
+
}>;
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Mischt zwei Rangfolgen über die PLATZIERUNG, nicht über die Punktzahl.
|
|
148
|
+
*
|
|
149
|
+
* Punktzahlen aus BM25 und aus einem Kosinus sind nicht vergleichbar: die eine
|
|
150
|
+
* ist nach oben offen, die andere liegt zwischen -1 und 1. Wer sie addiert oder
|
|
151
|
+
* skaliert, wählt unbeabsichtigt einen Gewinner und merkt es nie.
|
|
152
|
+
*
|
|
153
|
+
* Reciprocal Rank Fusion braucht keine gemeinsame Einheit.
|
|
154
|
+
*
|
|
155
|
+
* ── Wie viel Wortabgleich? ──────────────────────────────────────────────────
|
|
156
|
+
*
|
|
157
|
+
* Gemessen am 19.08.2026 an 499 Lektionen mit 100 Fragen:
|
|
158
|
+
*
|
|
159
|
+
* Wortgewicht Platz 1 Top 3 Top 10 MRR
|
|
160
|
+
* 0,0 (nur Sinn) 40 % 60 % 68 % 51,4 %
|
|
161
|
+
* 0,1 41 % 63 % 71 % 53,5 %
|
|
162
|
+
* 0,2 42 % 57 % 70 % 52,0 %
|
|
163
|
+
* 0,3 39 % 55 % 71 % 49,0 %
|
|
164
|
+
* 0,4 38 % 51 % 69 % 47,7 %
|
|
165
|
+
* 1,0 (nur Wörter) 21 % 33 % 47 % 29,3 %
|
|
166
|
+
*
|
|
167
|
+
* 0,1 ist auf drei von vier Maßen vorn und überall besser als reine Bedeutung.
|
|
168
|
+
* Wenig Wortabgleich hilft, viel schadet: die Wörter fangen den Fall, in dem
|
|
169
|
+
* jemand eine Fehlermeldung wörtlich einfügt, und stören sonst.
|
|
170
|
+
*
|
|
171
|
+
* Zu 0,2 ist der Abstand bei Platz 1 EINE Frage. Das ist kein Beleg, sondern
|
|
172
|
+
* eine Richtung — wer hier dreht, misst nach.
|
|
173
|
+
*/
|
|
174
|
+
export declare const WORT_GEWICHT = 0.1;
|
|
175
|
+
export declare function mischeRangfolgen(woerter: string[], bedeutung: string[], wortGewicht?: number, k?: number): string[];
|
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bedeutungsabgleich: die Frage findet die Lektion auch ohne gemeinsame Wörter.
|
|
3
|
+
*
|
|
4
|
+
* ── Warum es das gibt ────────────────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* Gemessen am 19.08.2026 an 499 echten Lektionen mit 100 Fragen in Alltags-
|
|
7
|
+
* sprache:
|
|
8
|
+
*
|
|
9
|
+
* Verfahren Platz 1 Top 3 MRR
|
|
10
|
+
* Wörter (bisher) 21 % 33 % 29,3 %
|
|
11
|
+
* Bedeutung (bge-m3) 40 % 60 % 51,4 %
|
|
12
|
+
*
|
|
13
|
+
* Der Wortabgleich war ausgereizt: nach drei Reparaturen brachten weitere
|
|
14
|
+
* Eingriffe je eine Frage. Der Grund liegt in der Aufgabe, nicht in der
|
|
15
|
+
* Umsetzung. Eine Frage beschreibt ein SYMPTOM, die Lektion eine URSACHE.
|
|
16
|
+
* "Der Deploy hängt beim Bauen" und "No space left on device" teilen kein Wort
|
|
17
|
+
* und meinen dasselbe. Kein Wörterbuch schließt diese Lücke.
|
|
18
|
+
*
|
|
19
|
+
* ── Und es ist SCHNELLER, nicht langsamer ───────────────────────────────────
|
|
20
|
+
*
|
|
21
|
+
* Das war die Überraschung. Am selben Tag gemessen:
|
|
22
|
+
*
|
|
23
|
+
* Wortabgleich (heute, 499 Lektionen) 788 ms (Mitte)
|
|
24
|
+
* Einbettung der Frage (Netzaufruf) 282 ms
|
|
25
|
+
* Vergleich gegen 494 Vektoren 0 ms (14 ms im schlechtesten Fall)
|
|
26
|
+
*
|
|
27
|
+
* Der Wortabgleich baut seinen Index bei JEDER Frage neu: alle Datensätze
|
|
28
|
+
* holen, zerlegen, Worthäufigkeiten und Bigramme rechnen. Das wächst linear
|
|
29
|
+
* mit dem Bestand — gemessen 1,6 ms je Lektion und Frage:
|
|
30
|
+
*
|
|
31
|
+
* Lektionen 50 100 250 499 998 1996
|
|
32
|
+
* Wartezeit 75ms 156ms 408ms 792ms 1601ms 3185ms
|
|
33
|
+
*
|
|
34
|
+
* Bei 2000 Lektionen sind das 3,2 Sekunden. Die automatische Einblendung hat
|
|
35
|
+
* ein 3-Sekunden-Budget — das erklärt, warum sie nichts liefert
|
|
36
|
+
* (cachly:ambient-recall-blendet-nichts-ein).
|
|
37
|
+
*
|
|
38
|
+
* Der Bedeutungsabgleich kostet dagegen konstant einen Netzaufruf. Er wird
|
|
39
|
+
* nicht langsamer, wenn der Bestand wächst.
|
|
40
|
+
*
|
|
41
|
+
* ── Was das Modul NICHT tut ─────────────────────────────────────────────────
|
|
42
|
+
*
|
|
43
|
+
* Es ersetzt den Wortabgleich nicht. Zwei Gründe:
|
|
44
|
+
*
|
|
45
|
+
* 1. Ohne Netz gibt es keine Einbettung. Der Wortabgleich läuft immer.
|
|
46
|
+
* 2. Gemischt sind die ersten zehn Treffer besser als bei beiden einzeln
|
|
47
|
+
* (71 % gegen 68 und 47). Wörter finden das wörtlich Genannte, Bedeutung
|
|
48
|
+
* das sinngemäß Gemeinte.
|
|
49
|
+
*/
|
|
50
|
+
/** Wo ein Lektionsvektor liegt. Ein Schlüssel je Lektion, neben der Lektion. */
|
|
51
|
+
export const VEKTOR_PRAEFIX = 'cachly:lesson:vec:';
|
|
52
|
+
/**
|
|
53
|
+
* Wo der Vektor des THEMENNAMENS liegt.
|
|
54
|
+
*
|
|
55
|
+
* Warum getrennt vom Volltextvektor: der Name ist kurz und frageähnlich, der
|
|
56
|
+
* Volltext ist 1376 Zeichen lang. Eine 60-Zeichen-Frage gegen 60 Zeichen zu
|
|
57
|
+
* halten ist symmetrisch, gegen 1376 nicht — der Sortierer bewertet beide
|
|
58
|
+
* deshalb als eigene Merkmale (`naeheText` 1,0 und `naeheThema` 0,6).
|
|
59
|
+
*
|
|
60
|
+
* Gemessen am 19.08.2026: der Name allein findet nur 26 %, im Verbund trägt er
|
|
61
|
+
* verlässlich. Bis zum 20.08. gab es diese Vektoren nur im Messstand
|
|
62
|
+
* (korpus-gross.sicht-c.json), im Produkt nicht — der Sortierer aus
|
|
63
|
+
* rangfolge.ts war deshalb auch gar nicht verdrahtet.
|
|
64
|
+
*/
|
|
65
|
+
export const NAME_VEKTOR_PRAEFIX = 'cachly:lesson:vecname:';
|
|
66
|
+
/** Der Text, aus dem der Namensvektor gebildet wird: Trennzeichen zu Wörtern. */
|
|
67
|
+
export function textFuerNamensVektor(topic) {
|
|
68
|
+
return topic.replace(/[:_-]+/g, ' ').replace(/\s+/g, ' ').trim();
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Vektoren werden als base64 gespeichert, nicht als JSON-Zahlenliste.
|
|
72
|
+
*
|
|
73
|
+
* 1024 Zahlen als JSON sind rund 20 KB je Lektion, als Float32 sind es 4 KB.
|
|
74
|
+
* Bei 500 Lektionen ist das der Unterschied zwischen 10 MB und 2 MB, die bei
|
|
75
|
+
* jedem kalten Start über die Leitung gehen.
|
|
76
|
+
*/
|
|
77
|
+
export function packe(vektor) {
|
|
78
|
+
const f = new Float32Array(vektor);
|
|
79
|
+
return Buffer.from(f.buffer, f.byteOffset, f.byteLength).toString('base64');
|
|
80
|
+
}
|
|
81
|
+
export function entpacke(s) {
|
|
82
|
+
try {
|
|
83
|
+
const b = Buffer.from(s, 'base64');
|
|
84
|
+
if (b.byteLength === 0 || b.byteLength % 4 !== 0)
|
|
85
|
+
return null;
|
|
86
|
+
const f = new Float32Array(b.buffer, b.byteOffset, b.byteLength / 4);
|
|
87
|
+
return Array.from(f);
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Kosinus-Ähnlichkeit zweier Vektoren.
|
|
95
|
+
*
|
|
96
|
+
* Die Länge wird bei JEDEM Aufruf neu gerechnet statt einmal vorher. Das ist
|
|
97
|
+
* absichtlich: gemessen liegt der gesamte Vergleich gegen 494 Vektoren bei
|
|
98
|
+
* 0 ms. Eine Vorberechnung wäre eine zweite Wahrheit über denselben Vektor —
|
|
99
|
+
* und der Gewinn wäre nicht messbar.
|
|
100
|
+
*/
|
|
101
|
+
export function kosinus(a, b) {
|
|
102
|
+
if (a.length !== b.length)
|
|
103
|
+
return -1;
|
|
104
|
+
let p = 0;
|
|
105
|
+
let qa = 0;
|
|
106
|
+
let qb = 0;
|
|
107
|
+
for (let i = 0; i < a.length; i++) {
|
|
108
|
+
p += a[i] * b[i];
|
|
109
|
+
qa += a[i] * a[i];
|
|
110
|
+
qb += b[i] * b[i];
|
|
111
|
+
}
|
|
112
|
+
return qa && qb ? p / Math.sqrt(qa * qb) : 0;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Der Text, aus dem der Vektor einer Lektion gebildet wird.
|
|
116
|
+
*
|
|
117
|
+
* Bewusst NICHT das rohe JSON: Feldnamen, Zeitstempel und Prüfspuren tragen
|
|
118
|
+
* keine Bedeutung, verbrauchen aber vom Fenster des Einbettungsmodells. Der
|
|
119
|
+
* Dienst kürzt bei rund 2000 Zeichen — gemessen liefern 2000, 4000 und 6000
|
|
120
|
+
* Zeichen denselben Vektor. Was hinten steht, existiert für ihn nicht.
|
|
121
|
+
*/
|
|
122
|
+
export function textFuerVektor(lektion) {
|
|
123
|
+
const s = (v) => (typeof v === 'string' ? v : '');
|
|
124
|
+
return [s(lektion.topic), s(lektion.what_worked), s(lektion.what_failed)]
|
|
125
|
+
.filter(Boolean).join('\n').slice(0, 2000);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Ein Vektorbestand, der im Arbeitsspeicher bleibt.
|
|
129
|
+
*
|
|
130
|
+
* Warum: 494 Vektoren sind rund 2 MB. Die bei jeder Frage aus dem Speicher zu
|
|
131
|
+
* holen wäre langsamer als der Wortabgleich, den wir gerade ersetzen — der
|
|
132
|
+
* Gewinn wäre weg, bevor er anfängt.
|
|
133
|
+
*
|
|
134
|
+
* Der MCP-Server läuft lange, also wird einmal geladen und danach nur
|
|
135
|
+
* nachgetragen. Die Prüfung "hat sich etwas geändert" läuft über die ANZAHL
|
|
136
|
+
* der Schlüssel, nicht über ihren Inhalt: eine geänderte Lektion bekommt beim
|
|
137
|
+
* Lernen sofort ihren neuen Vektor geschrieben, und dieser Bestand holt ihn
|
|
138
|
+
* beim nächsten Nachladen.
|
|
139
|
+
*/
|
|
140
|
+
export class Vektorbestand {
|
|
141
|
+
frischeMs;
|
|
142
|
+
praefix;
|
|
143
|
+
vektoren = new Map();
|
|
144
|
+
geladen = 0;
|
|
145
|
+
/**
|
|
146
|
+
* @param frischeMs wie lange ein geladener Bestand als frisch gilt
|
|
147
|
+
* @param praefix welche Sicht — Volltext (Standard) oder Themenname
|
|
148
|
+
*
|
|
149
|
+
* Der Präfix ist ein Parameter und keine zweite Klasse: die Mechanik ist
|
|
150
|
+
* identisch (scannen, in Blöcken holen, entpacken, Gelöschtes vergessen).
|
|
151
|
+
* Zwei Klassen dafür wären zwei Orte, an denen dieselbe Änderung nachgezogen
|
|
152
|
+
* werden müsste.
|
|
153
|
+
*/
|
|
154
|
+
constructor(frischeMs = 60_000, praefix = VEKTOR_PRAEFIX) {
|
|
155
|
+
this.frischeMs = frischeMs;
|
|
156
|
+
this.praefix = praefix;
|
|
157
|
+
}
|
|
158
|
+
/** Die Nähe einer Frage zu EINER Lektion. -2, wenn sie keinen Vektor hat. */
|
|
159
|
+
naehe(frage, topic) {
|
|
160
|
+
const v = this.vektoren.get(`${this.praefix}${topic}`);
|
|
161
|
+
return v ? kosinus(frage, v) : -2;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Der Vektor selbst — für die Rückkopplung, die aus den besten Treffern
|
|
165
|
+
* einen angereicherten Fragevektor baut (`reichereAn`). `null`, wenn die
|
|
166
|
+
* Lektion keinen hat; der Aufrufer filtert das weg.
|
|
167
|
+
*/
|
|
168
|
+
rohvektor(topic) {
|
|
169
|
+
return this.vektoren.get(`${this.praefix}${topic}`) ?? null;
|
|
170
|
+
}
|
|
171
|
+
get groesse() { return this.vektoren.size; }
|
|
172
|
+
/** Lädt fehlende Vektoren nach. Tut nichts, wenn kürzlich geladen wurde. */
|
|
173
|
+
async aktualisiere(redis, jetzt = Date.now()) {
|
|
174
|
+
if (this.vektoren.size > 0 && jetzt - this.geladen < this.frischeMs)
|
|
175
|
+
return;
|
|
176
|
+
const schluessel = [];
|
|
177
|
+
let cursor = '0';
|
|
178
|
+
do {
|
|
179
|
+
const [next, gefunden] = await redis.scan(cursor, 'MATCH', `${this.praefix}*`, 'COUNT', 500);
|
|
180
|
+
cursor = next;
|
|
181
|
+
schluessel.push(...gefunden);
|
|
182
|
+
} while (cursor !== '0');
|
|
183
|
+
const fehlend = schluessel.filter((k) => !this.vektoren.has(k));
|
|
184
|
+
for (let i = 0; i < fehlend.length; i += 100) {
|
|
185
|
+
const block = fehlend.slice(i, i + 100);
|
|
186
|
+
const werte = await redis.mget(...block);
|
|
187
|
+
for (const [j, roh] of werte.entries()) {
|
|
188
|
+
if (!roh)
|
|
189
|
+
continue;
|
|
190
|
+
const v = entpacke(roh);
|
|
191
|
+
if (v)
|
|
192
|
+
this.vektoren.set(block[j], v);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
// Gelöschte Lektionen wieder loswerden.
|
|
196
|
+
if (schluessel.length < this.vektoren.size) {
|
|
197
|
+
const da = new Set(schluessel);
|
|
198
|
+
for (const k of [...this.vektoren.keys()])
|
|
199
|
+
if (!da.has(k))
|
|
200
|
+
this.vektoren.delete(k);
|
|
201
|
+
}
|
|
202
|
+
this.geladen = jetzt;
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Die ähnlichsten Lektionen zu einem Fragevektor.
|
|
206
|
+
*
|
|
207
|
+
* Zurück kommen die THEMEN, nicht die Schlüssel — der Aufrufer arbeitet mit
|
|
208
|
+
* Themen und soll das Schlüsselformat nicht kennen müssen.
|
|
209
|
+
*/
|
|
210
|
+
aehnlichste(frage, anzahl) {
|
|
211
|
+
const aus = [];
|
|
212
|
+
for (const [k, v] of this.vektoren) {
|
|
213
|
+
aus.push({ topic: k.slice(this.praefix.length), naehe: kosinus(frage, v) });
|
|
214
|
+
}
|
|
215
|
+
aus.sort((a, b) => b.naehe - a.naehe);
|
|
216
|
+
return aus.slice(0, anzahl);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Mischt zwei Rangfolgen über die PLATZIERUNG, nicht über die Punktzahl.
|
|
221
|
+
*
|
|
222
|
+
* Punktzahlen aus BM25 und aus einem Kosinus sind nicht vergleichbar: die eine
|
|
223
|
+
* ist nach oben offen, die andere liegt zwischen -1 und 1. Wer sie addiert oder
|
|
224
|
+
* skaliert, wählt unbeabsichtigt einen Gewinner und merkt es nie.
|
|
225
|
+
*
|
|
226
|
+
* Reciprocal Rank Fusion braucht keine gemeinsame Einheit.
|
|
227
|
+
*
|
|
228
|
+
* ── Wie viel Wortabgleich? ──────────────────────────────────────────────────
|
|
229
|
+
*
|
|
230
|
+
* Gemessen am 19.08.2026 an 499 Lektionen mit 100 Fragen:
|
|
231
|
+
*
|
|
232
|
+
* Wortgewicht Platz 1 Top 3 Top 10 MRR
|
|
233
|
+
* 0,0 (nur Sinn) 40 % 60 % 68 % 51,4 %
|
|
234
|
+
* 0,1 41 % 63 % 71 % 53,5 %
|
|
235
|
+
* 0,2 42 % 57 % 70 % 52,0 %
|
|
236
|
+
* 0,3 39 % 55 % 71 % 49,0 %
|
|
237
|
+
* 0,4 38 % 51 % 69 % 47,7 %
|
|
238
|
+
* 1,0 (nur Wörter) 21 % 33 % 47 % 29,3 %
|
|
239
|
+
*
|
|
240
|
+
* 0,1 ist auf drei von vier Maßen vorn und überall besser als reine Bedeutung.
|
|
241
|
+
* Wenig Wortabgleich hilft, viel schadet: die Wörter fangen den Fall, in dem
|
|
242
|
+
* jemand eine Fehlermeldung wörtlich einfügt, und stören sonst.
|
|
243
|
+
*
|
|
244
|
+
* Zu 0,2 ist der Abstand bei Platz 1 EINE Frage. Das ist kein Beleg, sondern
|
|
245
|
+
* eine Richtung — wer hier dreht, misst nach.
|
|
246
|
+
*/
|
|
247
|
+
export const WORT_GEWICHT = 0.1;
|
|
248
|
+
export function mischeRangfolgen(woerter, bedeutung, wortGewicht = WORT_GEWICHT, k = 60) {
|
|
249
|
+
const punkte = new Map();
|
|
250
|
+
woerter.forEach((t, i) => punkte.set(t, (punkte.get(t) ?? 0) + wortGewicht / (k + i + 1)));
|
|
251
|
+
bedeutung.forEach((t, i) => punkte.set(t, (punkte.get(t) ?? 0) + (1 - wortGewicht) / (k + i + 1)));
|
|
252
|
+
return [...punkte.entries()].sort((a, b) => b[1] - a[1]).map(([t]) => t);
|
|
253
|
+
}
|
|
254
|
+
//# sourceMappingURL=bedeutung.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bedeutung.js","sourceRoot":"","sources":["../../src/bedeutung.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,7 +20,31 @@ export interface ExportLesson {
|
|
|
20
20
|
ts?: string;
|
|
21
21
|
author?: string;
|
|
22
22
|
recallCount?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Der GESPEICHERTE Datensatz, unveraendert.
|
|
25
|
+
*
|
|
26
|
+
* Warum es das Feld gibt: bis zum 19.08.2026 zog `cachly export` seine Daten
|
|
27
|
+
* aus /memory, einer Dashboard-Zusammenfassung. Ergebnis: 50 Lektionen von
|
|
28
|
+
* 493, what_worked auf 120 Zeichen abgeschnitten. Das Format war nie das
|
|
29
|
+
* Problem — dieses Modul kann what_failed und file_paths seit jeher — die
|
|
30
|
+
* QUELLE war es.
|
|
31
|
+
*
|
|
32
|
+
* Damit sich das nicht wiederholt, traegt die JSONL-Zeile jetzt den rohen
|
|
33
|
+
* Datensatz, wenn er da ist. Wer morgen ein Feld an die Lektion haengt, muss
|
|
34
|
+
* dieses Modul nicht anfassen: es steht automatisch im Export. Ein Export,
|
|
35
|
+
* der wissen muss, welche Felder es gibt, verliert genau die, an die niemand
|
|
36
|
+
* gedacht hat.
|
|
37
|
+
*/
|
|
38
|
+
raw?: Record<string, unknown>;
|
|
23
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Macht aus einem gespeicherten Lektions-Datensatz eine ExportLesson.
|
|
42
|
+
*
|
|
43
|
+
* Die bekannten Felder werden benannt, damit das Markdown sie rendern kann.
|
|
44
|
+
* Der ganze Datensatz bleibt zusaetzlich unter `raw` erhalten, damit die
|
|
45
|
+
* JSONL-Zeile nichts verliert.
|
|
46
|
+
*/
|
|
47
|
+
export declare function vonRohLektion(roh: unknown): ExportLesson | null;
|
|
24
48
|
/**
|
|
25
49
|
* Builds the human-readable Markdown dump: one `##` section per topic
|
|
26
50
|
* category (the part of `topic` before the first `:`, or "Ungrouped" when
|
package/dist/src/brain-export.js
CHANGED
|
@@ -4,6 +4,36 @@
|
|
|
4
4
|
* design — no network, no filesystem. The `export` CLI command in index.ts
|
|
5
5
|
* fetches the lessons and writes the files these functions build.
|
|
6
6
|
*/
|
|
7
|
+
/**
|
|
8
|
+
* Macht aus einem gespeicherten Lektions-Datensatz eine ExportLesson.
|
|
9
|
+
*
|
|
10
|
+
* Die bekannten Felder werden benannt, damit das Markdown sie rendern kann.
|
|
11
|
+
* Der ganze Datensatz bleibt zusaetzlich unter `raw` erhalten, damit die
|
|
12
|
+
* JSONL-Zeile nichts verliert.
|
|
13
|
+
*/
|
|
14
|
+
export function vonRohLektion(roh) {
|
|
15
|
+
if (typeof roh !== 'object' || roh === null)
|
|
16
|
+
return null;
|
|
17
|
+
const r = roh;
|
|
18
|
+
const topic = typeof r.topic === 'string' ? r.topic : '';
|
|
19
|
+
if (!topic)
|
|
20
|
+
return null;
|
|
21
|
+
const text = (v) => (typeof v === 'string' && v ? v : undefined);
|
|
22
|
+
const liste = (v) => Array.isArray(v) && v.length > 0 ? v.filter((x) => typeof x === 'string') : undefined;
|
|
23
|
+
return {
|
|
24
|
+
topic,
|
|
25
|
+
outcome: text(r.outcome),
|
|
26
|
+
severity: text(r.severity),
|
|
27
|
+
whatWorked: text(r.what_worked),
|
|
28
|
+
whatFailed: text(r.what_failed),
|
|
29
|
+
filePaths: liste(r.file_paths),
|
|
30
|
+
tags: liste(r.tags),
|
|
31
|
+
ts: text(r.ts),
|
|
32
|
+
author: text(r.author),
|
|
33
|
+
recallCount: typeof r.recall_count === 'number' ? r.recall_count : undefined,
|
|
34
|
+
raw: r,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
7
37
|
const UNGROUPED_HEADING = 'Ungrouped';
|
|
8
38
|
function groupByCategory(lessons) {
|
|
9
39
|
const groups = new Map();
|
|
@@ -73,7 +103,7 @@ export function buildLessonsMarkdown(lessons) {
|
|
|
73
103
|
export function buildLessonsJsonl(lessons) {
|
|
74
104
|
if (lessons.length === 0)
|
|
75
105
|
return '';
|
|
76
|
-
return lessons.map((lesson) => JSON.stringify({
|
|
106
|
+
return lessons.map((lesson) => JSON.stringify(lesson.raw ?? {
|
|
77
107
|
topic: lesson.topic,
|
|
78
108
|
outcome: lesson.outcome,
|
|
79
109
|
severity: lesson.severity,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"brain-export.js","sourceRoot":"","sources":["../../src/brain-export.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"brain-export.js","sourceRoot":"","sources":["../../src/brain-export.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deutsch für die Suche: Umlaute, Wortformen, zusammengesetzte Wörter.
|
|
3
|
+
*
|
|
4
|
+
* ── Warum es dieses Modul gibt ───────────────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* Gemessen am 19.08.2026 an 498 echten Lektionen mit 20 Fragen in Alltags-
|
|
7
|
+
* deutsch: 2 Treffer. Sechs Fragen teilten mit der richtigen Antwort NICHT EIN
|
|
8
|
+
* EINZIGES Wort. Die Aufteilung nach Ursachen (src/bench/wortschatz-oder-
|
|
9
|
+
* menge.ts) zeigte: schon bei 20 Lektionen und null Ablenkern scheitern 14 von
|
|
10
|
+
* 20. Es ist also der Wortschatz, nicht die Menge.
|
|
11
|
+
*
|
|
12
|
+
* Die Suche hatte deutsche Stoppwörter und 35 deutsche Wortpaare. Was fehlte,
|
|
13
|
+
* waren drei Dinge — und alle drei sind an echten Fragen belegt:
|
|
14
|
+
*
|
|
15
|
+
* 1. UMLAUTE. Die Wortliste stand mit echten Umlauten (`lösung`,
|
|
16
|
+
* `überwachung`), Lektionen und Fragen schreiben oft `ae/oe/ue`. Damit
|
|
17
|
+
* fand `ueberwachung` den Eintrag `überwachung` nie. Zwei Schreibweisen
|
|
18
|
+
* desselben Wortes, und die Suche hielt sie für verschieden.
|
|
19
|
+
*
|
|
20
|
+
* 2. WORTFORM. `abhaengigkeiten` gegen `abhaengigkeit`, `prozesse` gegen
|
|
21
|
+
* `prozess`. Für sieben Sprachen gab es einen Stemmer, für Deutsch nicht.
|
|
22
|
+
*
|
|
23
|
+
* 3. ZUSAMMENGESETZTE WÖRTER. Das ist die deutsche Besonderheit und war die
|
|
24
|
+
* größte Lücke. `Arbeitsspeicher` enthält `speicher`, und `speicher` steht
|
|
25
|
+
* längst in der Wortliste — nur kam niemand dort an. Genauso
|
|
26
|
+
* `Zugriffsprotokoll` → `protokoll` → `log`, `Browsertests` → `test`.
|
|
27
|
+
*
|
|
28
|
+
* ── Die Entscheidung, die dieses Modul klein hält ────────────────────────────
|
|
29
|
+
*
|
|
30
|
+
* Für die Zerlegung braucht es ein Wörterbuch der Grundwörter. Es wäre nahe-
|
|
31
|
+
* liegend, dafür eine zweite Liste zu pflegen. Genau das ist die Fehlerklasse,
|
|
32
|
+
* die uns in diesem Haus am häufigsten trifft: eine Wahrheit an zwei Orten.
|
|
33
|
+
*
|
|
34
|
+
* Deshalb: die Wortliste IST das Wörterbuch. Wer ein Wortpaar einträgt, macht
|
|
35
|
+
* es damit automatisch als Grundwort für zusammengesetzte Wörter verfügbar.
|
|
36
|
+
* Eine Liste, zwei Wirkungen.
|
|
37
|
+
*/
|
|
38
|
+
/**
|
|
39
|
+
* Schreibt Umlaute aus. Aus `ä` wird `ae`, aus `ß` wird `ss`.
|
|
40
|
+
*
|
|
41
|
+
* Beide Schreibweisen werden auf DIESELBE Form gebracht — nicht die eine in die
|
|
42
|
+
* andere übersetzt. Nur so ist `Lösung` dasselbe Wort wie `Loesung`, egal in
|
|
43
|
+
* welcher Richtung jemand tippt.
|
|
44
|
+
*/
|
|
45
|
+
export declare function ohneUmlaute(wort: string): string;
|
|
46
|
+
/**
|
|
47
|
+
* Deutsche Wortform auf einen gemeinsamen Stamm bringen.
|
|
48
|
+
*
|
|
49
|
+
* Absichtlich zurückhaltend: eine Endung, nicht mehrere hintereinander. Bei
|
|
50
|
+
* `Abhaengigkeiten` reicht `-en`, und `Abhaengigkeit` ist genau die Form, die
|
|
51
|
+
* in den Lektionen steht.
|
|
52
|
+
*/
|
|
53
|
+
export declare function deutscherStamm(wort: string): string;
|
|
54
|
+
/**
|
|
55
|
+
* Die Rückumlautung: `eintraeg` → `eintrag`, `laeuft` → `lauft`.
|
|
56
|
+
*
|
|
57
|
+
* Das Deutsche verändert im Plural und in gebeugten Formen den Stammvokal
|
|
58
|
+
* (Eintrag → Einträge). Ohne diesen Schritt bleiben Einzahl und Mehrzahl zwei
|
|
59
|
+
* verschiedene Wörter.
|
|
60
|
+
*
|
|
61
|
+
* Nur die letzte Umlautstelle wird zurückgenommen. `Ueberpruefung` soll nicht
|
|
62
|
+
* zu `Uberprufung` werden — das Wort beginnt mit einem echten `ü`, das kein
|
|
63
|
+
* Umlaut einer Mehrzahl ist.
|
|
64
|
+
*/
|
|
65
|
+
export declare function ohneRueckumlaut(stamm: string): string | null;
|
|
66
|
+
/**
|
|
67
|
+
* Zerlegt ein zusammengesetztes Wort in seine Bestandteile.
|
|
68
|
+
*
|
|
69
|
+
* `Arbeitsspeicher` → `speicher` (und `arbeit`)
|
|
70
|
+
* `Zugriffsprotokoll` → `protokoll` (und `zugriff`)
|
|
71
|
+
* `Browsertests` → `test` (und `browser`)
|
|
72
|
+
*
|
|
73
|
+
* Wie es arbeitet: Das Grundwort steht im Deutschen HINTEN. Also wird von
|
|
74
|
+
* hinten nach einem bekannten Wort gesucht. Der Rest davor wird ebenfalls
|
|
75
|
+
* zurückgegeben, wenn er lang genug ist — er trägt oft die eigentliche
|
|
76
|
+
* Bedeutung (`Arbeit` in `Arbeitsspeicher`).
|
|
77
|
+
*
|
|
78
|
+
* Das `s` in `Arbeits|speicher` ist eine Fuge und gehört zu keinem der beiden
|
|
79
|
+
* Teile. Es wird abgeschnitten.
|
|
80
|
+
*
|
|
81
|
+
* @param bekannt Grundwörter, gegen die geprüft wird. Das ist die Wortliste der
|
|
82
|
+
* Suche selbst — kein zweites Wörterbuch, das veralten kann.
|
|
83
|
+
*/
|
|
84
|
+
export declare function zerlegeKompositum(wort: string, bekannt: Set<string>): string[];
|
|
85
|
+
/**
|
|
86
|
+
* Deutsche Fachwörter, die in unseren Lektionen und Fragen vorkommen.
|
|
87
|
+
*
|
|
88
|
+
* ── Woher diese Liste kommt ──────────────────────────────────────────────────
|
|
89
|
+
*
|
|
90
|
+
* Nicht aus dem Kopf. Jeder Eintrag stammt aus der Messung vom 19.08.2026: es
|
|
91
|
+
* sind die Wörter, die in echten Fragen standen und in der richtigen Antwort
|
|
92
|
+
* keinen Anschluss fanden (src/bench/wo-bricht-die-frage.ts nennt sie).
|
|
93
|
+
*
|
|
94
|
+
* Deshalb ist die Liste kurz und wird es bleiben. Sie wächst aus Messungen,
|
|
95
|
+
* nicht aus Vermutungen — wer etwas einträgt, soll die Frage nennen können, die
|
|
96
|
+
* ohne den Eintrag danebengeht.
|
|
97
|
+
*/
|
|
98
|
+
export declare const DEUTSCHE_FACHWOERTER: ReadonlyArray<readonly [string, string[]]>;
|