Spaces:
Running
Running
| from pydantic import BaseModel, Field | |
| from typing import Literal | |
| from core.llm_router import get_llm | |
| from langchain_core.prompts import PromptTemplate | |
| from rag_pipeline import get_hybrid_retriever, rerank_documents | |
| import logging | |
| from tenacity import retry, stop_after_attempt, wait_exponential | |
| logger = logging.getLogger(__name__) | |
| class ExpenseEvaluationResponse(BaseModel): | |
| czy_wydatek_kwalifikowalny: bool = Field( | |
| description="Zwr贸膰 True je艣li wydatek jest w 100% zgodny z regulaminem i wytycznymi programu (kwalifikowalny)." | |
| ) | |
| uzasadnienie_prawne: str = Field( | |
| description="Cytat lub konkretne odwo艂anie do regulaminu uzasadniaj膮ce kwalifikowalno艣膰 lub jej brak." | |
| ) | |
| kategoria_badan: Literal[ | |
| "badania przemys艂owe", | |
| "prace rozwojowe", | |
| "prace przedwdro偶eniowe", | |
| "brak/nie dotyczy", | |
| ] = Field( | |
| description="Wybierz do jakiej kategorii zgodnie z polskim/unijnym prawem nale偶y ten wydatek. Wybierz 'brak/nie dotyczy' tylko je艣li wydatek jest ca艂kowicie poza B+R." | |
| ) | |
| intensywnosc_pomocy: float = Field( | |
| description="Zwr贸膰 w formie warto艣ci zmiennoprzecinkowej np. 0.50 (co oznacza 50%), 0.80 (co oznacza 80%) bazuj膮c na wielko艣ci firmy i rodzaju bada艅. 0.0 oznacza wydatek niekwalifikowalny." | |
| ) | |
| def evaluate_project_expense( | |
| expense_description: str, | |
| expense_amount: float, | |
| project_title: str, | |
| program_name: str, | |
| company_size: str, | |
| tenant_id: str = None, | |
| ) -> ExpenseEvaluationResponse: | |
| """ | |
| Agent ds. Oceny Kwalifikowalno艣ci (FAZA 4). | |
| Wymusza twarde, ustrukturyzowane ramy JSON za pomoc膮 Pydantic. | |
| Opiera si臋 na wiedzy RAG dotycz膮cej wybranego programu. | |
| """ | |
| # Pr贸ba za艂adowania kontekstu z RAG - Hard Filtering na aktualn膮 perspektyw臋 | |
| # Domy艣lnie wyszukujemy tylko w najnowszej perspektywie (FAZA 3, zapobieganie aplikacji starych przepis贸w) | |
| hard_filter = {"rok_perspektywy": {"$eq": "2021-2027"}} | |
| if program_name: | |
| # Operator $and dla Pinecone Vector Store | |
| hard_filter = { | |
| "$and": [ | |
| {"program_name": {"$eq": program_name}}, | |
| {"rok_perspektywy": {"$eq": "2021-2027"}}, | |
| ] | |
| } | |
| context_text = "Brak specyficznego regulaminu programu w bazie." | |
| try: | |
| retriever = get_hybrid_retriever( | |
| k=10, metadata_filter=hard_filter, namespace=tenant_id | |
| ) | |
| if retriever: | |
| query_for_rag = f"kwalifikowalno艣膰 wydatku badania kategoria intensywno艣膰 dotacji pomoc publiczna: {expense_description}" | |
| docs = retriever.invoke(query_for_rag) | |
| reranked_docs = rerank_documents(query_for_rag, docs, top_n=4) | |
| context_text = "\n\n".join( | |
| [ | |
| f"[殴R脫D艁O: {d.metadata.get('source', 'Brak')}]: {d.page_content}" | |
| for d in reranked_docs | |
| ] | |
| ) | |
| except Exception as e: | |
| logger.error(f"[ExpenseEvaluator] Error fetching RAG context: {str(e)}") | |
| template = """ | |
| Jeste艣 G艂贸wnym Prawnikiem i Audytorem Dotacyjnym oceniaj膮cym kwalifikowalno艣膰 wydatk贸w. | |
| Oceniasz pojedynczy wydatek dla projektu w ramach programu: {program_name}. | |
| Wielko艣膰 przedsi臋biorstwa wnioskodawcy: {company_size}. | |
| Opis wydatku do weryfikacji: | |
| "{expense_description}" (Kwota: {expense_amount} PLN) | |
| Kontekst z regulamin贸w z bazy wiedzy: | |
| -------------------------------------------------- | |
| {context} | |
| -------------------------------------------------- | |
| Zasady: | |
| 1. Przeanalizuj czy podany wydatek kwalifikuje si臋 do obj臋cia wsparciem zgodnie z baz膮 wiedzy. | |
| 2. Okre艣l kategori臋 bada艅 dla wydatku, zgodnie z definicjami (badania przemys艂owe, prace rozwojowe, przedwdro偶eniowe). | |
| 3. Je艣li wydatek jest kwalifikowalny, przypisz prawid艂ow膮 intensywno艣膰 pomocy (zazwyczaj mniejszy procent dla prac rozwojowych/du偶ych firm, wi臋kszy dla bada艅 przemys艂owych/M艢P). | |
| 4. Podaj bardzo precyzyjne uzasadnienie prawne odnosz膮ce si臋 do regulaminu. | |
| """ | |
| prompt = PromptTemplate.from_template(template) | |
| # LLM z typowaniem - GPT-4o jest du偶o lepszy do takich zada艅 analitycznych | |
| structured_llm = get_llm( | |
| task_type="legal_audit", structured_output_schema=ExpenseEvaluationResponse | |
| ) | |
| chain = prompt | structured_llm | |
| def _invoke_chain(): | |
| return chain.invoke( | |
| { | |
| "program_name": program_name or "Og贸lne zasady dotacji B+R", | |
| "company_size": company_size or "M艢P (nieokre艣lona wielko艣膰)", | |
| "expense_description": expense_description, | |
| "expense_amount": expense_amount, | |
| "context": context_text, | |
| } | |
| ) | |
| result = _invoke_chain() | |
| return result | |