Spaces:
Sleeping
Sleeping
| """ | |
| Tutti i componenti AI: Azure, RAG e CrewAI. | |
| """ | |
| import re | |
| from typing import Dict, List | |
| import streamlit as st | |
| from openai import AzureOpenAI | |
| # LangChain imports | |
| from langchain_text_splitters import CharacterTextSplitter | |
| from langchain_openai import AzureOpenAIEmbeddings, AzureChatOpenAI | |
| from langchain_community.vectorstores import FAISS | |
| from langchain.chains import RetrievalQA | |
| from langchain_core.prompts import PromptTemplate | |
| # CrewAI imports | |
| from crewai import Agent, Task, Crew | |
| from crewai.llm import LLM | |
| from config import Config | |
| class AzureProcessor: | |
| """Processore Azure OpenAI""" | |
| def __init__(self): | |
| self.client = None | |
| self.setup_client() | |
| def setup_client(self): | |
| """Setup client Azure""" | |
| if Config.AZURE_API_KEY and Config.AZURE_ENDPOINT: | |
| try: | |
| self.client = AzureOpenAI( | |
| api_key=Config.AZURE_API_KEY, | |
| api_version=Config.AZURE_API_VERSION, | |
| azure_endpoint=Config.AZURE_ENDPOINT | |
| ) | |
| except Exception as e: | |
| st.error(f"Errore Azure OpenAI: {e}") | |
| self.client = None | |
| else: | |
| st.warning("Credenziali Azure OpenAI non trovate.") | |
| def process_document(self, anonymized_text: str) -> str: | |
| """Processa documento con AI""" | |
| if not self.client: | |
| return "Azure OpenAI non configurato." | |
| try: | |
| messages = [ | |
| { | |
| "role": "system", | |
| "content": ( | |
| "Analizza il documento anonimizzato e fornisci:\n" | |
| "1. Tipo di documento\n" | |
| "2. Riepilogo (max 5 righe)\n" | |
| "3. Analisi semantica (temi, sentiment)\n" | |
| "4. Risposta suggerita se è comunicazione cliente\n" | |
| "Usa solo i contenuti del documento fornito." | |
| ) | |
| }, | |
| { | |
| "role": "user", | |
| "content": f"Analizza questo documento:\n\n{anonymized_text}" | |
| } | |
| ] | |
| response = self.client.chat.completions.create( | |
| model=Config.DEPLOYMENT_NAME, | |
| messages=messages, | |
| max_tokens=800, | |
| temperature=0.7 | |
| ) | |
| return response.choices[0].message.content | |
| except Exception as e: | |
| return f"Errore analisi AI: {e}" | |
| class RAGChatbot: | |
| """Chatbot RAG con LangChain""" | |
| def __init__(self): | |
| self.vector_store = None | |
| self.qa_chain = None | |
| self.embeddings = None | |
| self.llm = None | |
| self.setup_langchain_components() | |
| def setup_langchain_components(self): | |
| """Setup componenti LangChain""" | |
| if not (Config.AZURE_API_KEY and Config.AZURE_ENDPOINT and | |
| Config.AZURE_EMBEDDING_API_KEY and Config.AZURE_EMBEDDING_ENDPOINT): | |
| st.warning("Credenziali Azure incomplete. RAG non disponibile.") | |
| return | |
| try: | |
| # Embeddings | |
| self.embeddings = AzureOpenAIEmbeddings( | |
| model=Config.AZURE_EMBEDDING_DEPLOYMENT_NAME, | |
| api_version=Config.AZURE_API_VERSION, | |
| azure_endpoint=Config.AZURE_EMBEDDING_ENDPOINT, | |
| api_key=Config.AZURE_EMBEDDING_API_KEY, | |
| chunk_size=16 | |
| ) | |
| # LLM | |
| self.llm = AzureChatOpenAI( | |
| deployment_name=Config.DEPLOYMENT_NAME, | |
| azure_endpoint=Config.AZURE_ENDPOINT, | |
| api_key=Config.AZURE_API_KEY, | |
| api_version=Config.AZURE_API_VERSION, | |
| temperature=0.2 | |
| ) | |
| except Exception as e: | |
| st.error(f"Errore setup LangChain: {e}") | |
| self.embeddings = None | |
| self.llm = None | |
| def build_vector_store(self, anonymized_docs: Dict[str, Dict]): | |
| """Costruisce vector store FAISS""" | |
| if not self.embeddings or not self.llm: | |
| st.error("Componenti LangChain non configurati.") | |
| return | |
| # Prepara testi per RAG | |
| all_texts = [] | |
| for filename, doc_data in anonymized_docs.items(): | |
| if doc_data.get('confirmed', False): | |
| all_texts.append(f"Documento {filename}:\n{doc_data['anonymized']}") | |
| if not all_texts: | |
| st.warning("Nessun documento confermato per RAG.") | |
| return | |
| with st.spinner("Creando vector store..."): | |
| # Chunking | |
| combined_text = "\n\n".join(all_texts) | |
| text_splitter = CharacterTextSplitter( | |
| separator="\n\n", | |
| chunk_size=1000, | |
| chunk_overlap=200, | |
| length_function=len, | |
| ) | |
| texts = text_splitter.split_text(combined_text) | |
| # Crea FAISS index | |
| self.vector_store = FAISS.from_texts(texts, self.embeddings) | |
| st.success(f"Vector store con {len(texts)} chunks creato.") | |
| # Setup QA chain | |
| qa_prompt = """Usa il contesto per rispondere alla domanda. | |
| Se non sai la risposta, dillo chiaramente. | |
| {context} | |
| Domanda: {question} | |
| Risposta:""" | |
| QA_PROMPT = PromptTemplate.from_template(qa_prompt) | |
| self.qa_chain = RetrievalQA.from_chain_type( | |
| llm=self.llm, | |
| chain_type="stuff", | |
| retriever=self.vector_store.as_retriever(), | |
| return_source_documents=True, | |
| chain_type_kwargs={"prompt": QA_PROMPT} | |
| ) | |
| def answer_question(self, query: str) -> str: | |
| """Risponde usando RAG""" | |
| if not self.qa_chain: | |
| return "RAG non pronto. Costruisci prima il knowledge base." | |
| try: | |
| result = self.qa_chain.invoke({"query": query}) | |
| answer = result["result"] | |
| # Aggiungi fonti se disponibili | |
| source_docs = result.get("source_documents", []) | |
| if source_docs: | |
| answer += "\n\n**Fonti:**\n" | |
| for i, doc in enumerate(source_docs): | |
| match = re.search(r"Documento (.*?):\n", doc.page_content) | |
| source_info = f" (da {match.group(1)})" if match else "" | |
| answer += f"- ...{doc.page_content[-100:]}{source_info}\n" | |
| return answer | |
| except Exception as e: | |
| return f"Errore RAG: {e}" | |
| def get_relevant_context(self, query: str, max_docs: int = 3) -> str: | |
| """Estrae contesto rilevante per query""" | |
| if not self.vector_store: | |
| return "" | |
| try: | |
| docs = self.vector_store.similarity_search(query, k=max_docs) | |
| context = "\n\n".join([doc.page_content for doc in docs]) | |
| return context | |
| except Exception as e: | |
| return f"Errore contesto: {e}" | |
| class CrewAIManager: | |
| """Manager agenti CrewAI""" | |
| def __init__(self, rag_chatbot: RAGChatbot): | |
| self.rag_chatbot = rag_chatbot | |
| self.agents = None | |
| self.llm = None | |
| self.setup_crew() | |
| def setup_crew(self): | |
| """Setup agenti CrewAI""" | |
| if not Config.AZURE_API_KEY: | |
| st.warning("Azure non disponibile per CrewAI") | |
| return | |
| try: | |
| # LLM per CrewAI | |
| self.llm = LLM( | |
| model=f"azure/{Config.DEPLOYMENT_NAME}", | |
| api_key=Config.AZURE_API_KEY, | |
| base_url=Config.AZURE_ENDPOINT, | |
| api_version=Config.AZURE_API_VERSION | |
| ) | |
| # Agenti | |
| document_analyst = Agent( | |
| role="Document Analyst", | |
| goal="Analizzare documenti anonimizzati e fornire insights", | |
| backstory="Esperto analista documenti con focus su privacy e compliance. " | |
| "Lavori solo con documenti anonimizzati per proteggere i dati.", | |
| llm=self.llm, | |
| verbose=True, | |
| allow_delegation=False, | |
| max_iter=3 | |
| ) | |
| rag_specialist = Agent( | |
| role="RAG Specialist", | |
| goal="Rispondere a domande usando il sistema RAG", | |
| backstory="Esperto in Information Retrieval e RAG systems. " | |
| "Specializzato nel recupero di informazioni da documenti anonimizzati.", | |
| llm=self.llm, | |
| verbose=True, | |
| allow_delegation=False, | |
| max_iter=3 | |
| ) | |
| sentiment_analyst = Agent( | |
| role="Sentiment Analyst", | |
| goal="Analizzare sentiment e emozioni nei documenti", | |
| backstory="Esperto in sentiment analysis e behavioral analytics. " | |
| "Identifichi emozioni, trend e segnali nei documenti.", | |
| llm=self.llm, | |
| verbose=True, | |
| allow_delegation=False, | |
| max_iter=3 | |
| ) | |
| strategy_coordinator = Agent( | |
| role="Strategy Coordinator", | |
| goal="Coordinare analisi e fornire raccomandazioni strategiche", | |
| backstory="Senior consultant con background in strategic management. " | |
| "Traduci insights tecnici in raccomandazioni business concrete.", | |
| llm=self.llm, | |
| verbose=True, | |
| allow_delegation=True, | |
| max_iter=4 | |
| ) | |
| self.agents = { | |
| 'document_analyst': document_analyst, | |
| 'rag_specialist': rag_specialist, | |
| 'sentiment_analyst': sentiment_analyst, | |
| 'strategy_coordinator': strategy_coordinator | |
| } | |
| st.success("✅ Agenti CrewAI configurati") | |
| except Exception as e: | |
| st.error(f"Errore setup CrewAI: {e}") | |
| self.agents = None | |
| def create_analysis_task(self, query: str, analysis_type: str = "comprehensive") -> str: | |
| """Crea task di analisi per il crew""" | |
| if not self.agents: | |
| return "CrewAI non configurato" | |
| try: | |
| # Ottieni contesto dal RAG | |
| context = self.rag_chatbot.get_relevant_context(query, max_docs=5) | |
| tasks = [] | |
| if analysis_type in ["comprehensive", "document"]: | |
| # Task analisi documentale | |
| doc_task = Task( | |
| description=f""" | |
| Analizza documenti per: {query} | |
| CONTESTO: {context} | |
| Fornisci: | |
| - Tipo e classificazione documenti | |
| - Temi e argomenti principali | |
| - Elementi rilevanti business | |
| - Note compliance | |
| """, | |
| expected_output="Analisi strutturata con classificazione e insights", | |
| agent=self.agents['document_analyst'] | |
| ) | |
| tasks.append(doc_task) | |
| if analysis_type in ["comprehensive", "sentiment"]: | |
| # Task sentiment | |
| sentiment_task = Task( | |
| description=f""" | |
| Analizza sentiment per: {query} | |
| CONTESTO: {context} | |
| Valuta: | |
| - Sentiment generale (scala 1-10) | |
| - Emozioni prevalenti | |
| - Trend comunicazioni | |
| - Segnali rischio/opportunità | |
| """, | |
| expected_output="Analisi sentiment con valutazioni quantitative", | |
| agent=self.agents['sentiment_analyst'] | |
| ) | |
| tasks.append(sentiment_task) | |
| if analysis_type in ["comprehensive", "rag"]: | |
| # Task RAG | |
| rag_task = Task( | |
| description=f""" | |
| Rispondi usando RAG: {query} | |
| CONTESTO: {context} | |
| Includi: | |
| - Risposta diretta | |
| - Evidenze documenti | |
| - Correlazioni trovate | |
| - Informazioni mancanti | |
| - Suggerimenti approfondimento | |
| """, | |
| expected_output="Risposta RAG con evidenze", | |
| agent=self.agents['rag_specialist'] | |
| ) | |
| tasks.append(rag_task) | |
| # Task coordinamento (sempre incluso) | |
| coord_task = Task( | |
| description=f""" | |
| Sintetizza risultati per: {query} | |
| Crea sintesi con: | |
| - Executive Summary (3 punti) | |
| - Insights strategici | |
| - Raccomandazioni prioritarie | |
| - Next steps concreti | |
| - Valutazione rischi | |
| Output executive-ready e actionable. | |
| """, | |
| expected_output="Sintesi strategica con raccomandazioni", | |
| agent=self.agents['strategy_coordinator'] | |
| ) | |
| tasks.append(coord_task) | |
| # Crea crew | |
| crew = Crew( | |
| agents=list(self.agents.values()), | |
| tasks=tasks, | |
| verbose=True | |
| ) | |
| with st.spinner(f"Eseguendo analisi {analysis_type}..."): | |
| result = crew.kickoff() | |
| return str(result) | |
| except Exception as e: | |
| return f"Errore CrewAI: {e}" | |
| def create_custom_task(self, query: str, selected_agents: List[str], custom_instructions: str = "") -> str: | |
| """Task personalizzate con agenti specifici""" | |
| if not self.agents: | |
| return "CrewAI non configurato" | |
| try: | |
| context = self.rag_chatbot.get_relevant_context(query, max_docs=5) | |
| tasks = [] | |
| agents_to_use = [] | |
| for agent_key in selected_agents: | |
| if agent_key in self.agents: | |
| agents_to_use.append(self.agents[agent_key]) | |
| task = Task( | |
| description=f""" | |
| {custom_instructions if custom_instructions else f'Analizza secondo il ruolo di {agent_key}'} | |
| QUERY: {query} | |
| CONTESTO: {context} | |
| Fornisci analisi specializzata secondo il tuo ruolo. | |
| """, | |
| expected_output=f"Analisi specializzata da {agent_key}", | |
| agent=self.agents[agent_key] | |
| ) | |
| tasks.append(task) | |
| if not tasks: | |
| return "Nessun agente valido selezionato" | |
| crew = Crew( | |
| agents=agents_to_use, | |
| tasks=tasks, | |
| verbose=True | |
| ) | |
| with st.spinner(f"Eseguendo task con {len(agents_to_use)} agenti..."): | |
| result = crew.kickoff() | |
| return str(result) | |
| except Exception as e: | |
| return f"Errore task personalizzato: {e}" |