By Sagar Shankaran, Founder of CallSphere
Learn how to build an AI agent that helps shoppers discover products through natural language search, personalized filtering, side-by-side comparisons, and recommendation engines tailored to individual preferences.
Key takeaways
The average online store carries thousands of SKUs. Customers who cannot find what they want within a few interactions leave — and most never return. Traditional keyword search misses intent. A customer typing "something warm for a mountain trip" gets zero results on a site that indexes by material and garment type.
An AI-powered product discovery agent bridges this gap by understanding natural language queries, mapping them to product attributes, and personalizing results based on browsing history, past purchases, and stated preferences.
The agent sits between the customer and your product catalog. It receives free-text queries, extracts structured intent, queries your catalog database, applies personalization, and returns ranked results. The core loop involves three tools: semantic search, attribute filtering, and comparison.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
flowchart LR
CALLER(["Shopper"])
subgraph TEL["Telephony"]
SIP["Twilio SIP and PSTN"]
end
subgraph BRAIN["E-commerce AI Agent"]
STT["Streaming STT<br/>Deepgram or Whisper"]
NLU{"Intent and<br/>Entity Extraction"}
TOOLS["Tool Calls"]
TTS["Streaming TTS<br/>ElevenLabs or Rime"]
end
subgraph DATA["Live Data Plane"]
CRM[("CRM and Notes")]
CAL[("Calendar and<br/>Schedule")]
KB[("Knowledge Base<br/>and Policies")]
end
subgraph OUT["Outcomes"]
O1(["Order status answered"])
O2(["Return RMA created"])
O3(["Specialist handoff"])
end
CALLER --> SIP --> STT --> NLU
NLU -->|Lookup| TOOLS
TOOLS <--> CRM
TOOLS <--> CAL
TOOLS <--> KB
NLU --> TTS --> SIP --> CALLER
NLU -->|Resolved| O1
NLU -->|Schedule| O2
NLU -->|Escalate| O3
style CALLER fill:#f1f5f9,stroke:#64748b,color:#0f172a
style NLU fill:#4f46e5,stroke:#4338ca,color:#fff
style O1 fill:#059669,stroke:#047857,color:#fff
style O2 fill:#0ea5e9,stroke:#0369a1,color:#fff
style O3 fill:#f59e0b,stroke:#d97706,color:#1f2937
from agents import Agent, Runner, function_tool
from dataclasses import dataclass
from typing import Optional
@dataclass
class ProductResult:
id: str
name: str
price: float
category: str
attributes: dict
score: float
# Simulated catalog with embeddings
PRODUCT_CATALOG = [
{"id": "SKU-001", "name": "Merino Wool Hiking Jacket",
"price": 189.99, "category": "Outerwear",
"attributes": {"warmth": "high", "activity": "hiking", "material": "merino"}},
{"id": "SKU-002", "name": "Down Insulated Parka",
"price": 249.99, "category": "Outerwear",
"attributes": {"warmth": "extreme", "activity": "general", "material": "down"}},
{"id": "SKU-003", "name": "Fleece Midlayer Pullover",
"price": 79.99, "category": "Midlayers",
"attributes": {"warmth": "medium", "activity": "hiking", "material": "fleece"}},
]
@function_tool
def search_products(query: str, max_results: int = 5) -> str:
"""Search the product catalog using natural language."""
query_lower = query.lower()
scored = []
for product in PRODUCT_CATALOG:
score = 0
searchable = f"{product['name']} {product['category']} " \
f"{' '.join(product['attributes'].values())}".lower()
for word in query_lower.split():
if word in searchable:
score += 1
if score > 0:
scored.append({**product, "relevance_score": score})
scored.sort(key=lambda x: x["relevance_score"], reverse=True)
return str(scored[:max_results])
@function_tool
def filter_by_price(min_price: float, max_price: float) -> str:
"""Filter products within a price range."""
results = [p for p in PRODUCT_CATALOG
if min_price <= p["price"] <= max_price]
return str(results)
@function_tool
def compare_products(product_ids: str) -> str:
"""Compare products side by side. Accepts comma-separated IDs."""
ids = [pid.strip() for pid in product_ids.split(",")]
matches = [p for p in PRODUCT_CATALOG if p["id"] in ids]
if len(matches) < 2:
return "Need at least two valid product IDs to compare."
comparison = []
for p in matches:
comparison.append({
"name": p["name"],
"price": p["price"],
"attributes": p["attributes"],
})
return str(comparison)
With the tools defined, wire them into an agent with instructions that guide the conversational flow.
discovery_agent = Agent(
name="Product Discovery Assistant",
instructions="""You are a helpful shopping assistant for an outdoor
gear retailer. Your job is to help customers find the right products.
Guidelines:
- Ask clarifying questions when a query is vague
- Always search the catalog before making recommendations
- When showing results, highlight why each product matches the request
- Offer to compare products when showing multiple options
- Mention price range and key differentiators
- Never invent products that are not in the catalog""",
tools=[search_products, filter_by_price, compare_products],
)
result = Runner.run_sync(
discovery_agent,
"I need something warm for a hiking trip under 200 dollars",
)
print(result.final_output)
Personalization transforms generic search into a tailored experience. Store user preferences and purchase history, then inject them into the agent context.
def build_personalization_context(user_id: str) -> str:
"""Fetch user history and build context string."""
# In production, query your user profile database
user_profile = {
"preferred_brands": ["Patagonia", "Arc'teryx"],
"size": "M",
"past_categories": ["Outerwear", "Footwear"],
"price_sensitivity": "moderate",
"climate": "cold",
}
return (
f"Customer preferences: prefers {', '.join(user_profile['preferred_brands'])}. "
f"Size {user_profile['size']}. Usually buys {', '.join(user_profile['past_categories'])}. "
f"Price sensitivity: {user_profile['price_sensitivity']}. "
f"Lives in a {user_profile['climate']} climate."
)
# Inject personalization into the agent at runtime
personalized_agent = Agent(
name="Personalized Discovery Assistant",
instructions=f"""You are a shopping assistant with knowledge of
this customer. {build_personalization_context("user-123")}
Use these preferences to rank and explain recommendations.""",
tools=[search_products, filter_by_price, compare_products],
)
Production discovery agents must handle empty results gracefully. When no products match, the agent should suggest broadening the search, offer related categories, or ask the customer to adjust their criteria rather than returning a blank response.
@function_tool
def suggest_alternatives(category: str) -> str:
"""Suggest alternative categories when no exact match is found."""
alternatives_map = {
"Outerwear": ["Midlayers", "Vests", "Rain Shells"],
"Footwear": ["Socks", "Insoles", "Gaiters"],
"Midlayers": ["Base Layers", "Outerwear", "Vests"],
}
suggestions = alternatives_map.get(category, ["Popular Items"])
return f"No exact matches. Try browsing: {', '.join(suggestions)}"
Use an embedding model like OpenAI text-embedding-3-small to encode product descriptions into vectors. Store these in a vector database such as Pinecone, Weaviate, or pgvector. At query time, embed the customer query and perform a cosine similarity search against product vectors. This allows the agent to find "warm jacket for mountains" even if no product contains those exact words.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
Add an inventory check tool that queries your inventory management system before presenting results. Filter out-of-stock items by default but offer a "notify me when back in stock" option. Cache inventory status with a short TTL (30-60 seconds) to avoid hammering your inventory API on every search.
Track search-to-cart conversion rate, zero-result rate, average number of agent turns before a product is selected, and click-through rate on recommended items. Compare these against your baseline keyword search. A well-built discovery agent should reduce zero-result rates by 40-60 percent and increase add-to-cart rates measurably.
#ProductDiscovery #ECommerceAI #RecommendationEngine #RetailAI #ShoppingAssistant #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
Las Vegas retail inventory hit 70.7M SF in Q1 2026 with a 4.3% vacancy rate. Tourism + locals drive a unique multilingual call mix. Here is how a 2026 voice agent runs your storefront line.
Voice commerce went from gimmick to revenue channel in 2026. The retail deployments by surface — drive-through, kiosk, in-app — and the conversion data.
When product catalogs get complicated, customers hesitate and bounce. Learn how AI chat and voice agents guide buyers to the right product faster.
Learn how to build an AI agent that recommends accurate clothing sizes by mapping body measurements to brand-specific sizing charts, predicting fit preferences, and reducing return rates in fashion e-commerce.
Build an AI agent that checks real-time store inventory, sets up restock notifications for out-of-stock items, and suggests suitable alternatives — keeping customers engaged instead of bouncing to competitors.
Design and implement an AI recommendation agent that combines user preference modeling, inventory-aware filtering, and LLM-powered explanation generation for personalized product suggestions.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco