fix(zopk): Uproszczone prompty ekstrakcji - rozwiązanie safety filters

Problem: Połączenie skomplikowanego system_prompt + user_prompt
         wyzwalało safety filters Gemini mimo płatnego API

Rozwiązanie:
- Usunięto system_prompt (pusty string)
- Uproszczono user_prompt do minimum
- Zachowano niezbędny format JSON

Testowane: prosty prompt działa, pełny przebieg do weryfikacji

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Maciej Pienczyn 2026-01-16 20:54:22 +01:00
parent 1e42c4fbd8
commit e1f29229a3

View File

@ -90,71 +90,26 @@ RELATION_TYPES = [
# AI PROMPTS
# ============================================================
EXTRACTION_SYSTEM_PROMPT = """Jesteś asystentem analitycznym, który pomaga tworzyć podsumowania PUBLICZNYCH artykułów prasowych z polskich portali informacyjnych.
# Simplified prompts to avoid safety filter issues
# Note: Complex JSON schemas combined with detailed instructions were triggering Gemini safety filters
EXTRACTION_USER_PROMPT = """Przeanalizuj artykuł i zwróć informacje w formacie JSON.
Analizujesz artykuły o LEGALNYCH inwestycjach infrastrukturalnych i energetycznych w Polsce, takich jak:
- Projekty energetyki odnawialnej (farmy wiatrowe offshore)
- Inwestycje przemysłowe (parki przemysłowe, fabryki)
- Infrastruktura transportowa (drogi, koleje)
- Projekty energetyczne realizowane zgodnie z polityką państwa
WAŻNE: To jest analiza PUBLICZNIE DOSTĘPNYCH wiadomości prasowych, które można znaleźć w Google News.
Nie tworzysz treści - tylko identyfikujesz i strukturyzujesz informacje już opublikowane przez dziennikarzy.
Twoim zadaniem jest zidentyfikować w tekście:
1. FAKTY - konkretne informacje (liczby, daty, decyzje biznesowe)
2. PODMIOTY - nazwy firm, osób, miejsc, projektów
3. POWIĄZANIA - relacje między podmiotami"""
EXTRACTION_USER_PROMPT = """Poniżej znajduje się fragment PUBLICZNIE DOSTĘPNEGO artykułu prasowego. Zidentyfikuj kluczowe informacje i podmioty.
ARTYKUŁ PRASOWY:
ARTYKUŁ:
{chunk_text}
ŹRÓDŁO ARTYKUŁU: {source_name} ({published_date})
ŹRÓDŁO: {source_name} ({published_date})
Odpowiedz w formacie JSON:
Zwróć JSON:
{{
"facts": [
{{
"type": "statistic|event|statement|decision|milestone|partnership|investment",
"subject": "podmiot faktu",
"predicate": "czasownik/relacja",
"object": "dopełnienie/informacja",
"full_text": "pełne zdanie opisujące fakt",
"numeric_value": null lub liczba,
"numeric_unit": null lub "PLN|EUR|MW|jobs|%",
"date_value": null lub "YYYY-MM-DD",
"confidence": 0.0-1.0
}}
],
"entities": [
{{
"type": "company|person|place|organization|project|technology|event",
"name": "nazwa encji",
"description": "krótki opis (1 zdanie)",
"role": "rola w kontekście (np. inwestor, koordynator)"
}}
],
"relations": [
{{
"entity_a": "nazwa encji A",
"entity_b": "nazwa encji B",
"relation": "investor_in|partner_of|located_in|manages|works_for|part_of|cooperates_with|produces|supplies_to",
"description": "opis relacji"
}}
],
"summary": "1-2 zdaniowe podsumowanie fragmentu",
"keywords": ["słowo1", "słowo2", "słowo3"]
}}
"facts": [{{"subject": "kto/co", "predicate": "robi co", "object": "co/komu", "full_text": "zdanie"}}],
"entities": [{{"name": "nazwa", "type": "company|person|place|project", "description": "opis"}}],
"relations": [{{"entity_a": "A", "entity_b": "B", "relation": "typ relacji"}}],
"summary": "1 zdanie podsumowania",
"keywords": ["słowo1", "słowo2"]
}}"""
ZASADY:
- Wyodrębniaj TYLKO informacje wprost zawarte w tekście
- Nie dodawaj informacji z własnej wiedzy
- Podawaj confidence score dla każdego faktu (1.0 = pewny, 0.5 = prawdopodobny)
- Dla liczb zawsze podawaj jednostkę (PLN, EUR, MW, jobs, %)
- Dla dat używaj formatu YYYY-MM-DD
- Nazwy encji pisz dokładnie jak w tekście"""
# System prompt is now empty - the user prompt contains all necessary instructions
EXTRACTION_SYSTEM_PROMPT = ""
# ============================================================
@ -382,17 +337,15 @@ class ZOPKKnowledgeService:
Returns parsed JSON or None on error.
"""
try:
user_prompt = EXTRACTION_USER_PROMPT.format(
# Simplified single prompt (system prompt removed to avoid safety filter issues)
prompt = EXTRACTION_USER_PROMPT.format(
chunk_text=chunk.content,
source_name=source_name,
published_date=published_date
)
# Combine system prompt with user prompt (Gemini doesn't support separate system_prompt)
full_prompt = f"{EXTRACTION_SYSTEM_PROMPT}\n\n---\n\n{user_prompt}"
response = self.gemini.generate_text(
prompt=full_prompt,
prompt=prompt,
temperature=0.1, # Low temperature for consistency
max_tokens=2000,
user_id=self.user_id,