Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
| """ | |
| Query Understanding Agent β Enriches raw search queries using user profile context. | |
| Returns only the enriched query attributes; the orchestrator handles the actual search. | |
| """ | |
| from typing import Optional | |
| from strands import Agent | |
| from strands.models import BedrockModel | |
| from search_personalization.agentic_memory.config import BEDROCK_MODEL_ID_FAST, AWS_REGION | |
| from search_personalization.agentic_memory.schema_cache import get_schema_prompt_block | |
| _DEFAULT_SCHEMA_BLOCK = """PRODUCT INDEX SCHEMA: | |
| - category (keyword, 5 values): apparel, accessories, footwear, electronics, jewelry | |
| - style (keyword): jacket, formal, shirt, glasses, scarf, backpack, sneaker, belt, handbag, bracelet, camera, sandals, boot, necklace, socks, watch, earrings, bag, speaker, headphones, cable, television, computer, keyboard | |
| - gender_affinity (keyword): F, M | |
| - price (numeric) | |
| - name (text) β product name | |
| - description (text) β product description""" | |
| _SYSTEM_PROMPT_TEMPLATE = """You are the Query Understanding Agent. Enrich a search query using the user's behavioral profile. | |
| {schema_block} | |
| FIELD USAGE: | |
| - category β hard filter (exact match, 5 values) | |
| - gender_affinity β hard filter ("F" or "M") | |
| - max_price β hard filter (numeric ceiling) β ONLY when user explicitly states price in query | |
| - enriched_query β drives vector/semantic search (no filter values here) | |
| ENRICHED QUERY RULES: | |
| 1. Format: space-separated keywords/phrases, NOT a sentence. Example: "shoes sneakers athletic comfortable black gray" | |
| 2. Start with the user's original query words (strip any price mentions like "under $50"). | |
| 3. Append descriptors reflecting the user's DOMINANT style pattern (majority of behavior, not outliers). Example: 3 sneaker purchases + 1 dress shirt = sneaker person, NOT formal person. | |
| 4. If the profile shows strong preference for a product sub-type (e.g., bought 3 sneakers), include that sub-type. This is the most important signal. | |
| 5. Express aversions as positive opposites (e.g., aversion "flashy" β add "understated"; "loud colors" β "muted tones"). Do NOT use negation words. | |
| 6. NEVER include in enriched_query: price values ("under $50", "$50", "budget"), category names, gender letters, or any term related to the aversions (e.g., if aversions include "formal"/"fancy", never add "formal", "dress", "elegant", or similar). | |
| 7. Use conversation history to resolve references (e.g., "in blue" after "shoes" β "shoes blue"). | |
| INFERRED ATTRIBUTES: | |
| - category: REQUIRED. Map query to: apparel, accessories, footwear, electronics, jewelry. | |
| - gender_affinity: "F" or "M" if any gender signal exists in profile. Null otherwise. | |
| - max_price: numeric ONLY if the user's raw query text explicitly mentions a price constraint (e.g., "under $50", "below $100"). If the query does NOT contain a price mention, max_price MUST be null even if the profile suggests a budget ceiling. | |
| - style: closest schema value or null. | |
| - colors: recurring color preferences from profile. | |
| - preferred_materials: recurring materials from profile. | |
| - use_context: primary shopping context (e.g., "campus daily wear", "business-casual work"). | |
| - aversions: raw aesthetic dislikes from profile (e.g., ["flashy", "formal"]). Never include the searched product type. | |
| OUTPUT (strict JSON, nothing else): | |
| {{"enriched_query": "keywords only, e.g.: shoes sneakers athletic comfortable muted", "inferred_attributes": {{"colors": [], "style": null, "category": null, "gender_affinity": null, "max_price": null, "aversions": [], "preferred_materials": [], "use_context": null}}}} | |
| """ | |
| def create_query_agent() -> Agent: | |
| """Create the Query Understanding Agent (no tools β pure reasoning).""" | |
| schema_block = get_schema_prompt_block() or _DEFAULT_SCHEMA_BLOCK | |
| system_prompt = _SYSTEM_PROMPT_TEMPLATE.format(schema_block=schema_block) | |
| model = BedrockModel( | |
| model_id=BEDROCK_MODEL_ID_FAST, | |
| region_name=AWS_REGION, | |
| ) | |
| return Agent( | |
| model=model, | |
| tools=[], | |
| system_prompt=system_prompt, | |
| ) | |
| def invoke_query_agent( | |
| query: str, | |
| persona_id: str, | |
| session_id: str, | |
| profile: str = "", | |
| session_context: str = "", | |
| ) -> str: | |
| """ | |
| Invoke the Query Understanding Agent. | |
| Returns: | |
| JSON string with enriched_query and inferred_attributes only. | |
| """ | |
| agent = create_query_agent() | |
| profile_section = f"\n\nUser Profile:\n{profile}" if profile else "\n\nNo profile available β use query as-is." | |
| history_section = f"\n\nConversation History:\n{session_context}" if session_context else "" | |
| prompt = ( | |
| f"Process this search:\n\n" | |
| f"Raw query: \"{query}\"{profile_section}{history_section}\n\n" | |
| f"Enrich the query and output the JSON." | |
| ) | |
| result = agent(prompt) | |
| return str(result) | |