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); } }