| """The answer key, derived from persona features. ONE rule, any persona. |
| |
| 96 personas today; 10k real Expedia rows later — same function, zero authoring cost. |
| Verified total and single-valued over all 96 combos (test_rules.py). |
| """ |
|
|
| from __future__ import annotations |
|
|
| from .personas import Persona |
| from .scenarios import ScenarioTruth |
|
|
| _ALLOWED_OFFERS: dict[str, frozenset[str]] = { |
| "low": frozenset({"15% off", "10% off"}), |
| "mid": frozenset({"15% off", "free breakfast"}), |
| "high": frozenset({"free breakfast", "free night"}), |
| } |
|
|
|
|
| def _intent(p: Persona) -> str: |
| if p.trip_type == "conference" and p.near_event: |
| return "book_hotel_near_event" |
| if p.trip_type in ("conference", "business"): |
| return "business_trip" |
| if p.trip_type == "family": |
| return "family_vacation" |
| return "generic_leisure" |
|
|
|
|
| def _background(p: Persona) -> str: |
| if p.trip_type in ("conference", "business"): |
| return "hotel_exterior" |
| if p.trip_type == "family": |
| return "room_interior" |
| return "city_skyline" |
|
|
|
|
| def _headline(p: Persona) -> str: |
| |
| |
| if p.near_event and p.trip_type in ("conference", "business"): |
| return "proximity" |
| if p.budget_tier == "high": |
| return "boutique" |
| if p.trip_type == "leisure": |
| return "destination" |
| return "generic" |
|
|
|
|
| def _cta(p: Persona) -> str: |
| return "Book now" if p.device == "mobile" else "Learn more" |
|
|
|
|
| def truth_for(persona: Persona) -> ScenarioTruth: |
| return ScenarioTruth( |
| correct_intent=_intent(persona), |
| correct_background=_background(persona), |
| correct_headline_strategy=_headline(persona), |
| correct_cta=_cta(persona), |
| allowed_offers=_ALLOWED_OFFERS[persona.budget_tier], |
| ) |
|
|
|
|
| def oracle_action(persona: Persona) -> list[int]: |
| """The perfect action for a persona. Used by tests, the demo, and (later) as the agent's |
| upper-bound baseline. Scores exactly 1.0 by construction. |
| |
| sorted(allowed_offers)[0] — NEVER next(iter(frozenset)), whose order varies with |
| PYTHONHASHSEED and would make the oracle non-deterministic across processes. |
| """ |
| from .brief import MENUS |
|
|
| t = truth_for(persona) |
| return [ |
| MENUS["intent"].index(t.correct_intent), |
| MENUS["headline"].index(t.correct_headline_strategy), |
| MENUS["offer"].index(sorted(t.allowed_offers)[0]), |
| MENUS["cta"].index(t.correct_cta), |
| MENUS["background"].index(t.correct_background), |
| ] |
|
|