Commit f9b10ebe authored by Vũ Hoàng Anh's avatar Vũ Hoàng Anh

refactor: unified SKU search (exact match, no subsequence) + store search...

refactor: unified SKU search (exact match, no subsequence) + store search upgrade (token AND + reverse LIKE fallback)
parent 40f5a569
...@@ -130,6 +130,8 @@ def format_product_results(products: list[dict]) -> list[dict]: ...@@ -130,6 +130,8 @@ def format_product_results(products: list[dict]) -> list[dict]:
continue continue
seen_skus.add(dedup_key) seen_skus.add(dedup_key)
description_value = p.get("description_text_full") or p.get("description_text") or ""
product_entry = { product_entry = {
"sku": sku, "sku": sku,
"sku_color": sku_color, "sku_color": sku_color,
...@@ -139,7 +141,7 @@ def format_product_results(products: list[dict]) -> list[dict]: ...@@ -139,7 +141,7 @@ def format_product_results(products: list[dict]) -> list[dict]:
"sale_price": int(sale_price) if sale_price else int(original_price), "sale_price": int(sale_price) if sale_price else int(original_price),
"url": web_url, "url": web_url,
"thumbnail_image_url": thumb_url, "thumbnail_image_url": thumb_url,
"description": _neutralize_generic_print(p.get("description_text") or ""), "description": _neutralize_generic_print(description_value),
} }
size_scale = p.get("size_scale") size_scale = p.get("size_scale")
if size_scale: if size_scale:
......
...@@ -3,7 +3,6 @@ import logging ...@@ -3,7 +3,6 @@ import logging
from langchain_core.tools import tool from langchain_core.tools import tool
from pydantic import BaseModel, Field from pydantic import BaseModel, Field
from agent.prompt_utils import read_tool_prompt
from common.starrocks_connection import get_db_connection from common.starrocks_connection import get_db_connection
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
...@@ -33,23 +32,55 @@ async def canifa_store_search(location: str) -> str: ...@@ -33,23 +32,55 @@ async def canifa_store_search(location: str) -> str:
clean = clean.replace(prefix, "") clean = clean.replace(prefix, "")
clean = clean.strip() clean = clean.strip()
if not clean: # Tách thành tokens, deduplicate (giữ thứ tự)
# VD: "hà đông, hà nội" → ["hà", "đông", "nội"]
tokens = list(dict.fromkeys(
t for t in clean.replace(',', ' ').split() if t.strip()
))
if not tokens:
return "Vui lòng cho em biết khu vực bạn muốn tìm cửa hàng CANIFA (ví dụ: Hoàng Mai, Cầu Giấy, Đà Nẵng...)." return "Vui lòng cho em biết khu vực bạn muốn tìm cửa hàng CANIFA (ví dụ: Hoàng Mai, Cầu Giấy, Đà Nẵng...)."
# Search trên các cột structured: city, state, address, store_name # Search trên concat tất cả cột địa chỉ
sql = f""" text_col = "LOWER(concat_ws(' ', store_name, address, city, state))"
def _build_sql(where_clause: str) -> str:
return f"""
SELECT store_name, address, city, state, phone_number, SELECT store_name, address, city, state, phone_number,
schedule_name, time_open_today, time_close_today schedule_name, time_open_today, time_close_today
FROM {STORE_TABLE} FROM {STORE_TABLE}
WHERE LOWER(city) LIKE '%{clean}%' WHERE {where_clause}
OR LOWER(state) LIKE '%{clean}%'
OR LOWER(address) LIKE '%{clean}%'
OR LOWER(store_name) LIKE '%{clean}%'
ORDER BY state, city, store_name ORDER BY state, city, store_name
LIMIT 20 LIMIT 20
""" """
results = await sr.execute_query_async(sql) # ═══════════════════════════════════════════════
# Step 1: AND tất cả tokens (strict match)
# "hà đông hà nội" → tokens ["hà","đông","nội"] → AND → 5 stores ✓
# ═══════════════════════════════════════════════
and_conds = [f"{text_col} LIKE '%{tk}%'" for tk in tokens]
results = await sr.execute_query_async(_build_sql(' AND '.join(and_conds)))
# ═══════════════════════════════════════════════
# Step 2: Fallback — Reverse LIKE
# Dùng chính DB làm từ điển địa danh:
# Kiểm tra tên quận/huyện/tỉnh nào trong DB XUẤT HIỆN trong input user
# "hà đông cầu giấy" chứa "hà đông" (city) + "cầu giấy" (city) → lấy CẢ 2
# ═══════════════════════════════════════════════
if not results and len(tokens) >= 2:
# Strip prefix khỏi city: "Quận Hà Đông" → "hà đông"
city_stripped = """LOWER(TRIM(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
city, 'Quận ', ''), 'Huyện ', ''), 'Thành phố ', ''), 'Thị xã ', ''), 'TP. ', '')))"""
state_lower = "LOWER(TRIM(state))"
fallback_where = f"""
(LOCATE({city_stripped}, '{clean}') > 0 AND LENGTH({city_stripped}) > 1)
OR
(LOCATE({state_lower}, '{clean}') > 0 AND LENGTH({state_lower}) > 1)
"""
results = await sr.execute_query_async(_build_sql(fallback_where))
logger.info(f"📊 Store search: reverse-LIKE fallback for '{clean}'")
logger.info(f"📊 Store search: {len(results)} stores found for '{location}'") logger.info(f"📊 Store search: {len(results)} stores found for '{location}'")
if not results: if not results:
...@@ -86,4 +117,3 @@ async def canifa_store_search(location: str) -> str: ...@@ -86,4 +117,3 @@ async def canifa_store_search(location: str) -> str:
return "Tôi đang gặp khó khăn khi tìm kiếm cửa hàng. Bạn có thể liên hệ hotline 1800 6061 để được hỗ trợ." return "Tôi đang gặp khó khăn khi tìm kiếm cửa hàng. Bạn có thể liên hệ hotline 1800 6061 để được hỗ trợ."
canifa_store_search.__doc__ = read_tool_prompt("store_search_tool") or canifa_store_search.__doc__
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment