import logging import json from typing import Dict, Any, Optional, Tuple from pydantic import BaseModel, Field from core.llm_router import get_llm from langchain_core.messages import SystemMessage, HumanMessage from core.utils import extract_markdown_and_sanitize logger = logging.getLogger(__name__) class FinancialCalculations(BaseModel): content_markdown: str = Field(description="Zredagowany tekst sekcji finansowej w formacie Markdown z tabelami i wyliczeniami.") missing_data_question: Optional[str] = Field(None, description="Brakujące dane.") class FinanceAgent: """ Specjalistyczny Agent Finansowy obsługujący generowanie sekcji 'budget' i 'finance'. Odpowiada za wyliczanie NPV, IRR, kategoryzację CAPEX/OPEX i wskaźniki zwrotu z inwestycji. """ def __init__(self): self.llm = get_llm(task_type="critical", structured_output_schema=FinancialCalculations) def draft_financial_section(self, document_type: str, section_name: str, project_desc: str, context: str) -> Tuple[str, Optional[str]]: logger.info(f"[FinanceAgent] Drafting financial section: {section_name}") system_prompt = ( "Jesteś profesjonalnym Analitykiem Finansowym (Financial AI) specjalizującym się w funduszach UE.\n" f"Obecnie przygotowujesz sekcję finansową: '{section_name}' dla dokumentu '{document_type}'.\n\n" "Wytyczne Główne:\n" " - Wygeneruj szczegółowy, analityczny tekst w Markdown, uwzględniający szacunkowe tabele budżetowe.\n" " - Wykorzystaj dostarczony Kontekst RAG, aby dopasować limity kosztów i kwalifikowalność.\n" " - Zbuduj logiczny podział na Koszty Kwalifikowalne (CAPEX / OPEX) i Koszty Niekwalifikowalne.\n" " - Wprowadź profesjonalne założenia wskaźników finansowych: NPV (Wartość zaktualizowana netto), IRR (Wewnętrzna stopa zwrotu), ROI (Zwrot z inwestycji).\n" " - Jeżeli w danych projektu brakuje konkretnych kwot, użyj profesjonalnych, realistycznych założeń (szacunków) opartych na wielkości firmy i branży, zaznaczając to w tabeli.\n" " - >>> BEZWZGLĘDNIE GENERUJ CAŁĄ TREŚĆ WYŁĄCZNIE W JĘZYKU POLSKIM. <<<\n" " - ZABRANIA SIĘ używania słów w języku angielskim. Tabele i nagłówki MUSZĄ być po polsku.\n" " - ZADBÓJ O ZWIĘZŁOŚĆ NAGŁÓWKÓW: Ogranicz długość nagłówków/tytułów sekcji do maksymalnie 5 wyrazów.\n" ) human_content = f"Kontekst Programu:\n{context}\n\nOpis Projektu:\n{project_desc}" try: response = self.llm.invoke([ SystemMessage(content=system_prompt), HumanMessage(content=human_content) ]) content = extract_markdown_and_sanitize(response.content_markdown) if response.content_markdown else "Błąd wyliczania finansów." missing = response.missing_data_question return content, missing except Exception as e: logger.error(f"[FinanceAgent] Błąd LLM: {e}") return f"*(Błąd podczas generowania sekcji finansowej: {str(e)})*", None finance_agent = FinanceAgent()