nis2-agile/application/services/RagService.php
DevEnv nis2-agile a7a21faa82 [FEAT] Knowledge Base RAG multi-livello (SYSTEM/FIRM/ORG) + Qdrant + Voyage
- KnowledgeBaseController: ingest, list, firmOrgs, search, delete
- VectorService (Qdrant + buildAuthzFilter), EmbedService (Voyage), RagService (pipeline)
- AIService::askWithRag con fallback graceful
- docker-compose: servizio qdrant + env Voyage (chiave da .env/vault, no hardcoded)
- SQL 012 consulting_firms, 013 firm_assignments + kb_uploaded_documents
- public/kb.html + kb.js (upload, lista, search preview)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 15:44:13 +02:00

65 lines
2.0 KiB
PHP

<?php
/**
* NIS2 Agile - RagService
*
* Combina EmbedService + VectorService per cercare nella KB multi-livello
* filtrando per il contesto utente (Migration 012-014).
*/
require_once __DIR__ . '/VectorService.php';
require_once __DIR__ . '/EmbedService.php';
class RagService
{
private VectorService $vector;
private EmbedService $embed;
public function __construct()
{
$this->vector = new VectorService();
$this->embed = new EmbedService();
}
/**
* Cerca i top-k chunks visibili all'utente.
*
* @param array $userContext ['user_id', 'organization_id', 'consulting_firm_id']
* @return array Lista chunks con title, content, score, scope
*/
public function searchForUser(string $question, array $userContext, int $topK = 5, float $minScore = 0.28): array
{
$vector = $this->embed->embed($question);
$filter = VectorService::buildAuthzFilter($userContext);
$hits = $this->vector->search($vector, $filter, $topK, $minScore);
$out = [];
foreach ($hits as $h) {
$p = $h['payload'] ?? [];
$out[] = [
'id' => $h['id'] ?? null,
'score' => round($h['score'] ?? 0, 4),
'title' => $p['title'] ?? '',
'content' => $p['chunk'] ?? '',
'scope' => $p['scope'] ?? null,
'source' => $p['source'] ?? null,
'lang' => $p['lang'] ?? 'it',
];
}
return $out;
}
/**
* Compatta i risultati in un blocco di testo da iniettare nel system prompt Claude.
*/
public function formatContext(array $hits): string
{
if (empty($hits)) return '';
$blocks = [];
foreach ($hits as $i => $h) {
$idx = $i + 1;
$blocks[] = "[$idx] {$h['title']} (scope={$h['scope']}, score={$h['score']})\n{$h['content']}";
}
return implode("\n\n---\n\n", $blocks);
}
}