From 49c304d06f284d48d3b07c8d7bc44bf8f5707217 Mon Sep 17 00:00:00 2001 From: Piotr Oleszczyk Date: Sun, 1 Mar 2026 21:45:05 +0100 Subject: [PATCH] fix(routines): use system prompt for suggest and dedupe rules --- backend/innercontext/api/routines.py | 69 +++++++++++++++++++--------- 1 file changed, 47 insertions(+), 22 deletions(-) diff --git a/backend/innercontext/api/routines.py b/backend/innercontext/api/routines.py index 8f171fd..a7e1f43 100644 --- a/backend/innercontext/api/routines.py +++ b/backend/innercontext/api/routines.py @@ -306,19 +306,44 @@ def _build_objectives_context(include_minoxidil_beard: bool) -> str: return "" -_RULES = """\ -ZASADY: - - Kolejność warstw: cleanser → toner → essence → serum → moisturizer → [SPF dla AM] - - Respektuj incompatible_with (scope: same_step / same_day / same_period) - - Respektuj context_rules (safe_after_shaving, safe_after_acids itp.) - - Respektuj min_interval_hours i max_frequency_per_week - - Jeśli notatki użytkownika mówią o poprawie gęstości brody/wąsów, rozważ minoksydyl (jeśli jest dostępny na liście produktów) - - Dla minoksydylu respektuj usage_notes i ustaw region na obszar zarostu (broda/wąsy), jeśli to adekwatne - - 4–7 kroków na rutynę - - product_id musi być UUID produktu z listy lub null dla czynności pielęgnacyjnych - - action_type: tylko shaving_razor | shaving_oneblade | dermarolling (lub null) - - Nie używaj retinoidów i kwasów w tej samej rutynie - - W AM zawsze uwzględnij SPF jeśli dostępny +_ROUTINES_SYSTEM_PROMPT = """\ +Jesteś ekspertem planowania pielęgnacji. + +CEL: +Twórz realistyczne, bezpieczne i krótkie rutyny o wysokiej zgodności z danymi wejściowymi. + +PRIORYTETY DECYZYJNE (od najwyższego): +1) Bezpieczeństwo i przeciwwskazania +2) Zgodność ze schematem JSON i listą dostępnych produktów +3) Reguły częstotliwości oraz odstępów czasowych +4) Cel użytkownika (np. broda/wąsy, nawilżenie, tolerancja) +5) Prostota rutyny + +WYMAGANIA ODPOWIEDZI: +- Zwracaj wyłącznie poprawny JSON (bez markdown, bez komentarzy, bez preambuły). +- Trzymaj się dokładnie przekazanego schematu odpowiedzi. +- Nie używaj żadnych pól spoza schematu. +- Nie twórz produktów spoza listy wejściowej. +- Jeśli nie da się bezpiecznie dodać kroku, pomiń go zamiast zgadywać. + +ZASADY PLANOWANIA: +- Kolejność warstw: cleanser -> toner -> essence -> serum -> moisturizer -> [SPF dla AM]. +- Respektuj: incompatible_with (same_step / same_day / same_period), context_rules, + min_interval_hours, max_frequency_per_week, usage_notes. +- Nie łącz retinoidów i kwasów w tej samej rutynie ani tego samego dnia (dla planu wielodniowego). +- W AM zawsze uwzględnij SPF, jeśli kompatybilny produkt SPF istnieje na liście. +- Dla minoksydylu (jeśli celem jest zarost i produkt jest dostępny): ustaw adekwatny region + broda/wąsy i nie naruszaj ograniczeń bezpieczeństwa. +- Preferuj 4-7 kroków na pojedynczą rutynę; unikaj zbędnych duplikatów aktywnych. +- Jeśli krok to produkt: podaj poprawny UUID z listy. +- Jeśli krok to czynność pielęgnacyjna: product_id = null i action_type tylko z: + shaving_razor | shaving_oneblade | dermarolling. +- Nie zwracaj "pustych" kroków: każdy krok musi mieć product_id albo action_type. + +JAK ROZWIĄZYWAĆ KONFLIKTY: +- Gdy cel użytkownika koliduje z bezpieczeństwem, wybierz bezpieczeństwo. +- Gdy dwa produkty są podobne, wybierz łagodniejszy lub prostszy wariant. +- Gdy dane są niepełne lub niejednoznaczne, wybierz wariant konserwatywny. """ @@ -399,14 +424,17 @@ def suggest_routine( prompt = ( f"Zaproponuj rutynę pielęgnacyjną {data.part_of_day.value.upper()} " f"na {data.routine_date} ({day_name}).\n\n" - f"{skin_ctx}\n{grooming_ctx}\n{history_ctx}\n{products_ctx}\n{objectives_ctx}\n{_RULES}{notes_line}" - "\nZwróć JSON zgodny ze schematem." + "DANE WEJŚCIOWE:\n" + f"{skin_ctx}\n{grooming_ctx}\n{history_ctx}\n{products_ctx}\n{objectives_ctx}" + f"{notes_line}\n" + "Zwróć JSON zgodny ze schematem." ) response = call_gemini( endpoint="routines/suggest", contents=prompt, config=genai_types.GenerateContentConfig( + system_instruction=_ROUTINES_SYSTEM_PROMPT, response_mime_type="application/json", response_schema=_SuggestionOut, max_output_tokens=4096, @@ -469,13 +497,9 @@ def suggest_batch( prompt = ( f"Zaproponuj plan pielęgnacji AM + PM dla każdego dnia z zakresu:\n{dates_str}\n\n" - f"{skin_ctx}\n{grooming_ctx}\n{history_ctx}\n{products_ctx}\n{objectives_ctx}\n{_RULES}{notes_line}" - "\nDodatkowe zasady dla planu wielodniowego:\n" - " - Retinol/retinoidy: przestrzegaj max_frequency_per_week i min_interval_hours między użyciami\n" - " - Nie stosuj kwasów i retinoidów tego samego dnia\n" - " - Uwzględnij safe_after_shaving dla dni golenia\n" - " - Zmienność aktywnych składników przez dni dla lepszej tolerancji\n" - " - Pole date w każdym dniu MUSI być w formacie YYYY-MM-DD\n" + "DANE WEJŚCIOWE:\n" + f"{skin_ctx}\n{grooming_ctx}\n{history_ctx}\n{products_ctx}\n{objectives_ctx}" + f"{notes_line}\n" "\nZwróć JSON zgodny ze schematem." ) @@ -483,6 +507,7 @@ def suggest_batch( endpoint="routines/suggest-batch", contents=prompt, config=genai_types.GenerateContentConfig( + system_instruction=_ROUTINES_SYSTEM_PROMPT, response_mime_type="application/json", response_schema=_BatchOut, max_output_tokens=8192,