Files
Ninjaserver/config/llm-blacklist.js

254 lines
7.4 KiB
JavaScript

/**
* 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
};