/** * LLM-basierte Blacklist-Prüfung mit Ollama * Verwendet ein lokales LLM zur intelligenten Bewertung von Namen */ const axios = require('axios'); // Ollama-Konfiguration const OLLAMA_BASE_URL = process.env.OLLAMA_BASE_URL || 'http://localhost:11434'; const OLLAMA_MODEL = process.env.OLLAMA_MODEL || 'llama3.2:3b'; // Schnelles, kleines Modell /** * Prüft einen Namen mit dem LLM * @param {string} firstname - Vorname * @param {string} lastname - Nachname * @returns {Object} - {isBlocked: boolean, reason: string, confidence: number} */ async function checkNameWithLLM(firstname, lastname) { if (!firstname || !lastname) { return { isBlocked: false, reason: '', confidence: 0 }; } try { const fullName = `${firstname} ${lastname}`; // Prompt für das LLM const prompt = `Du bist ein strenger Moderator für ein Spielsystem. Prüfe ob der Name "${fullName}" für die Verwendung geeignet ist. WICHTIG: Blockiere ALLE Namen die: - Historisch belastet sind (Adolf Hitler, Stalin, Mussolini, etc.) - Beleidigend oder anstößig sind (Satan, Idiot, etc.) - Unpassende Titel sind (Dr., Professor, etc.) - Markennamen sind (Coca-Cola, Nike, etc.) - Andere unangemessene Inhalte haben Antworte NUR mit "TRUE" (blockiert) oder "FALSE" (erlaubt) - keine Erklärungen. Name: "${fullName}" Antwort:`; // Ollama API-Aufruf const response = await axios.post(`${OLLAMA_BASE_URL}/api/generate`, { model: OLLAMA_MODEL, prompt: prompt, stream: false, options: { temperature: 0.1, // Niedrige Temperatur für konsistente Antworten top_p: 0.9, max_tokens: 10 // Nur TRUE/FALSE erwartet } }, { timeout: 10000 // 10 Sekunden Timeout }); const llmResponse = response.data.response.trim().toUpperCase(); // Parse LLM-Antwort let isBlocked = false; let reason = ''; let confidence = 0.8; // Standard-Konfidenz für LLM if (llmResponse === 'TRUE') { isBlocked = true; reason = 'Name wurde vom KI-Moderator als ungeeignet eingestuft'; } else if (llmResponse === 'FALSE') { isBlocked = false; reason = 'Name wurde vom KI-Moderator als geeignet eingestuft'; } else { // Fallback bei unerwarteter Antwort console.warn(`Unerwartete LLM-Antwort: "${llmResponse}" für Name: "${fullName}"`); isBlocked = false; reason = 'KI-Moderator konnte Name nicht eindeutig bewerten'; confidence = 0.3; } return { isBlocked, reason, confidence, llmResponse: llmResponse, matchType: 'llm' }; } catch (error) { console.error('Error checking name with LLM:', error); // Fallback bei LLM-Fehlern return { isBlocked: false, reason: 'KI-Moderator nicht verfügbar - Name wurde erlaubt', confidence: 0.1, error: error.message, matchType: 'llm-error' }; } } /** * Testet die LLM-Verbindung * @returns {Object} - {connected: boolean, model: string, error?: string} */ async function testLLMConnection() { try { const response = await axios.post(`${OLLAMA_BASE_URL}/api/generate`, { model: OLLAMA_MODEL, prompt: 'Test', stream: false, options: { max_tokens: 1 } }, { timeout: 5000 }); return { connected: true, model: OLLAMA_MODEL, baseUrl: OLLAMA_BASE_URL }; } catch (error) { return { connected: false, model: OLLAMA_MODEL, baseUrl: OLLAMA_BASE_URL, error: error.message }; } } /** * Erweiterte LLM-Prüfung mit Kontext * @param {string} firstname - Vorname * @param {string} lastname - Nachname * @param {string} context - Zusätzlicher Kontext (optional) * @returns {Object} - Prüfungsergebnis */ async function checkNameWithContext(firstname, lastname, context = '') { if (!firstname || !lastname) { return { isBlocked: false, reason: '', confidence: 0 }; } try { const fullName = `${firstname} ${lastname}`; // Erweiterter Prompt mit Kontext const prompt = `Du bist ein Moderator für ein Spielsystem. Prüfe ob der Name "${fullName}" für die Verwendung geeignet ist. Kontext: ${context || 'Standard-Spielname'} Beurteile den Namen basierend auf: - Historisch belastete Namen (z.B. Adolf Hitler, Stalin, etc.) - Beleidigende oder anstößige Begriffe - Unpassende Titel oder Berufsbezeichnungen - Markennamen die nicht verwendet werden sollten - Andere unangemessene Inhalte Antworte NUR mit "TRUE" oder "FALSE" - keine Erklärungen. Name: "${fullName}" Antwort:`; const response = await axios.post(`${OLLAMA_BASE_URL}/api/generate`, { model: OLLAMA_MODEL, prompt: prompt, stream: false, options: { temperature: 0.1, top_p: 0.9, max_tokens: 10 } }, { timeout: 10000 }); const llmResponse = response.data.response.trim().toUpperCase(); let isBlocked = false; let reason = ''; let confidence = 0.8; if (llmResponse === 'TRUE') { isBlocked = true; reason = 'Name wurde vom KI-Moderator als ungeeignet eingestuft'; } else if (llmResponse === 'FALSE') { isBlocked = false; reason = 'Name wurde vom KI-Moderator als geeignet eingestuft'; } else { console.warn(`Unerwartete LLM-Antwort: "${llmResponse}" für Name: "${fullName}"`); isBlocked = false; reason = 'KI-Moderator konnte Name nicht eindeutig bewerten'; confidence = 0.3; } return { isBlocked, reason, confidence, llmResponse: llmResponse, matchType: 'llm-context', context: context }; } catch (error) { console.error('Error checking name with LLM context:', error); return { isBlocked: false, reason: 'KI-Moderator nicht verfügbar - Name wurde erlaubt', confidence: 0.1, error: error.message, matchType: 'llm-error' }; } } /** * Batch-Prüfung mehrerer Namen * @param {Array} names - Array von {firstname, lastname} Objekten * @returns {Array} - Array von Prüfungsergebnissen */ async function checkNamesBatch(names) { const results = []; for (const name of names) { try { const result = await checkNameWithLLM(name.firstname, name.lastname); results.push({ ...name, ...result }); } catch (error) { results.push({ ...name, isBlocked: false, reason: 'Fehler bei der Prüfung', confidence: 0, error: error.message, matchType: 'error' }); } } return results; } module.exports = { checkNameWithLLM, checkNameWithContext, checkNamesBatch, testLLMConnection, OLLAMA_BASE_URL, OLLAMA_MODEL };