fix(lang): pin the detected customer language explicitly (Spanish-heavy context was overriding plain mirror); None->mirror fallback
Browse files- app/prompts.py +25 -17
- tests/test_lang.py +9 -8
- tests/test_prompts.py +6 -4
app/prompts.py
CHANGED
|
@@ -101,23 +101,31 @@ def build_system_prompt(
|
|
| 101 |
channel: str = "web",
|
| 102 |
extra_instructions: str = "",
|
| 103 |
) -> str:
|
| 104 |
-
# LANGUAGE
|
| 105 |
-
#
|
| 106 |
-
#
|
| 107 |
-
#
|
| 108 |
-
#
|
| 109 |
-
#
|
| 110 |
-
#
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 121 |
channel_handoff = _WA_HANDOFF if channel == "whatsapp" else _WEB_HANDOFF
|
| 122 |
# The merchant's own rules go HIGH in the prompt (right after the language
|
| 123 |
# rule, before GROUNDING) and are framed as TOP priority that overrides the
|
|
|
|
| 101 |
channel: str = "web",
|
| 102 |
extra_instructions: str = "",
|
| 103 |
) -> str:
|
| 104 |
+
# LANGUAGE: reply in the customer's language. The store's catalog, tool results,
|
| 105 |
+
# brand and even these instructions are heavily Spanish, which DRAGS the model
|
| 106 |
+
# into Spanish even for an English customer (observed live) — so a plain "mirror"
|
| 107 |
+
# hint is too weak. We pin the customer's language EXPLICITLY when we detected it
|
| 108 |
+
# (detection is upstream and now reliable: English->English, Spanish->Spanish,
|
| 109 |
+
# and the old all-caps-Spanish->Hungarian misfire is fixed at the detector). When
|
| 110 |
+
# detection is unsure (None) we fall back to "mirror" and trust the model. The
|
| 111 |
+
# non-Latin script guard (app.lang.needs_language_repair) is the last safety net.
|
| 112 |
+
if language:
|
| 113 |
+
language_rule = (
|
| 114 |
+
f"The customer is writing in {language}. Write your ENTIRE reply in "
|
| 115 |
+
f"{language}. The store's catalog, product sheets, tool results and even "
|
| 116 |
+
f"these instructions may be in a DIFFERENT language — IGNORE that for your "
|
| 117 |
+
f"choice of language and TRANSLATE everything into {language}. Never reply "
|
| 118 |
+
f"in any other language and never mix languages. This rule is absolute."
|
| 119 |
+
)
|
| 120 |
+
lang_short = language
|
| 121 |
+
else:
|
| 122 |
+
language_rule = (
|
| 123 |
+
"Reply in EXACTLY the same language the customer wrote their last message "
|
| 124 |
+
"in — detect it yourself and mirror it. The store's data and these "
|
| 125 |
+
"instructions may be in another language; translate them into the "
|
| 126 |
+
"customer's language. Never switch languages unless the customer does."
|
| 127 |
+
)
|
| 128 |
+
lang_short = "the customer's language"
|
| 129 |
channel_handoff = _WA_HANDOFF if channel == "whatsapp" else _WEB_HANDOFF
|
| 130 |
# The merchant's own rules go HIGH in the prompt (right after the language
|
| 131 |
# rule, before GROUNDING) and are framed as TOP priority that overrides the
|
tests/test_lang.py
CHANGED
|
@@ -208,16 +208,17 @@ def test_prompt_forbids_emojis():
|
|
| 208 |
assert "emoji" in build_system_prompt("X").lower()
|
| 209 |
|
| 210 |
|
| 211 |
-
def
|
| 212 |
-
#
|
| 213 |
-
#
|
| 214 |
-
#
|
| 215 |
-
#
|
| 216 |
from app.prompts import build_system_prompt
|
| 217 |
|
| 218 |
-
|
| 219 |
-
assert "
|
| 220 |
-
|
|
|
|
| 221 |
|
| 222 |
|
| 223 |
def test_prompt_forbids_redirecting_customer_to_third_parties():
|
|
|
|
| 208 |
assert "emoji" in build_system_prompt("X").lower()
|
| 209 |
|
| 210 |
|
| 211 |
+
def test_prompt_pins_detected_language_else_mirrors():
|
| 212 |
+
# The store context is heavily Spanish and drags the model into Spanish even for
|
| 213 |
+
# an English customer (observed live), so a detected language is pinned EXPLICITLY;
|
| 214 |
+
# when detection is unsure (None) we mirror. The Hungarian misfire is fixed at the
|
| 215 |
+
# DETECTOR (see test_spanish_detected), not by refusing to pin.
|
| 216 |
from app.prompts import build_system_prompt
|
| 217 |
|
| 218 |
+
pinned = build_system_prompt("Tienda", "English").lower()
|
| 219 |
+
assert "english" in pinned and "entire reply" in pinned
|
| 220 |
+
unsure = build_system_prompt("Tienda", None).lower()
|
| 221 |
+
assert "same language" in unsure and "mirror" in unsure
|
| 222 |
|
| 223 |
|
| 224 |
def test_prompt_forbids_redirecting_customer_to_third_parties():
|
tests/test_prompts.py
CHANGED
|
@@ -9,11 +9,13 @@ def test_system_prompt_includes_rules_and_brand():
|
|
| 9 |
# language is the prominent RULE #1
|
| 10 |
assert "RULE #1" in p
|
| 11 |
assert "language" in p.lower()
|
| 12 |
-
#
|
| 13 |
-
#
|
|
|
|
| 14 |
pinned = build_system_prompt("X", language="portugués de Portugal (pt-PT)")
|
| 15 |
-
assert "pt-PT"
|
| 16 |
-
|
|
|
|
| 17 |
# anti-hallucination grounding
|
| 18 |
assert "invent" in p.lower()
|
| 19 |
# order verification (email + order number / postal code)
|
|
|
|
| 9 |
# language is the prominent RULE #1
|
| 10 |
assert "RULE #1" in p
|
| 11 |
assert "language" in p.lower()
|
| 12 |
+
# A DETECTED language is pinned explicitly (the Spanish-heavy context otherwise
|
| 13 |
+
# drags the model into Spanish even for an English customer). The Hungarian bug
|
| 14 |
+
# is fixed at the DETECTOR, not by refusing to pin.
|
| 15 |
pinned = build_system_prompt("X", language="portugués de Portugal (pt-PT)")
|
| 16 |
+
assert "pt-PT" in pinned
|
| 17 |
+
# when detection is unsure (None) we fall back to mirroring
|
| 18 |
+
assert "mirror" in build_system_prompt("X", language=None).lower()
|
| 19 |
# anti-hallucination grounding
|
| 20 |
assert "invent" in p.lower()
|
| 21 |
# order verification (email + order number / postal code)
|