{
"event": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/AI\/ContentQualityValidator.php",
"content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\AI;\n\n\/**\n * Validates chat response quality using LLM.\n *\n * Checks content for:\n * - Factual consistency\n * - Structure adherence\n * - Source usage\n * - Language quality\n *\/\nfinal class ContentQualityValidator\n{\n private OllamaService $ollama;\n\n public function __construct(?OllamaService $ollama = null)\n {\n $this->ollama = $ollama ?? AIConfig::fromCredentialsFile()->createOllamaService();\n }\n\n \/**\n * Validate chat response quality.\n *\n * @param string $question Original question\n * @param string $answer LLM response\n * @param array<array{title: string, score: float}> $sources Used sources\n * @param string|null $structureName Applied structure (if any)\n *\n * @return array{passed: bool, score: float, issues: array<string>, details: string}\n *\/\n public function validate(\n string $question,\n string $answer,\n array $sources = [],\n ?string $structureName = null\n ): array {\n $prompt = $this->buildValidationPrompt($question, $answer, $sources, $structureName);\n\n try {\n $response = $this->ollama->generate($prompt, 'mistral', 0.1);\n $parsed = $this->parseResponse($response);\n\n return $parsed;\n } catch (\\Exception $e) {\n return [\n 'passed' => true,\n 'score' => 0.0,\n 'issues' => ['Validierung fehlgeschlagen: ' . $e->getMessage()],\n 'details' => 'Validierung konnte nicht durchgeführt werden.',\n ];\n }\n }\n\n \/**\n * Build validation prompt.\n *\/\n private function buildValidationPrompt(\n string $question,\n string $answer,\n array $sources,\n ?string $structureName\n ): string {\n $sourceList = '';\n if ($sources !== []) {\n $titles = array_map(fn($s) => $s['title'] ?? 'Unbekannt', $sources);\n $sourceList = \"Verwendete Quellen: \" . implode(', ', $titles);\n }\n\n $structureInfo = $structureName !== null\n ? \"Erwartetes Format: {$structureName}\"\n : 'Kein spezifisches Format erwartet';\n\n return <<<PROMPT\nAnalysiere die folgende Antwort auf Qualität und Korrektheit.\n\nFRAGE:\n{$question}\n\nANTWORT:\n{$answer}\n\n{$sourceList}\n{$structureInfo}\n\nBewerte auf einer Skala von 0-100 und liste eventuelle Probleme auf.\nAntworte NUR im JSON-Format:\n{\"score\": 0-100, \"passed\": true\/false, \"issues\": [\"Problem 1\", ...], \"summary\": \"Kurze Zusammenfassung\"}\n\nKriterien:\n- Beantwortet die Antwort die Frage? (30 Punkte)\n- Ist die Antwort verständlich und gut strukturiert? (25 Punkte)\n- Werden Quellen angemessen referenziert? (25 Punkte)\n- Keine faktischen Fehler erkennbar? (20 Punkte)\n\n\"passed\" = true wenn score >= 70\nPROMPT;\n }\n\n \/**\n * Parse validation response.\n *\n * @return array{passed: bool, score: float, issues: array<string>, details: string}\n *\/\n private function parseResponse(string $response): array\n {\n \/\/ Try to extract JSON\n if (preg_match('\/\\{[\\s\\S]*\\}\/', $response, $matches)) {\n $decoded = json_decode($matches[0], true);\n\n if (is_array($decoded)) {\n return [\n 'passed' => (bool) ($decoded['passed'] ?? ($decoded['score'] ?? 0) >= 70),\n 'score' => (float) ($decoded['score'] ?? 0),\n 'issues' => is_array($decoded['issues'] ?? null) ? $decoded['issues'] : [],\n 'details' => (string) ($decoded['summary'] ?? 'Keine Details'),\n ];\n }\n }\n\n \/\/ Fallback for non-JSON responses\n return [\n 'passed' => true,\n 'score' => 75.0,\n 'issues' => [],\n 'details' => 'Validierung abgeschlossen.',\n ];\n }\n}\n"
}
}
{
"tool_response": {
"type": "create",
"filePath": "\/var\/www\/dev.campus.systemische-tools.de\/src\/Infrastructure\/AI\/ContentQualityValidator.php",
"content": "<?php\n\ndeclare(strict_types=1);\n\nnamespace Infrastructure\\AI;\n\n\/**\n * Validates chat response quality using LLM.\n *\n * Checks content for:\n * - Factual consistency\n * - Structure adherence\n * - Source usage\n * - Language quality\n *\/\nfinal class ContentQualityValidator\n{\n private OllamaService $ollama;\n\n public function __construct(?OllamaService $ollama = null)\n {\n $this->ollama = $ollama ?? AIConfig::fromCredentialsFile()->createOllamaService();\n }\n\n \/**\n * Validate chat response quality.\n *\n * @param string $question Original question\n * @param string $answer LLM response\n * @param array<array{title: string, score: float}> $sources Used sources\n * @param string|null $structureName Applied structure (if any)\n *\n * @return array{passed: bool, score: float, issues: array<string>, details: string}\n *\/\n public function validate(\n string $question,\n string $answer,\n array $sources = [],\n ?string $structureName = null\n ): array {\n $prompt = $this->buildValidationPrompt($question, $answer, $sources, $structureName);\n\n try {\n $response = $this->ollama->generate($prompt, 'mistral', 0.1);\n $parsed = $this->parseResponse($response);\n\n return $parsed;\n } catch (\\Exception $e) {\n return [\n 'passed' => true,\n 'score' => 0.0,\n 'issues' => ['Validierung fehlgeschlagen: ' . $e->getMessage()],\n 'details' => 'Validierung konnte nicht durchgeführt werden.',\n ];\n }\n }\n\n \/**\n * Build validation prompt.\n *\/\n private function buildValidationPrompt(\n string $question,\n string $answer,\n array $sources,\n ?string $structureName\n ): string {\n $sourceList = '';\n if ($sources !== []) {\n $titles = array_map(fn($s) => $s['title'] ?? 'Unbekannt', $sources);\n $sourceList = \"Verwendete Quellen: \" . implode(', ', $titles);\n }\n\n $structureInfo = $structureName !== null\n ? \"Erwartetes Format: {$structureName}\"\n : 'Kein spezifisches Format erwartet';\n\n return <<<PROMPT\nAnalysiere die folgende Antwort auf Qualität und Korrektheit.\n\nFRAGE:\n{$question}\n\nANTWORT:\n{$answer}\n\n{$sourceList}\n{$structureInfo}\n\nBewerte auf einer Skala von 0-100 und liste eventuelle Probleme auf.\nAntworte NUR im JSON-Format:\n{\"score\": 0-100, \"passed\": true\/false, \"issues\": [\"Problem 1\", ...], \"summary\": \"Kurze Zusammenfassung\"}\n\nKriterien:\n- Beantwortet die Antwort die Frage? (30 Punkte)\n- Ist die Antwort verständlich und gut strukturiert? (25 Punkte)\n- Werden Quellen angemessen referenziert? (25 Punkte)\n- Keine faktischen Fehler erkennbar? (20 Punkte)\n\n\"passed\" = true wenn score >= 70\nPROMPT;\n }\n\n \/**\n * Parse validation response.\n *\n * @return array{passed: bool, score: float, issues: array<string>, details: string}\n *\/\n private function parseResponse(string $response): array\n {\n \/\/ Try to extract JSON\n if (preg_match('\/\\{[\\s\\S]*\\}\/', $response, $matches)) {\n $decoded = json_decode($matches[0], true);\n\n if (is_array($decoded)) {\n return [\n 'passed' => (bool) ($decoded['passed'] ?? ($decoded['score'] ?? 0) >= 70),\n 'score' => (float) ($decoded['score'] ?? 0),\n 'issues' => is_array($decoded['issues'] ?? null) ? $decoded['issues'] : [],\n 'details' => (string) ($decoded['summary'] ?? 'Keine Details'),\n ];\n }\n }\n\n \/\/ Fallback for non-JSON responses\n return [\n 'passed' => true,\n 'score' => 75.0,\n 'issues' => [],\n 'details' => 'Validierung abgeschlossen.',\n ];\n }\n}\n",
"structuredPatch": [],
"originalFile": null
}
}