Files
omnisupport-ai/services/geminiService.ts
fcarraUniSa 0102f0e285 feat: Initialize OmniSupport AI project structure
Sets up the basic project structure for OmniSupport AI, including:
- Vite build tool configuration.
- React and necessary dependencies.
- TypeScript configuration.
- Basic HTML and root component setup.
- Initial type definitions and mock data for core entities like tickets, agents, and queues.
- A README with setup instructions.
- A .gitignore file for common build artifacts and development files.
2026-02-16 16:24:31 +01:00

143 lines
5.0 KiB
TypeScript

import { GoogleGenAI } from "@google/genai";
import { KBArticle, Ticket, TicketStatus } from "../types";
const apiKey = process.env.API_KEY || '';
// Initialize properly with named parameter
const ai = new GoogleGenAI({ apiKey });
/**
* Agent 1: Customer Support Chat
* Uses the KB to answer questions.
* SUPPORTS MULTI-LANGUAGE via Prompt Engineering.
*/
export const getSupportResponse = async (
userQuery: string,
chatHistory: string[],
knowledgeBase: KBArticle[]
): Promise<string> => {
if (!apiKey) return "API Key mancante. Configura l'ambiente.";
// Prepare Context from KB
const kbContext = knowledgeBase.map(a => {
// Note: We now include 'content' even for URLs because we scrape the text into it.
if (a.type === 'url') {
return `Fonte Esterna [${a.category}]: ${a.title} - URL: ${a.url}\nContenuto Estratto: ${a.content}`;
}
return `Articolo [${a.category}]: ${a.title}\nContenuto: ${a.content}`;
}).join('\n\n');
const systemInstruction = `
Sei "OmniSupport AI", un assistente clienti virtuale globale.
IL TUO COMPITO:
Rispondere alle domande dei clienti basandoti ESCLUSIVAMENTE sulla seguente Base di Conoscenza (KB) fornita in ITALIANO.
GESTIONE LINGUA (IMPORTANTE):
1. Rileva automaticamente la lingua utilizzata dall'utente nel suo ultimo messaggio.
2. Anche se la KB è in Italiano, devi tradurre mentalmente la richiesta, cercare la risposta nella KB Italiana, e poi RISPONDERE NELLA LINGUA DELL'UTENTE.
3. Esempio: Se l'utente scrive in Inglese "How do I reset my password?", tu cerchi "Come reimpostare la password" nella KB e rispondi in Inglese.
BASE DI CONOSCENZA (ITALIANO):
${kbContext}
REGOLE:
1. Se la risposta è nella KB, forniscila.
2. Se l'articolo è una fonte web (URL), usa il "Contenuto Estratto" per rispondere e fornisci anche il link originale all'utente.
3. Se la risposta NON si trova nella KB, ammettilo gentilmente (nella lingua dell'utente) e consiglia di aprire un ticket.
4. Sii cortese, professionale e sintetico.
`;
try {
const response = await ai.models.generateContent({
model: 'gemini-3-flash-preview',
contents: [
...chatHistory.map(msg => ({ role: 'user', parts: [{ text: msg }] })),
{ role: 'user', parts: [{ text: userQuery }] }
],
config: {
systemInstruction: systemInstruction,
temperature: 0.3,
}
});
return response.text || "I apologize, I cannot process your request at the moment / Mi dispiace, non riesco a rispondere al momento.";
} catch (error) {
console.error("Gemini Error:", error);
return "Service Error / Errore del servizio.";
}
};
/**
* Agent 2: Knowledge Extraction
* Scans resolved tickets to find gaps in KB and drafts new articles.
*/
export const generateNewKBArticle = async (
resolvedTickets: Ticket[],
existingArticles: KBArticle[]
): Promise<{ title: string; content: string; category: string } | null> => {
if (!apiKey) return null;
// Filter only resolved tickets
const relevantTickets = resolvedTickets.filter(t => t.status === TicketStatus.RESOLVED);
if (relevantTickets.length === 0) return null;
// Aggregate ticket conversations
const transcripts = relevantTickets.map(t => {
const convo = t.messages.map(m => `${m.role.toUpperCase()}: ${m.content}`).join('\n');
return `TICKET ID: ${t.id}\nOGGETTO: ${t.subject}\nCONVERSAZIONE:\n${convo}\n---`;
}).join('\n');
const existingTitles = existingArticles.map(a => a.title).join(', ');
const prompt = `
Sei un Knowledge Manager AI esperto.
Analizza i seguenti ticket risolti e confrontali con gli articoli esistenti nella Knowledge Base.
ARTICOLI ESISTENTI: ${existingTitles}
TICKET RISOLTI RECENTI:
${transcripts}
OBIETTIVO:
1. Identifica un problema ricorrente o una soluzione tecnica presente nei ticket risolti MA NON coperta dagli articoli esistenti.
2. Se trovi una lacuna, scrivi un NUOVO articolo di Knowledge Base per colmarla.
3. Restituisci il risultato ESCLUSIVAMENTE in formato JSON.
SCHEMA JSON RICHIESTO:
{
"foundGap": boolean,
"title": "Titolo del nuovo articolo",
"content": "Contenuto dettagliato in formato Markdown",
"category": "Categoria suggerita (es. Tecnico, Amministrazione, Account)"
}
Se non trovi nulla di rilevante o nuovo, imposta "foundGap" a false.
`;
try {
const response = await ai.models.generateContent({
model: 'gemini-3-pro-preview', // Pro model for better reasoning
contents: prompt,
config: {
responseMimeType: "application/json"
}
});
const text = response.text;
if (!text) return null;
const result = JSON.parse(text);
if (result.foundGap) {
return {
title: result.title,
content: result.content,
category: result.category
};
}
return null;
} catch (error) {
console.error("Knowledge Agent Error:", error);
return null;
}
};