KI-Chat: Web-Recherche ueber Ollama Web Search

Der Assistent kannte bisher nur die Datenbank. Fragen wie "Was muss ich
ueber die Firma wissen?" konnte er damit nicht beantworten - Groesse,
Produkte, News, Kultur stehen nirgends in der Bewerbung.

Zwei neue Werkzeuge (web_suche, web_seite_lesen) ueber die Web-Search-API
von ollama.com. Sie authentifizieren sich mit demselben OLLAMA_API_KEY,
den der Benutzer fuer Chat und Dokumente ohnehin hinterlegt hat: keine
zusaetzliche Konfiguration. Anders als /api/chat haengt die Websuche
nicht an OLLAMA_HOST - wer rein lokal chattet, hat schlicht keine
Websuche, der Rest laeuft unveraendert weiter.

- Tool-Runden 4 -> 8: Bewerbung holen, suchen, Seiten lesen, antworten
  lief vorher ins Limit, statt zu antworten.
- Antworten der Websuche hart gedeckelt, sie wandern sonst in jeder
  weiteren Runde erneut ins Kontextfenster.
- Status-Label zeigt, wonach gesucht wird - sonst sieht der Nutzer nicht,
  ob die richtige Firma erwischt wurde.
- Nackte URLs im Chat sind klickbar: Belege nennt das Modell meist als
  reine URL, nicht als Markdown-Link.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-14 12:02:29 +02:00
co-authored by Claude Opus 4.8
parent 9fc4225204
commit 4a036fe215
4 changed files with 188 additions and 6 deletions
+23 -3
View File
@@ -28,17 +28,37 @@
// targets are restricted to http(s)/mailto/relative so a `javascript:` URL
// from the model cannot become a clickable XSS vector, and the URL is stripped
// of characters that could break out of the href attribute.
function link(url, label) {
const safe = String(url).trim().replace(/["'<>`\\]/g, '');
return `<a href="${safe}" target="_blank" rel="noopener noreferrer">${label}</a>`;
}
function inlineFmt(t) {
return t
// Fertige <a>-Tags werden geparkt, damit die Autolink-Regel unten nicht in
// eine bereits gebaute href hineinläuft und den Tag zerlegt.
const parked = [];
const park = (html) => `\u0000${parked.push(html) - 1}\u0000`;
let s = t
.replace(/`([^`]+)`/g, '<code>$1</code>')
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
.replace(/(^|[^*])\*([^*]+)\*/g, '$1<em>$2</em>')
.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, (m, label, url) => {
const u = String(url).trim();
if (u && !/^(https?:|mailto:|\/|#)/i.test(u)) return m;
const safe = u.replace(/["'<>`\\]/g, '');
return `<a href="${safe}" target="_blank" rel="noopener noreferrer">${label}</a>`;
return park(link(u, label));
});
// Nackte Quellen-URLs klickbar machen — bei Web-Recherchen nennt der
// Assistent Belege oft als reine URL, nicht als Markdown-Link. Nur http(s),
// und Satzzeichen am Ende gehören zum Satz, nicht zur Adresse.
s = s.replace(/https?:\/\/[^\s<>"'`\u0000]+/g, (u) => {
const m = u.match(/[.,;:!?)\]]+$/);
const rein = m ? u.slice(0, -m[0].length) : u;
return park(link(rein, rein)) + (m ? m[0] : '');
});
return s.replace(/\u0000(\d+)\u0000/g, (m, i) => parked[Number(i)]);
}
function renderMarkdown(src) {