@cachly-dev/mcp-server 0.10.124 → 0.10.126
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 +536 -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 +272 -26
- 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/versuch.d.ts +179 -0
- package/dist/src/versuch.js +250 -0
- package/dist/src/versuch.js.map +1 -0
- 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,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lesson-preview — one place that decides how a lesson is shown in a briefing.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS (measured 2026-08-16, real incident):
|
|
5
|
+
* A session briefing showed this lesson line:
|
|
6
|
+
*
|
|
7
|
+
* ✅🔴 `node4:einrichtung-contabo-und-fallen` — node-4 (Contabo, 169.58.175.157,
|
|
8
|
+
* Lauterbourg FR, 12 Kerne / 48 GB / 400 GB, 25,29 EUR/Monat) eingeri
|
|
9
|
+
*
|
|
10
|
+
* The reader needed one thing from that lesson: the WireGuard address of the
|
|
11
|
+
* machine. It sits at character 323 of `what_worked`. The preview cut at 100.
|
|
12
|
+
* Worse, the lesson's `what_failed` field held the literal sentence "always read
|
|
13
|
+
* `wg show wg0 allowed-ips`" — and no briefing surface renders `what_failed` for
|
|
14
|
+
* a lesson whose outcome is "success". The lesson was displayed, at the right
|
|
15
|
+
* moment, and the exact mistake it warns about happened anyway.
|
|
16
|
+
*
|
|
17
|
+
* The fix is not "show more prose". The two most actionable fields of that
|
|
18
|
+
* lesson were already short and structured:
|
|
19
|
+
*
|
|
20
|
+
* commands: ["wg show wg0 allowed-ips", "ssh -i ~/.ssh/... -p 2222 root@..."]
|
|
21
|
+
* file_paths: ["/etc/wireguard/wg0.conf", ...]
|
|
22
|
+
*
|
|
23
|
+
* ~15 tokens that answer the question, thrown away in favour of 100 characters
|
|
24
|
+
* of preamble. So a preview renders three things now: trimmed prose, a facts
|
|
25
|
+
* tail drawn from the structured fields, and — for critical lessons — the
|
|
26
|
+
* warning, regardless of outcome.
|
|
27
|
+
*
|
|
28
|
+
* This helps the EXISTING corpus. A writing rule ("put the fact first") only
|
|
29
|
+
* improves lessons written after it; reading structured fields improves every
|
|
30
|
+
* lesson ever stored.
|
|
31
|
+
*/
|
|
32
|
+
export interface PreviewLesson {
|
|
33
|
+
topic: string;
|
|
34
|
+
outcome?: string;
|
|
35
|
+
severity?: string;
|
|
36
|
+
what_worked?: string;
|
|
37
|
+
what_failed?: string;
|
|
38
|
+
commands?: string[];
|
|
39
|
+
file_paths?: string[];
|
|
40
|
+
}
|
|
41
|
+
export interface PreviewOptions {
|
|
42
|
+
/** Prose budget for what_worked. Default 100 — the historical briefing value. */
|
|
43
|
+
maxChars?: number;
|
|
44
|
+
/** Render the commands/paths tail. Default true. */
|
|
45
|
+
facts?: boolean;
|
|
46
|
+
/** Render the warning line for critical lessons. Default true. */
|
|
47
|
+
warning?: boolean;
|
|
48
|
+
/** How many commands + paths to show. Default 2 each. */
|
|
49
|
+
maxFacts?: number;
|
|
50
|
+
/** Per-fact character cap. Default 64. */
|
|
51
|
+
factChars?: number;
|
|
52
|
+
/** Character cap for the warning line. Default 110. */
|
|
53
|
+
warningChars?: number;
|
|
54
|
+
/** Indent for continuation lines. Default 5 spaces (matches briefing lists). */
|
|
55
|
+
indent?: string;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Cut to `max` characters, preferring a word boundary, and mark the cut.
|
|
59
|
+
* The marker matters: without it a truncated line reads like a complete one,
|
|
60
|
+
* and the reader never learns there is more to fetch.
|
|
61
|
+
*/
|
|
62
|
+
export declare function trimTo(text: string, max: number): string;
|
|
63
|
+
/**
|
|
64
|
+
* Facts worth showing even when the prose is cut: commands first (they are
|
|
65
|
+
* directly runnable), then file paths. Deduplicated, order preserved.
|
|
66
|
+
*/
|
|
67
|
+
export declare function factsOf(l: PreviewLesson, maxFacts: number, factChars: number): string[];
|
|
68
|
+
/**
|
|
69
|
+
* The warning is the single most fear-inducing, mistake-preventing part of a
|
|
70
|
+
* lesson — and it was invisible whenever `outcome` was "success". Show it for
|
|
71
|
+
* critical lessons no matter how the lesson ended.
|
|
72
|
+
*/
|
|
73
|
+
export declare function warningOf(l: PreviewLesson, max: number): string | null;
|
|
74
|
+
/**
|
|
75
|
+
* Render the body of one briefing entry: prose, then facts, then warning.
|
|
76
|
+
* Returns lines WITHOUT the caller's bullet/emoji prefix — the caller owns the
|
|
77
|
+
* first line's prefix, this owns everything after the em dash.
|
|
78
|
+
*/
|
|
79
|
+
export declare function lessonPreviewLines(l: PreviewLesson, opts?: PreviewOptions): string[];
|
|
80
|
+
/** Convenience: the same content as a single newline-joined string. */
|
|
81
|
+
export declare function lessonPreview(l: PreviewLesson, opts?: PreviewOptions): string;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* lesson-preview — one place that decides how a lesson is shown in a briefing.
|
|
3
|
+
*
|
|
4
|
+
* WHY THIS EXISTS (measured 2026-08-16, real incident):
|
|
5
|
+
* A session briefing showed this lesson line:
|
|
6
|
+
*
|
|
7
|
+
* ✅🔴 `node4:einrichtung-contabo-und-fallen` — node-4 (Contabo, 169.58.175.157,
|
|
8
|
+
* Lauterbourg FR, 12 Kerne / 48 GB / 400 GB, 25,29 EUR/Monat) eingeri
|
|
9
|
+
*
|
|
10
|
+
* The reader needed one thing from that lesson: the WireGuard address of the
|
|
11
|
+
* machine. It sits at character 323 of `what_worked`. The preview cut at 100.
|
|
12
|
+
* Worse, the lesson's `what_failed` field held the literal sentence "always read
|
|
13
|
+
* `wg show wg0 allowed-ips`" — and no briefing surface renders `what_failed` for
|
|
14
|
+
* a lesson whose outcome is "success". The lesson was displayed, at the right
|
|
15
|
+
* moment, and the exact mistake it warns about happened anyway.
|
|
16
|
+
*
|
|
17
|
+
* The fix is not "show more prose". The two most actionable fields of that
|
|
18
|
+
* lesson were already short and structured:
|
|
19
|
+
*
|
|
20
|
+
* commands: ["wg show wg0 allowed-ips", "ssh -i ~/.ssh/... -p 2222 root@..."]
|
|
21
|
+
* file_paths: ["/etc/wireguard/wg0.conf", ...]
|
|
22
|
+
*
|
|
23
|
+
* ~15 tokens that answer the question, thrown away in favour of 100 characters
|
|
24
|
+
* of preamble. So a preview renders three things now: trimmed prose, a facts
|
|
25
|
+
* tail drawn from the structured fields, and — for critical lessons — the
|
|
26
|
+
* warning, regardless of outcome.
|
|
27
|
+
*
|
|
28
|
+
* This helps the EXISTING corpus. A writing rule ("put the fact first") only
|
|
29
|
+
* improves lessons written after it; reading structured fields improves every
|
|
30
|
+
* lesson ever stored.
|
|
31
|
+
*/
|
|
32
|
+
const DEFAULTS = {
|
|
33
|
+
maxChars: 100,
|
|
34
|
+
facts: true,
|
|
35
|
+
warning: true,
|
|
36
|
+
maxFacts: 2,
|
|
37
|
+
factChars: 64,
|
|
38
|
+
warningChars: 110,
|
|
39
|
+
indent: ' ',
|
|
40
|
+
};
|
|
41
|
+
/** Collapse whitespace so a multi-line field cannot break list layout. */
|
|
42
|
+
function flat(s) {
|
|
43
|
+
return String(s ?? '').replace(/\s+/g, ' ').trim();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Cut to `max` characters, preferring a word boundary, and mark the cut.
|
|
47
|
+
* The marker matters: without it a truncated line reads like a complete one,
|
|
48
|
+
* and the reader never learns there is more to fetch.
|
|
49
|
+
*/
|
|
50
|
+
export function trimTo(text, max) {
|
|
51
|
+
const s = flat(text);
|
|
52
|
+
if (max <= 0)
|
|
53
|
+
return '';
|
|
54
|
+
if (s.length <= max)
|
|
55
|
+
return s;
|
|
56
|
+
const hard = s.slice(0, max);
|
|
57
|
+
const space = hard.lastIndexOf(' ');
|
|
58
|
+
// Only honour the word boundary if it does not throw away most of the budget.
|
|
59
|
+
const body = space > max * 0.6 ? hard.slice(0, space) : hard;
|
|
60
|
+
return `${body.replace(/[\s,;:.-]+$/, '')}…`;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Facts worth showing even when the prose is cut: commands first (they are
|
|
64
|
+
* directly runnable), then file paths. Deduplicated, order preserved.
|
|
65
|
+
*/
|
|
66
|
+
export function factsOf(l, maxFacts, factChars) {
|
|
67
|
+
const out = [];
|
|
68
|
+
const seen = new Set();
|
|
69
|
+
const take = (raw, prefix, limit) => {
|
|
70
|
+
let n = 0;
|
|
71
|
+
for (const item of Array.isArray(raw) ? raw : []) {
|
|
72
|
+
if (n >= limit)
|
|
73
|
+
break;
|
|
74
|
+
const v = flat(item);
|
|
75
|
+
if (!v)
|
|
76
|
+
continue;
|
|
77
|
+
const key = `${prefix}${v}`;
|
|
78
|
+
if (seen.has(key))
|
|
79
|
+
continue;
|
|
80
|
+
seen.add(key);
|
|
81
|
+
out.push(`${prefix}${trimTo(v, factChars)}`);
|
|
82
|
+
n++;
|
|
83
|
+
}
|
|
84
|
+
};
|
|
85
|
+
take(l.commands, '⌘ ', maxFacts);
|
|
86
|
+
take(l.file_paths, '▸ ', maxFacts);
|
|
87
|
+
return out;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The warning is the single most fear-inducing, mistake-preventing part of a
|
|
91
|
+
* lesson — and it was invisible whenever `outcome` was "success". Show it for
|
|
92
|
+
* critical lessons no matter how the lesson ended.
|
|
93
|
+
*/
|
|
94
|
+
export function warningOf(l, max) {
|
|
95
|
+
const w = flat(l.what_failed);
|
|
96
|
+
if (!w)
|
|
97
|
+
return null;
|
|
98
|
+
const isCritical = String(l.severity ?? '').toLowerCase() === 'critical';
|
|
99
|
+
const failed = l.outcome === 'failure' || l.outcome === 'partial';
|
|
100
|
+
if (!isCritical && !failed)
|
|
101
|
+
return null;
|
|
102
|
+
return trimTo(w, max);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Render the body of one briefing entry: prose, then facts, then warning.
|
|
106
|
+
* Returns lines WITHOUT the caller's bullet/emoji prefix — the caller owns the
|
|
107
|
+
* first line's prefix, this owns everything after the em dash.
|
|
108
|
+
*/
|
|
109
|
+
export function lessonPreviewLines(l, opts = {}) {
|
|
110
|
+
const o = { ...DEFAULTS, ...opts };
|
|
111
|
+
const lines = [];
|
|
112
|
+
const prose = trimTo(l.what_worked ?? '', o.maxChars);
|
|
113
|
+
if (prose)
|
|
114
|
+
lines.push(prose);
|
|
115
|
+
if (o.warning) {
|
|
116
|
+
const w = warningOf(l, o.warningChars);
|
|
117
|
+
if (w)
|
|
118
|
+
lines.push(`${o.indent}⚠️ ${w}`);
|
|
119
|
+
}
|
|
120
|
+
if (o.facts) {
|
|
121
|
+
const f = factsOf(l, o.maxFacts, o.factChars);
|
|
122
|
+
// One line, not one per fact: a briefing lists many lessons, and a
|
|
123
|
+
// four-line block per lesson would push the interesting ones off screen.
|
|
124
|
+
if (f.length)
|
|
125
|
+
lines.push(`${o.indent}${f.join(' ')}`);
|
|
126
|
+
}
|
|
127
|
+
return lines;
|
|
128
|
+
}
|
|
129
|
+
/** Convenience: the same content as a single newline-joined string. */
|
|
130
|
+
export function lessonPreview(l, opts = {}) {
|
|
131
|
+
return lessonPreviewLines(l, opts).join('\n');
|
|
132
|
+
}
|
|
133
|
+
//# sourceMappingURL=lesson-preview.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lesson-preview.js","sourceRoot":"","sources":["../../src/lesson-preview.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Der zusammengesetzte Sortierer.
|
|
3
|
+
*
|
|
4
|
+
* ── Was hier drinsteckt, und was NICHT ──────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* Am 19.08.2026 wurden an 499 echten Lektionen mit 100 Fragen ein Dutzend
|
|
7
|
+
* Verfahren durchgemessen. Die meisten haben nichts gebracht. Sie stehen hier,
|
|
8
|
+
* damit sie niemand ein zweites Mal baut:
|
|
9
|
+
*
|
|
10
|
+
* Symptom-Sicht (what_failed als eigener Vektor) 17 % statt 40 % — das Feld
|
|
11
|
+
* beschreibt bei uns nicht das Symptom, sondern was vergeblich versucht wurde
|
|
12
|
+
* Schwerpunkt abziehen 41 % — im Rauschen
|
|
13
|
+
* Hauptrichtungen entfernen (1, 3, 6 Stueck) 37–41 % — im Rauschen
|
|
14
|
+
* Nabenabzug (CSLS-artig) 41 % — im Rauschen
|
|
15
|
+
* Text in Stuecke schneiden nicht gebaut: gemessen deckt
|
|
16
|
+
* das beste Stueck genauso viel ab wie der ganze Text (Verhaeltnis 1,00),
|
|
17
|
+
* und es liegt bei 86 von 96 Fragen ohnehin am Anfang
|
|
18
|
+
*
|
|
19
|
+
* Was BLEIBT, sind vier Bestandteile — kreuzweise geprueft, also auf der einen
|
|
20
|
+
* Haelfte der Fragen eingestellt und auf der anderen gemessen, in beide
|
|
21
|
+
* Richtungen:
|
|
22
|
+
*
|
|
23
|
+
* P@1 MRR
|
|
24
|
+
* nur Bedeutung 40 % 51,3 %
|
|
25
|
+
* zusammengesetzt 51 % 57,0 %
|
|
26
|
+
*
|
|
27
|
+
* ── Die vier Bestandteile ───────────────────────────────────────────────────
|
|
28
|
+
*
|
|
29
|
+
* 1. NAEHE ZUM GANZEN TEXT (Sicht A). Der Grundstock.
|
|
30
|
+
*
|
|
31
|
+
* 2. NAEHE ZUM THEMENNAMEN (Sicht C). Der Name ist kurz wie eine Frage — der
|
|
32
|
+
* Vergleich ist damit symmetrisch, waehrend eine 60-Zeichen-Frage gegen
|
|
33
|
+
* 1376 Zeichen Text es nicht ist. Allein liefert diese Sicht nur 26 %, im
|
|
34
|
+
* Verbund traegt sie verlaesslich (Gewicht 0,6 in beiden Richtungen).
|
|
35
|
+
*
|
|
36
|
+
* 3. RUECKKOPPLUNG. Die ersten drei Treffer sind meistens ungefaehr richtig.
|
|
37
|
+
* Ihre Vektoren werden zu einem Viertel unter die Frage gemischt, dann wird
|
|
38
|
+
* erneut verglichen. Kostet nichts — keine neue Einbettung, nur eine
|
|
39
|
+
* Mittelung vorhandener Zahlen.
|
|
40
|
+
*
|
|
41
|
+
* 4. SELTENHEITS-UEBERLAPPUNG. Welcher Anteil der SELTENEN Fragewoerter kommt
|
|
42
|
+
* im Text vor. Ohne Laengennormierung, ohne unscharfe Treffer, ohne
|
|
43
|
+
* Bigramme.
|
|
44
|
+
*
|
|
45
|
+
* Das ist der unbequeme Befund: dieses eine Verhaeltnis bekam beim Einstellen
|
|
46
|
+
* das Gewicht 1,0 bis 1,6 — unser ausgefeiltes BM25 mit Laengennormierung,
|
|
47
|
+
* Bigramm-Naehe, unscharfen Treffern und Frischebonus bekam NULL. Nicht weil
|
|
48
|
+
* Woerter nichts taugen, sondern weil das ganze Beiwerk beim Sortieren
|
|
49
|
+
* daneben misst.
|
|
50
|
+
*
|
|
51
|
+
* ── Warum innerhalb des Topfes normiert wird ────────────────────────────────
|
|
52
|
+
*
|
|
53
|
+
* Ueber den ganzen Bestand liegen alle Aehnlichkeiten zwischen 0,42 und 0,61 —
|
|
54
|
+
* ein sehr schmales Band, weil alle Vektoren eine gemeinsame Grundrichtung
|
|
55
|
+
* teilen (gemessen: 69,8 Prozent). Unter fuenfzig Kandidaten spreizt eine
|
|
56
|
+
* Normierung auf 0 bis 1 genau die Unterschiede auf, um die es geht. Das ist
|
|
57
|
+
* dasselbe Ziel wie beim Schwerpunkt-Abziehen — nur dort, wo es wirkt.
|
|
58
|
+
*/
|
|
59
|
+
/** Wörter eines Textes, umlautfrei, ohne Kurzwörter. */
|
|
60
|
+
export declare function inhaltsWoerter(text: string): Set<string>;
|
|
61
|
+
/**
|
|
62
|
+
* Es gibt hier KEINE Stammbildung — und das ist gemessen, nicht vergessen.
|
|
63
|
+
*
|
|
64
|
+
* Naheliegend wäre, `Abhaengigkeiten` und `Abhaengigkeit` auf einen Nenner zu
|
|
65
|
+
* bringen. Drei Varianten wurden am 19.08.2026 an 499 Lektionen mit 100 Fragen
|
|
66
|
+
* kreuzweise durchgemessen:
|
|
67
|
+
*
|
|
68
|
+
* ohne Stammbildung P@1 50 % MRR 56,4 %
|
|
69
|
+
* erste 6 Zeichen P@1 47 % MRR 54,7 %
|
|
70
|
+
* erste 8 Zeichen P@1 47 % MRR 54,7 %
|
|
71
|
+
*
|
|
72
|
+
* Jede Form von Kürzen kostet drei Punkte. Der Grund liegt daran, was diese
|
|
73
|
+
* Suche trägt: seltene Fachwörter. `fail2ban`, `stampede`, `timestamptz`,
|
|
74
|
+
* `keycloak` entscheiden eine Frage praktisch allein — und jede Verschmelzung
|
|
75
|
+
* verwischt genau sie. `container` und `contains` werden zu demselben Wort,
|
|
76
|
+
* und aus zwei seltenen wird ein häufiges.
|
|
77
|
+
*
|
|
78
|
+
* Beugung einzufangen lohnt sich also nur, wo die häufigen Wörter zählen. Hier
|
|
79
|
+
* zählen die seltenen.
|
|
80
|
+
*
|
|
81
|
+
* (Die Wortform-Behandlung in deutsch.ts bleibt davon unberührt — sie wirkt im
|
|
82
|
+
* Wortabgleich, der die Vorauswahl macht, nicht in dieser Bewertung.)
|
|
83
|
+
*/
|
|
84
|
+
export declare function grobStamm(w: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Die Seltenheit jedes Wortes im Bestand — einmal berechnet, oft benutzt.
|
|
87
|
+
*
|
|
88
|
+
* Trifft eine Frage das Wort "nicht", sagt das nichts. Trifft sie "fail2ban",
|
|
89
|
+
* ist die Sache praktisch entschieden. Ohne diese Gewichtung sind beide gleich
|
|
90
|
+
* viel wert.
|
|
91
|
+
*/
|
|
92
|
+
export declare class Seltenheit {
|
|
93
|
+
private df;
|
|
94
|
+
private anzahl;
|
|
95
|
+
/** @param texte je ein Text pro Datensatz */
|
|
96
|
+
constructor(texte: string[]);
|
|
97
|
+
wert(wort: string): number;
|
|
98
|
+
/**
|
|
99
|
+
* Welcher Anteil der seltenen Fragewörter kommt im Text vor.
|
|
100
|
+
*
|
|
101
|
+
* Der Nenner ist die Summe über ALLE Fragewörter — ein fehlendes seltenes
|
|
102
|
+
* Wort zieht damit den Wert herunter. Fehlende Belege zählen also mit, nicht
|
|
103
|
+
* nur vorhandene.
|
|
104
|
+
*/
|
|
105
|
+
deckung(frageWoerter: Set<string>, textWoerter: Set<string>): number;
|
|
106
|
+
}
|
|
107
|
+
/** Auf 0 bis 1 spreizen — innerhalb des Topfes, nicht über den Bestand. */
|
|
108
|
+
export declare function spreizeImTopf(werte: number[]): number[];
|
|
109
|
+
/**
|
|
110
|
+
* Die Frage mit ihren besten Treffern anreichern.
|
|
111
|
+
*
|
|
112
|
+
* Die Frage benutzt Alltagsworte, die Lektionen Fachworte. Die ersten Treffer
|
|
113
|
+
* schlagen diese Brücke: ihre Vektoren zeigen dorthin, wo die Antwort liegt.
|
|
114
|
+
*
|
|
115
|
+
* Das Risiko ist bekannt und begrenzt: sind die ersten Treffer falsch, zieht
|
|
116
|
+
* die Anreicherung in die falsche Richtung. Deshalb bleibt die ursprüngliche
|
|
117
|
+
* Frage mit drei Vierteln stehen — gemessen war das besser als jede stärkere
|
|
118
|
+
* Beimischung.
|
|
119
|
+
*/
|
|
120
|
+
export declare function reichereAn(frage: number[], besteTreffer: number[][], anteil?: number): number[];
|
|
121
|
+
/**
|
|
122
|
+
* Die Gewichte.
|
|
123
|
+
*
|
|
124
|
+
* Kreuzweise ermittelt: auf der einen Hälfte der Fragen eingestellt, auf der
|
|
125
|
+
* anderen gemessen, in beide Richtungen. Die drei Werte hier waren in BEIDEN
|
|
126
|
+
* Richtungen stabil. Zwei weitere Merkmale — Überlappung mit dem Themennamen
|
|
127
|
+
* und Qualitätsmerkmale — schwankten zwischen den Richtungen und sind deshalb
|
|
128
|
+
* NICHT dabei: was nur in einer Hälfte hilft, ist eine Eigenschaft dieser
|
|
129
|
+
* Hälfte, keine des Verfahrens.
|
|
130
|
+
*/
|
|
131
|
+
export declare const GEWICHTE: {
|
|
132
|
+
/** Nähe zum ganzen Text. Der Grundstock, deshalb 1. */
|
|
133
|
+
readonly text: 1;
|
|
134
|
+
/** Nähe zum Themennamen. In beiden Richtungen 0,6. */
|
|
135
|
+
readonly thema: 0.6;
|
|
136
|
+
/** Angereicherte Frage gegen den ganzen Text. In beiden Richtungen 0,3. */
|
|
137
|
+
readonly rueckkopplung: 0.3;
|
|
138
|
+
/** Anteil der seltenen Fragewörter im Text. 1,0 bis 1,6 — Mitte genommen. */
|
|
139
|
+
readonly seltenheit: 1.3;
|
|
140
|
+
};
|
|
141
|
+
export interface Bewertbar {
|
|
142
|
+
/** Kosinus zur Sicht über den ganzen Text, oder -2 wenn kein Vektor da ist. */
|
|
143
|
+
naeheText: number;
|
|
144
|
+
/** Kosinus zur Sicht über den Themennamen. */
|
|
145
|
+
naeheThema: number;
|
|
146
|
+
/** Kosinus der angereicherten Frage zum ganzen Text. */
|
|
147
|
+
naeheRueckkopplung: number;
|
|
148
|
+
/** Anteil der seltenen Fragewörter, die im Text vorkommen. */
|
|
149
|
+
seltenheitsDeckung: number;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Punktzahl je Kandidat. Höher ist besser.
|
|
153
|
+
*
|
|
154
|
+
* Erwartet den GANZEN Topf auf einmal, weil die Normierung ihn braucht — eine
|
|
155
|
+
* Punktzahl für einen einzelnen Kandidaten gibt es nicht und kann es nicht
|
|
156
|
+
* geben.
|
|
157
|
+
*/
|
|
158
|
+
export declare function bewerteTopf(topf: Bewertbar[], gewichte?: {
|
|
159
|
+
/** Nähe zum ganzen Text. Der Grundstock, deshalb 1. */
|
|
160
|
+
readonly text: 1;
|
|
161
|
+
/** Nähe zum Themennamen. In beiden Richtungen 0,6. */
|
|
162
|
+
readonly thema: 0.6;
|
|
163
|
+
/** Angereicherte Frage gegen den ganzen Text. In beiden Richtungen 0,3. */
|
|
164
|
+
readonly rueckkopplung: 0.3;
|
|
165
|
+
/** Anteil der seltenen Fragewörter im Text. 1,0 bis 1,6 — Mitte genommen. */
|
|
166
|
+
readonly seltenheit: 1.3;
|
|
167
|
+
}): number[];
|
|
168
|
+
/** Bequemlichkeit: Kosinus mit Lücken-Behandlung. */
|
|
169
|
+
export declare function naeheOderLuecke(frage: number[] | null, vektor: number[] | null | undefined): number;
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Der zusammengesetzte Sortierer.
|
|
3
|
+
*
|
|
4
|
+
* ── Was hier drinsteckt, und was NICHT ──────────────────────────────────────
|
|
5
|
+
*
|
|
6
|
+
* Am 19.08.2026 wurden an 499 echten Lektionen mit 100 Fragen ein Dutzend
|
|
7
|
+
* Verfahren durchgemessen. Die meisten haben nichts gebracht. Sie stehen hier,
|
|
8
|
+
* damit sie niemand ein zweites Mal baut:
|
|
9
|
+
*
|
|
10
|
+
* Symptom-Sicht (what_failed als eigener Vektor) 17 % statt 40 % — das Feld
|
|
11
|
+
* beschreibt bei uns nicht das Symptom, sondern was vergeblich versucht wurde
|
|
12
|
+
* Schwerpunkt abziehen 41 % — im Rauschen
|
|
13
|
+
* Hauptrichtungen entfernen (1, 3, 6 Stueck) 37–41 % — im Rauschen
|
|
14
|
+
* Nabenabzug (CSLS-artig) 41 % — im Rauschen
|
|
15
|
+
* Text in Stuecke schneiden nicht gebaut: gemessen deckt
|
|
16
|
+
* das beste Stueck genauso viel ab wie der ganze Text (Verhaeltnis 1,00),
|
|
17
|
+
* und es liegt bei 86 von 96 Fragen ohnehin am Anfang
|
|
18
|
+
*
|
|
19
|
+
* Was BLEIBT, sind vier Bestandteile — kreuzweise geprueft, also auf der einen
|
|
20
|
+
* Haelfte der Fragen eingestellt und auf der anderen gemessen, in beide
|
|
21
|
+
* Richtungen:
|
|
22
|
+
*
|
|
23
|
+
* P@1 MRR
|
|
24
|
+
* nur Bedeutung 40 % 51,3 %
|
|
25
|
+
* zusammengesetzt 51 % 57,0 %
|
|
26
|
+
*
|
|
27
|
+
* ── Die vier Bestandteile ───────────────────────────────────────────────────
|
|
28
|
+
*
|
|
29
|
+
* 1. NAEHE ZUM GANZEN TEXT (Sicht A). Der Grundstock.
|
|
30
|
+
*
|
|
31
|
+
* 2. NAEHE ZUM THEMENNAMEN (Sicht C). Der Name ist kurz wie eine Frage — der
|
|
32
|
+
* Vergleich ist damit symmetrisch, waehrend eine 60-Zeichen-Frage gegen
|
|
33
|
+
* 1376 Zeichen Text es nicht ist. Allein liefert diese Sicht nur 26 %, im
|
|
34
|
+
* Verbund traegt sie verlaesslich (Gewicht 0,6 in beiden Richtungen).
|
|
35
|
+
*
|
|
36
|
+
* 3. RUECKKOPPLUNG. Die ersten drei Treffer sind meistens ungefaehr richtig.
|
|
37
|
+
* Ihre Vektoren werden zu einem Viertel unter die Frage gemischt, dann wird
|
|
38
|
+
* erneut verglichen. Kostet nichts — keine neue Einbettung, nur eine
|
|
39
|
+
* Mittelung vorhandener Zahlen.
|
|
40
|
+
*
|
|
41
|
+
* 4. SELTENHEITS-UEBERLAPPUNG. Welcher Anteil der SELTENEN Fragewoerter kommt
|
|
42
|
+
* im Text vor. Ohne Laengennormierung, ohne unscharfe Treffer, ohne
|
|
43
|
+
* Bigramme.
|
|
44
|
+
*
|
|
45
|
+
* Das ist der unbequeme Befund: dieses eine Verhaeltnis bekam beim Einstellen
|
|
46
|
+
* das Gewicht 1,0 bis 1,6 — unser ausgefeiltes BM25 mit Laengennormierung,
|
|
47
|
+
* Bigramm-Naehe, unscharfen Treffern und Frischebonus bekam NULL. Nicht weil
|
|
48
|
+
* Woerter nichts taugen, sondern weil das ganze Beiwerk beim Sortieren
|
|
49
|
+
* daneben misst.
|
|
50
|
+
*
|
|
51
|
+
* ── Warum innerhalb des Topfes normiert wird ────────────────────────────────
|
|
52
|
+
*
|
|
53
|
+
* Ueber den ganzen Bestand liegen alle Aehnlichkeiten zwischen 0,42 und 0,61 —
|
|
54
|
+
* ein sehr schmales Band, weil alle Vektoren eine gemeinsame Grundrichtung
|
|
55
|
+
* teilen (gemessen: 69,8 Prozent). Unter fuenfzig Kandidaten spreizt eine
|
|
56
|
+
* Normierung auf 0 bis 1 genau die Unterschiede auf, um die es geht. Das ist
|
|
57
|
+
* dasselbe Ziel wie beim Schwerpunkt-Abziehen — nur dort, wo es wirkt.
|
|
58
|
+
*/
|
|
59
|
+
import { kosinus } from './bedeutung.js';
|
|
60
|
+
/** Wörter eines Textes, umlautfrei, ohne Kurzwörter. */
|
|
61
|
+
export function inhaltsWoerter(text) {
|
|
62
|
+
return new Set(text.toLowerCase()
|
|
63
|
+
.replace(/ä/g, 'ae').replace(/ö/g, 'oe').replace(/ü/g, 'ue').replace(/ß/g, 'ss')
|
|
64
|
+
.split(/[^a-z0-9]+/)
|
|
65
|
+
.filter((w) => w.length > 3));
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Es gibt hier KEINE Stammbildung — und das ist gemessen, nicht vergessen.
|
|
69
|
+
*
|
|
70
|
+
* Naheliegend wäre, `Abhaengigkeiten` und `Abhaengigkeit` auf einen Nenner zu
|
|
71
|
+
* bringen. Drei Varianten wurden am 19.08.2026 an 499 Lektionen mit 100 Fragen
|
|
72
|
+
* kreuzweise durchgemessen:
|
|
73
|
+
*
|
|
74
|
+
* ohne Stammbildung P@1 50 % MRR 56,4 %
|
|
75
|
+
* erste 6 Zeichen P@1 47 % MRR 54,7 %
|
|
76
|
+
* erste 8 Zeichen P@1 47 % MRR 54,7 %
|
|
77
|
+
*
|
|
78
|
+
* Jede Form von Kürzen kostet drei Punkte. Der Grund liegt daran, was diese
|
|
79
|
+
* Suche trägt: seltene Fachwörter. `fail2ban`, `stampede`, `timestamptz`,
|
|
80
|
+
* `keycloak` entscheiden eine Frage praktisch allein — und jede Verschmelzung
|
|
81
|
+
* verwischt genau sie. `container` und `contains` werden zu demselben Wort,
|
|
82
|
+
* und aus zwei seltenen wird ein häufiges.
|
|
83
|
+
*
|
|
84
|
+
* Beugung einzufangen lohnt sich also nur, wo die häufigen Wörter zählen. Hier
|
|
85
|
+
* zählen die seltenen.
|
|
86
|
+
*
|
|
87
|
+
* (Die Wortform-Behandlung in deutsch.ts bleibt davon unberührt — sie wirkt im
|
|
88
|
+
* Wortabgleich, der die Vorauswahl macht, nicht in dieser Bewertung.)
|
|
89
|
+
*/
|
|
90
|
+
export function grobStamm(w) {
|
|
91
|
+
return w;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Die Seltenheit jedes Wortes im Bestand — einmal berechnet, oft benutzt.
|
|
95
|
+
*
|
|
96
|
+
* Trifft eine Frage das Wort "nicht", sagt das nichts. Trifft sie "fail2ban",
|
|
97
|
+
* ist die Sache praktisch entschieden. Ohne diese Gewichtung sind beide gleich
|
|
98
|
+
* viel wert.
|
|
99
|
+
*/
|
|
100
|
+
export class Seltenheit {
|
|
101
|
+
df = new Map();
|
|
102
|
+
anzahl = 0;
|
|
103
|
+
/** @param texte je ein Text pro Datensatz */
|
|
104
|
+
constructor(texte) {
|
|
105
|
+
this.anzahl = texte.length;
|
|
106
|
+
for (const t of texte) {
|
|
107
|
+
for (const w of inhaltsWoerter(t))
|
|
108
|
+
this.df.set(grobStamm(w), (this.df.get(grobStamm(w)) ?? 0) + 1);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
wert(wort) {
|
|
112
|
+
return Math.log((this.anzahl + 1) / ((this.df.get(grobStamm(wort)) ?? 0) + 1));
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Welcher Anteil der seltenen Fragewörter kommt im Text vor.
|
|
116
|
+
*
|
|
117
|
+
* Der Nenner ist die Summe über ALLE Fragewörter — ein fehlendes seltenes
|
|
118
|
+
* Wort zieht damit den Wert herunter. Fehlende Belege zählen also mit, nicht
|
|
119
|
+
* nur vorhandene.
|
|
120
|
+
*/
|
|
121
|
+
deckung(frageWoerter, textWoerter) {
|
|
122
|
+
let gesamt = 0;
|
|
123
|
+
let getroffen = 0;
|
|
124
|
+
for (const w of frageWoerter) {
|
|
125
|
+
const g = this.wert(w);
|
|
126
|
+
gesamt += g;
|
|
127
|
+
if (textWoerter.has(grobStamm(w)))
|
|
128
|
+
getroffen += g;
|
|
129
|
+
}
|
|
130
|
+
return gesamt > 0 ? getroffen / gesamt : 0;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
/** Auf 0 bis 1 spreizen — innerhalb des Topfes, nicht über den Bestand. */
|
|
134
|
+
export function spreizeImTopf(werte) {
|
|
135
|
+
const gueltig = werte.filter((x) => Number.isFinite(x) && x > -2);
|
|
136
|
+
if (gueltig.length === 0)
|
|
137
|
+
return werte.map(() => 0);
|
|
138
|
+
let min = Infinity;
|
|
139
|
+
let max = -Infinity;
|
|
140
|
+
for (const x of gueltig) {
|
|
141
|
+
if (x < min)
|
|
142
|
+
min = x;
|
|
143
|
+
if (x > max)
|
|
144
|
+
max = x;
|
|
145
|
+
}
|
|
146
|
+
if (max === min)
|
|
147
|
+
return werte.map(() => 0.5);
|
|
148
|
+
return werte.map((x) => (x > -2 ? (x - min) / (max - min) : 0));
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Die Frage mit ihren besten Treffern anreichern.
|
|
152
|
+
*
|
|
153
|
+
* Die Frage benutzt Alltagsworte, die Lektionen Fachworte. Die ersten Treffer
|
|
154
|
+
* schlagen diese Brücke: ihre Vektoren zeigen dorthin, wo die Antwort liegt.
|
|
155
|
+
*
|
|
156
|
+
* Das Risiko ist bekannt und begrenzt: sind die ersten Treffer falsch, zieht
|
|
157
|
+
* die Anreicherung in die falsche Richtung. Deshalb bleibt die ursprüngliche
|
|
158
|
+
* Frage mit drei Vierteln stehen — gemessen war das besser als jede stärkere
|
|
159
|
+
* Beimischung.
|
|
160
|
+
*/
|
|
161
|
+
export function reichereAn(frage, besteTreffer, anteil = 0.25) {
|
|
162
|
+
if (besteTreffer.length === 0)
|
|
163
|
+
return frage;
|
|
164
|
+
const d = frage.length;
|
|
165
|
+
const m = new Array(d).fill(0);
|
|
166
|
+
for (const v of besteTreffer) {
|
|
167
|
+
if (v.length !== d)
|
|
168
|
+
continue;
|
|
169
|
+
for (let i = 0; i < d; i++)
|
|
170
|
+
m[i] += v[i] / besteTreffer.length;
|
|
171
|
+
}
|
|
172
|
+
return frage.map((x, i) => (1 - anteil) * x + anteil * m[i]);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Die Gewichte.
|
|
176
|
+
*
|
|
177
|
+
* Kreuzweise ermittelt: auf der einen Hälfte der Fragen eingestellt, auf der
|
|
178
|
+
* anderen gemessen, in beide Richtungen. Die drei Werte hier waren in BEIDEN
|
|
179
|
+
* Richtungen stabil. Zwei weitere Merkmale — Überlappung mit dem Themennamen
|
|
180
|
+
* und Qualitätsmerkmale — schwankten zwischen den Richtungen und sind deshalb
|
|
181
|
+
* NICHT dabei: was nur in einer Hälfte hilft, ist eine Eigenschaft dieser
|
|
182
|
+
* Hälfte, keine des Verfahrens.
|
|
183
|
+
*/
|
|
184
|
+
export const GEWICHTE = {
|
|
185
|
+
/** Nähe zum ganzen Text. Der Grundstock, deshalb 1. */
|
|
186
|
+
text: 1,
|
|
187
|
+
/** Nähe zum Themennamen. In beiden Richtungen 0,6. */
|
|
188
|
+
thema: 0.6,
|
|
189
|
+
/** Angereicherte Frage gegen den ganzen Text. In beiden Richtungen 0,3. */
|
|
190
|
+
rueckkopplung: 0.3,
|
|
191
|
+
/** Anteil der seltenen Fragewörter im Text. 1,0 bis 1,6 — Mitte genommen. */
|
|
192
|
+
seltenheit: 1.3,
|
|
193
|
+
};
|
|
194
|
+
/**
|
|
195
|
+
* Punktzahl je Kandidat. Höher ist besser.
|
|
196
|
+
*
|
|
197
|
+
* Erwartet den GANZEN Topf auf einmal, weil die Normierung ihn braucht — eine
|
|
198
|
+
* Punktzahl für einen einzelnen Kandidaten gibt es nicht und kann es nicht
|
|
199
|
+
* geben.
|
|
200
|
+
*/
|
|
201
|
+
export function bewerteTopf(topf, gewichte = GEWICHTE) {
|
|
202
|
+
const t = spreizeImTopf(topf.map((k) => k.naeheText));
|
|
203
|
+
const th = spreizeImTopf(topf.map((k) => k.naeheThema));
|
|
204
|
+
const r = spreizeImTopf(topf.map((k) => k.naeheRueckkopplung));
|
|
205
|
+
return topf.map((k, i) => gewichte.text * t[i]
|
|
206
|
+
+ gewichte.thema * th[i]
|
|
207
|
+
+ gewichte.rueckkopplung * r[i]
|
|
208
|
+
+ gewichte.seltenheit * k.seltenheitsDeckung);
|
|
209
|
+
}
|
|
210
|
+
/** Bequemlichkeit: Kosinus mit Lücken-Behandlung. */
|
|
211
|
+
export function naeheOderLuecke(frage, vektor) {
|
|
212
|
+
return frage && vektor && vektor.length > 0 ? kosinus(frage, vektor) : -2;
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=rangfolge.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rangfolge.js","sourceRoot":"","sources":["../../src/rangfolge.ts"],"names":[],"mappings":"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"}
|