KI-Feinschliff, Volltextsuche über E-Mails, Prompt für die Antwort-Mail
Drei Erweiterungen, die sich server.js und lib/documents.js teilen und deshalb zusammen liegen: Jobsuche: Der Feinschliff (Zusatzbegriffe/Ausschluesse) laesst sich per KI aus den eigenen Basis-Unterlagen vorschlagen. Der Rollen-Prompt ist wie die uebrigen unter Vorlagen editierbar; gespeichert wird erst auf Klick. Suche: Startseite und KI-Chat durchsuchen jetzt per SQLite-FTS5 auch die E-Mail-Korrespondenz (Absender, Betreff, Text), Notizen und Stellenbeschreibung - ein Name aus einer Mail findet die Bewerbung. Ranking per bm25, Fundstellen mit Snippet, Feldfilter (firma:, von:), Phrasen, Ausschluesse, Umlaut-Varianten (mueller/muller/mueller). Der Index wird von SQL-Triggern gepflegt, weil Bewerbungen und Mails aus UI, REST-API, IMAP und KI-Import geschrieben werden. E-Mail: Vor dem Generieren laesst sich vorgeben, was in der Antwort stehen soll; die Vorgabe geht als verbindliche Anweisung in den Prompt. Steht schon ein Entwurf im Feld, ueberarbeitet die KI ihn, statt neu anzufangen. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+73
-3
@@ -105,9 +105,27 @@
|
||||
</section>
|
||||
|
||||
<section class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6">
|
||||
<h3 class="text-base font-semibold text-gray-800 dark:text-white">Feinschliff</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1 mb-5">
|
||||
Optional. Rollen und Technologien kommen aus deinem Lebenslauf – hier ergänzt du nur Leitplanken.
|
||||
<div class="flex flex-wrap items-start justify-between gap-4">
|
||||
<div>
|
||||
<h3 class="text-base font-semibold text-gray-800 dark:text-white">Feinschliff</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 mt-1">
|
||||
Optional. Rollen und Technologien kommen aus deinem Lebenslauf – hier ergänzt du nur Leitplanken.
|
||||
</p>
|
||||
</div>
|
||||
<button type="button" id="feinschliffKi"
|
||||
<%= (!hatUnterlagen || !hatOllamaKey) ? 'disabled' : '' %>
|
||||
title="<%= !hatUnterlagen ? 'Dafür fehlen Basis-Unterlagen unter Vorlagen.' : (!hatOllamaKey ? 'Dafür fehlt der Ollama-API-Schlüssel in den Einstellungen.' : 'Leitet Vorschläge aus deinen Basis-Unterlagen ab.') %>"
|
||||
class="inline-flex items-center gap-2 px-3 py-1.5 shrink-0 text-sm border border-blue-600 text-blue-600 dark:border-blue-400 dark:text-blue-400 rounded-md transition-colors hover:bg-blue-50 dark:hover:bg-blue-900/30 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent">
|
||||
<svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 3v4M3 5h4M6 17v4m-2-2h4m5-16l2.286 6.857L21 12l-5.714 2.143L13 21l-2.286-6.857L5 12l5.714-2.143L13 3z"></path>
|
||||
</svg>
|
||||
<span id="feinschliffKiText">Per KI vorschlagen</span>
|
||||
</button>
|
||||
</div>
|
||||
<p id="feinschliffHinweis" class="hidden text-sm mt-3"></p>
|
||||
<p class="text-xs text-gray-400 dark:text-gray-500 mt-3 mb-5">
|
||||
Der KI-Vorschlag wertet deine <a href="/vorlagen" class="underline">Basis-Unterlagen</a> aus und
|
||||
überschreibt beide Felder. Gespeichert wird er erst, wenn du unten auf „Speichern“ klickst.
|
||||
</p>
|
||||
|
||||
<div class="grid gap-4 sm:grid-cols-2">
|
||||
@@ -223,6 +241,58 @@
|
||||
const initial = <%- JSON.stringify(laeufe.map(function (l) { return { status: l.status }; })) %>;
|
||||
if (offen(initial)) timer = setInterval(poll, 5000);
|
||||
})();
|
||||
|
||||
// "Per KI vorschlagen": lets the model derive the two guard-rail lists from the
|
||||
// user's Basis-Unterlagen. Only fills the fields — saving stays a deliberate act.
|
||||
(function () {
|
||||
const btn = document.getElementById('feinschliffKi');
|
||||
if (!btn) return;
|
||||
const label = document.getElementById('feinschliffKiText');
|
||||
const hinweis = document.getElementById('feinschliffHinweis');
|
||||
const zusatz = document.getElementById('zusatz_begriffe');
|
||||
const ausschluesse = document.getElementById('ausschluesse');
|
||||
const zielUser = <%- fremd ? zielUser.id : 'null' %>;
|
||||
|
||||
const FARBE = {
|
||||
info: 'text-gray-500 dark:text-gray-400',
|
||||
ok: 'text-green-700 dark:text-green-400',
|
||||
fehler: 'text-red-600 dark:text-red-400',
|
||||
};
|
||||
function melde(text, art) {
|
||||
hinweis.textContent = text;
|
||||
hinweis.className = 'text-sm mt-3 ' + FARBE[art];
|
||||
}
|
||||
|
||||
btn.addEventListener('click', async function () {
|
||||
btn.disabled = true;
|
||||
label.textContent = 'KI denkt nach …';
|
||||
melde('Die KI liest deine Basis-Unterlagen – das dauert einen Moment.', 'info');
|
||||
|
||||
const modus = document.querySelector('input[name="modus"]:checked');
|
||||
try {
|
||||
const res = await fetch('/jobsuche/feinschliff', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
ziel_user: zielUser,
|
||||
modus: modus ? modus.value : '',
|
||||
staedte: document.getElementById('staedte').value,
|
||||
}),
|
||||
});
|
||||
const data = await res.json().catch(function () { return {}; });
|
||||
if (!res.ok) throw new Error(data.error || ('Fehler ' + res.status));
|
||||
|
||||
zusatz.value = data.zusatz_begriffe || '';
|
||||
ausschluesse.value = data.ausschluesse || '';
|
||||
melde('Vorschlag übernommen. Prüfe ihn und klicke auf „Speichern“.', 'ok');
|
||||
} catch (e) {
|
||||
melde(e.message || 'Der KI-Vorschlag ist fehlgeschlagen.', 'fehler');
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
label.textContent = 'Per KI vorschlagen';
|
||||
}
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user