Skip to content

Commit eaa9f6d

Browse files
committed
feat(bot): improve intent handling and price comparator fallback
Added INTENT_GENERAL to handle greetings and small talk without hallucinating product searches. Added INTENT_CONTACT to catch user requests to speak with a manager, returning direct contacts. Enhanced PriceComparator to extract real Part Numbers from WooCommerce titles for Datacomp searches. Implemented a broad search fallback in PriceComparator by cleaning category prefixes when exact SKU search fails. Resolves bot intent hallucinations and fixes Datacomp scraper search coverage.
1 parent 150b46b commit eaa9f6d

4 files changed

Lines changed: 48 additions & 1 deletion

File tree

app/core/constants.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@
4747
INTENT_CHECKOUT = "checkout"
4848
INTENT_HYBRID = "hybrid"
4949
INTENT_FAQ = "faq"
50+
INTENT_GENERAL = "general"
51+
INTENT_CONTACT = "contact"
5052

5153
# --- Regex Patterns ---
5254
REGEX_PHONE = r"(?:\+380|380|0)\d{9}"

app/services/price_comparator.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,12 @@ async def compare(
5858

5959
sku = woo_result.sku
6060

61+
import re
62+
63+
match = re.search(r"\(([^)]+)\)$", woo_result.name.strip())
64+
if match:
65+
sku = match.group(1).strip()
66+
6167
if not sku:
6268
return PriceComparisonResult(
6369
product_name=woo_result.name,
@@ -89,6 +95,22 @@ async def compare(
8995
logger.info("Cache MISS or FORCE REFRESH", sku=sku, is_checkout=is_checkout)
9096
dc_result = await self.scraper_service.scrape_datacomp(sku)
9197

98+
if not dc_result:
99+
base_name = re.sub(r"\([^)]+\)$", "", woo_result.name).strip()
100+
words = base_name.split()
101+
start_idx = 0
102+
for i, w in enumerate(words):
103+
if re.search(r"[A-Za-z]", w):
104+
start_idx = i
105+
break
106+
cleaned_name = " ".join(words[start_idx:])
107+
108+
if cleaned_name and cleaned_name != sku:
109+
logger.info(
110+
"Fallback scraping by cleaned name", sku=sku, cleaned_name=cleaned_name
111+
)
112+
dc_result = await self.scraper_service.scrape_datacomp(cleaned_name)
113+
92114
if dc_result:
93115
dc_price_uah = dc_result.price_uah
94116
dc_availability_raw = dc_result.availability_status

app/services/rag_engine.py

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
from app.core.config import Settings
99
from app.core.constants import (
1010
INTENT_CHECKOUT,
11+
INTENT_CONTACT,
1112
INTENT_FAQ,
13+
INTENT_GENERAL,
1214
INTENT_HYBRID,
1315
INTENT_PRODUCT,
1416
INTENT_SEARCH,
@@ -32,7 +34,12 @@
3234
from app.services.price_comparator import PriceComparator
3335
from app.services.telegram_service import TelegramService
3436
from app.services.vector_service import VectorService
35-
from app.utils.prompts import INTENT_ANALYZER_PROMPT, NO_CONTEXT_RESPONSE, RAG_SYSTEM_PROMPT
37+
from app.utils.prompts import (
38+
INSTR_CONTACT_MANAGER,
39+
INTENT_ANALYZER_PROMPT,
40+
NO_CONTEXT_RESPONSE,
41+
RAG_SYSTEM_PROMPT,
42+
)
3643

3744
logger = get_logger(__name__)
3845

@@ -92,6 +99,8 @@ async def detect_intent(self, question: str, history_context: str) -> dict[str,
9299
intent_search=INTENT_SEARCH,
93100
intent_checkout=INTENT_CHECKOUT,
94101
intent_hybrid=INTENT_HYBRID,
102+
intent_general=INTENT_GENERAL,
103+
intent_contact=INTENT_CONTACT,
95104
)
96105

97106
try:
@@ -213,6 +222,15 @@ async def _get_intent_context(
213222
requires_lead = res.requires_lead
214223
lead_form_type = res.lead_form_type
215224

225+
elif intent_type == INTENT_CONTACT:
226+
requires_lead = True
227+
lead_form_type = "contact"
228+
system_instructions.append(INSTR_CONTACT_MANAGER)
229+
extracted_links.append(
230+
{"text": LINK_TELEGRAM, "url": self.settings.telegram_contact_url}
231+
)
232+
extracted_links.append({"text": LINK_VIBER, "url": self.settings.viber_contact_url})
233+
216234
return IntentContextResult(
217235
product_facts=product_facts,
218236
system_instructions=system_instructions,

app/utils/prompts.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@
3737
5. CRITICAL (Hybrid): The presence of ANY question about delivery, payment, warranty, or installment plan FORCES the intent to "{intent_hybrid}". This is the only way to return FAQ queries.
3838
-> {{"intent": "{intent_hybrid}", "product_name": "model name (if any)", "strict_query": "commercial query (if any)", "broad_query": "short query (if any)", "category_query": "EXACT category name", "normalized_faq_queries": ["installment plan", "delivery"]}}
3939
6. CRITICAL: For "category_query" choose the EXACT name from this list: [{categories_str}]. If nothing fits - null.
40+
7. If the client uses conversational greetings, small talk, or says thanks (e.g., "привіт", "дякую", "як справи"):
41+
-> {{"intent": "{intent_general}", "product_name": null, "strict_query": null, "broad_query": null, "category_query": null, "normalized_faq_queries": []}}
42+
8. If the client expresses a desire to contact a manager, talk to a human, or leave contacts:
43+
-> {{"intent": "{intent_contact}", "product_name": null, "strict_query": null, "broad_query": null, "category_query": null, "normalized_faq_queries": []}}
4044
4145
Respond ONLY with valid JSON."""
4246

@@ -51,3 +55,4 @@
5155
INSTR_CHECKOUT_PRICE_ISSUE = "Інструкція: Ти щойно актуалізував дані на складі для фіналізації замовлення і виникла необхідність додаткового узгодження деталей постачання. Ввічливо скажи клієнту, що для завершення оформлення потрібне уточнення менеджера, і попроси залишити номер телефону (Telegram/Viber)."
5256
INSTR_DISCOUNT_AVAILABLE = "Інструкція: Для цього товару доступна індивідуальна знижка. Запропонуй клієнту передати номер телефону (Viber/Telegram), щоб менеджер узгодив з ним фінальну ціну."
5357
INSTR_PRICE_CHECKING = "Інформація для тебе: ціна на '{product_name}' зараз перевіряється. Скажи клієнту, що запит передано менеджеру і попроси контакти."
58+
INSTR_CONTACT_MANAGER = "Клієнт хоче зв'язатися з менеджером. ТВОЯ ЗАДАЧА: попросити клієнта залишити свій номер телефону в чаті АБО написати нам у Telegram/Viber (посилання вже згенеровані)."

0 commit comments

Comments
 (0)