What Is an E-Commerce Support Agent That Converts?
An e-commerce support agent that converts is an AI-powered conversational system designed to do more than just answer questions. It actively guides shoppers through the buying journey — from product discovery to checkout — while handling objections, recommending products, and closing sales. Unlike traditional FAQ bots that deflect tickets, a conversion-focused agent is built with revenue generation as its primary metric.
These agents blend three core capabilities:
- Support — answering product questions, shipping policies, return windows, and order status inquiries
- Merchandising — surfacing relevant products based on customer intent, browsing context, and inventory signals
- Closing — handling objections ("Is this worth the price?"), offering social proof, and nudging toward cart addition or checkout completion
Under the hood, a conversion agent typically combines a large language model (LLM) with structured e-commerce data sources — product catalogs, real-time inventory, pricing engines, and CRM systems — to produce grounded, persuasive responses that respect business constraints.
Why It Matters
Every support interaction on an e-commerce site is a conversion opportunity. A customer asking "Does this jacket run large?" is signaling purchase intent. A traditional support setup might return a sizing chart link; a conversion agent returns the chart plus a tailored recommendation ("Based on your fit preference, the relaxed-fit version in medium would work well — it's in stock and ships free today").
The business impact is measurable across three dimensions:
- Revenue per session — Conversion agents have been shown to lift assisted-session conversion rates by 15–35% compared to passive FAQ bots, because they reduce the friction between curiosity and purchase
- Average order value — By intelligently cross-selling and upselling within the conversation flow (e.g., "Customers who bought this jacket also grabbed these hiking socks — want me to add them?"), AOV increases naturally without intrusive pop-ups
- Support deflection without abandonment — The agent resolves common inquiries autonomously, but unlike ticket-deflection bots, it never leaves the customer feeling blocked; every answer includes a path back to shopping
For developers, building this agent means solving an interesting engineering challenge: orchestrating LLM reasoning with deterministic business logic so the system is both conversational and reliably on-brand.
Core Architecture Overview
Before diving into code, let's map the architecture. A production-grade conversion agent typically has these layers:
- Orchestration Layer — Manages conversation state, routes intents, and decides when to call tools
- Tool Layer — Functions that fetch live data (product search, inventory, pricing, cart operations)
- Prompt Layer — System prompts that encode the brand voice, merchandising rules, and conversion playbook
- Guardrails Layer — Validates outputs, ensures pricing accuracy, and prevents off-brand claims
The diagram below illustrates the request flow:
User Message
│
▼
┌──────────────────────────┐
│ Intent Classifier │ → product_inquiry / order_status / general_chat
└──────────────────────────┘
│
▼
┌──────────────────────────┐
│ Context Aggregator │ → pulls session data, cart contents, browse history
└──────────────────────────┘
│
▼
┌──────────────────────────┐
│ Tool Dispatcher │ → calls search_product(), get_inventory(), etc.
└──────────────────────────┘
│
▼
┌──────────────────────────┐
│ LLM Response Generator │ → constructs final message with conversion hooks
└──────────────────────────┘
│
▼
┌──────────────────────────┐
│ Output Validator │ → checks prices, links, brand tone compliance
└──────────────────────────┘
│
▼
Response to User
Step 1: Setting Up the Tool Layer
The tool layer is the agent's connection to reality. Without it, the LLM hallucinates product names, prices, and availability. Start by building clean, typed functions that your agent can call.
Product Search Tool
# tools/product_tools.py
from typing import List, Optional, Dict, Any
from dataclasses import dataclass
import json
@dataclass
class Product:
id: str
name: str
price: float
currency: str
in_stock: bool
category: str
attributes: Dict[str, Any]
rating: float
review_count: int
image_url: str
url: str
def search_products(
query: str,
category_filter: Optional[str] = None,
max_price: Optional[float] = None,
sort_by: str = "relevance",
limit: int = 5
) -> List[Product]:
"""
Search the product catalog. Returns a list of matching products
with full metadata for the agent to reference.
"""
# In production, this queries Elasticsearch, Algolia, or your search DB
# Here we simulate with a mock catalog
catalog = _load_catalog() # your actual data source
results = []
query_lower = query.lower()
for item in catalog:
if query_lower in item["name"].lower() or query_lower in item.get("description", "").lower():
if category_filter and item["category"] != category_filter:
continue
if max_price and item["price"] > max_price:
continue
results.append(Product(
id=item["id"],
name=item["name"],
price=item["price"],
currency=item.get("currency", "USD"),
in_stock=item["inventory_count"] > 0,
category=item["category"],
attributes=item.get("attributes", {}),
rating=item.get("rating", 0.0),
review_count=item.get("review_count", 0),
image_url=item.get("image_url", ""),
url=f"https://store.example.com/products/{item['id']}"
))
# Sort results
if sort_by == "price_asc":
results.sort(key=lambda p: p.price)
elif sort_by == "price_desc":
results.sort(key=lambda p: p.price, reverse=True)
elif sort_by == "rating":
results.sort(key=lambda p: p.rating, reverse=True)
return results[:limit]
def _load_catalog() -> List[Dict[str, Any]]:
"""Mock catalog — replace with your actual data pipeline."""
return [
{
"id": "prod_001",
"name": "Trailblazer Hiking Boot",
"description": "Waterproof leather hiking boot with Vibram sole",
"price": 189.99,
"currency": "USD",
"inventory_count": 42,
"category": "footwear",
"attributes": {"color": "Brown", "sizes": ["7", "8", "9", "10", "11", "12"]},
"rating": 4.7,
"review_count": 384,
"image_url": "/images/trailblazer-boot.jpg"
},
{
"id": "prod_002",
"name": "Summit Down Jacket",
"description": "800-fill down jacket for extreme cold, packs into its own pocket",
"price": 349.00,
"currency": "USD",
"inventory_count": 15,
"category": "outerwear",
"attributes": {"color": "Navy", "sizes": ["S", "M", "L", "XL"]},
"rating": 4.9,
"review_count": 127,
"image_url": "/images/summit-jacket.jpg"
}
]
Inventory and Pricing Tool
# tools/inventory_tools.py
from typing import Dict, Optional
import datetime
def check_inventory(product_id: str, size: Optional[str] = None, color: Optional[str] = None) -> Dict:
"""
Returns real-time inventory status for a specific product/variant.
Always call this before promising availability to a customer.
"""
# In production, query your inventory management system or warehouse API
inventory_data = {
"prod_001": {
"total_available": 42,
"variants": {
"7_Brown": 5, "8_Brown": 8, "9_Brown": 12,
"10_Brown": 10, "11_Brown": 5, "12_Brown": 2
},
"low_stock_threshold": 3,
"next_restock_date": "2025-02-01"
},
"prod_002": {
"total_available": 15,
"variants": {
"S_Navy": 4, "M_Navy": 6, "L_Navy": 4, "XL_Navy": 1
},
"low_stock_threshold": 3,
"next_restock_date": None
}
}
if product_id not in inventory_data:
return {"available": False, "error": "Product not found in inventory system"}
product_inventory = inventory_data[product_id]
if size and color:
variant_key = f"{size}_{color}"
variant_qty = product_inventory["variants"].get(variant_key, 0)
return {
"product_id": product_id,
"variant": variant_key,
"available": variant_qty > 0,
"quantity": variant_qty,
"low_stock": variant_qty <= product_inventory["low_stock_threshold"],
"next_restock": product_inventory.get("next_restock_date")
}
return {
"product_id": product_id,
"total_available": product_inventory["total_available"],
"variants": product_inventory["variants"],
"low_stock_warning": product_inventory["total_available"] <= product_inventory["low_stock_threshold"]
}
def get_current_price(product_id: str, customer_id: Optional[str] = None) -> Dict:
"""
Returns the customer-eligible price, accounting for discounts,
loyalty tiers, and active promotions.
"""
base_prices = {"prod_001": 189.99, "prod_002": 349.00}
if product_id not in base_prices:
return {"error": "Product not found"}
price = base_prices[product_id]
discount_applied = None
# Check customer-specific pricing (loyalty, abandoned-cart offers, etc.)
if customer_id:
loyalty_discount = _get_loyalty_discount(customer_id)
if loyalty_discount:
price = round(price * (1 - loyalty_discount), 2)
discount_applied = f"Loyalty tier: {loyalty_discount*100}% off"
return {
"product_id": product_id,
"list_price": base_prices[product_id],
"customer_price": price,
"discount": discount_applied,
"currency": "USD"
}
def _get_loyalty_discount(customer_id: str) -> Optional[float]:
"""Mock loyalty lookup — replace with your CRM integration."""
loyal_customers = {"cust_abc123": 0.10} # 10% off for VIP tier
return loyal_customers.get(customer_id)
Cart Operations Tool
# tools/cart_tools.py
from typing import Dict, List, Optional
import uuid
def get_cart_contents(session_id: str) -> Dict:
"""
Retrieves the current cart for a browsing session.
Returns items, subtotal, and any applied promotions.
"""
# In production, query your cart service or session store
carts = {
"sess_xyz789": {
"items": [
{"product_id": "prod_001", "name": "Trailblazer Hiking Boot",
"variant": "9_Brown", "quantity": 1, "unit_price": 189.99},
],
"subtotal": 189.99,
"item_count": 1,
"promotions": ["FREE_SHIPPING"],
"estimated_total": 189.99
}
}
return carts.get(session_id, {"items": [], "subtotal": 0, "item_count": 0})
def add_to_cart(
session_id: str,
product_id: str,
variant: str,
quantity: int = 1
) -> Dict:
"""
Adds an item to the cart. Returns updated cart state.
The agent should call this after customer confirms intent to purchase.
"""
# In production, call your cart service API
# This is a simplified mock
current_cart = get_cart_contents(session_id)
new_item = {
"product_id": product_id,
"variant": variant,
"quantity": quantity,
"unit_price": 189.99 # Would come from pricing service
}
current_cart["items"].append(new_item)
current_cart["subtotal"] = sum(item["unit_price"] * item["quantity"] for item in current_cart["items"])
current_cart["item_count"] = len(current_cart["items"])
current_cart["estimated_total"] = current_cart["subtotal"]
return {
"success": True,
"cart": current_cart,
"action": f"Added {variant} of {product_id} to cart"
}
Step 2: Building the Prompt Layer (The Conversion Playbook)
The system prompt is where you encode conversion behavior. It's the single most important piece of the agent — it defines the tone, the merchandising strategy, and the guardrails. Here's a battle-tested template:
# prompts/system_prompt.py
CONVERSION_AGENT_PROMPT = """
You are a knowledgeable shopping assistant for OutdoorGear Co., a premium outdoor equipment retailer.
## YOUR ROLE
You help customers find the right products, answer questions accurately, and guide them toward confident purchases. You are warm, enthusiastic, and never pushy. Your goal is to be so helpful that customers naturally want to buy.
## CONVERSATION RULES
### Always Do:
- Answer the customer's direct question FIRST, before any product suggestion
- Use tools to fetch live data — never guess prices, inventory, or product details
- When inventory is low on an item the customer wants, mention it gently ("This is popular — only 2 left in your size")
- After answering, offer ONE relevant next step: a product recommendation, a size check, or a cart action
- Use the customer's name if available in context
- Keep responses concise but warm — 2-4 sentences max before asking a question
### Never Do:
- Make up product specifications, prices, or availability
- Pressure the customer ("You should buy this now!")
- Recommend products that are out of stock without noting alternatives
- Suggest adding to cart unless the customer has shown clear purchase intent
- Use generic phrases like "How can I help you today?" after the first exchange
## MERCHANDISING PLAYBOOK
When the customer shows intent (asks about a product, compares options, or says "I'm looking for..."), follow this sequence:
1. **Validate** — Acknowledge their need ("Great choice — the Trailblazer is our best-rated boot")
2. **Inform** — Share a key detail that matters for their use case, pulled from product attributes
3. **Check** — Verify a preference ("Would you prefer something more lightweight, or is ankle support your priority?")
4. **Recommend** — Suggest a specific variant or complementary product, with reasoning
5. **Bridge to Cart** — If they respond positively, offer to add it: "Want me to add the size 9 Brown to your cart? It's in stock and ships free."
## OBJECTION HANDLING
When a customer expresses hesitation about price:
- Acknowledge the concern ("That's fair — it is an investment")
- Provide value context ("These boots use full-grain leather and a Vibram sole — customers typically get 4+ years of heavy use")
- Offer social proof ("They're rated 4.7 from 384 reviews")
- If available, mention any applicable discount or promotion
- Never argue with the customer's perception
## PRODUCT LINKING RULES
- When mentioning a product, always reference it by its exact name
- Cross-sells should be relevant to the primary product's use case
- The cross-sell ratio: after every 3 product-focused responses, offer 1 complementary item suggestion
## TONE
- Professional but friendly. Use "great," "perfect," "excellent" sparingly
- Match the customer's level of expertise (don't talk down to beginners, don't oversimplify for experts)
- Be specific, never vague. "This jacket has 800-fill down" not "This jacket is warm"
"""
Step 3: Orchestrating the Agent Loop
Now we wire everything together. The orchestrator manages the conversation loop: classify the intent, gather context, call tools, generate the response, validate it, and return it. Here's a production-ready implementation using the OpenAI API with tool calling:
# agent/orchestrator.py
import json
import uuid
from typing import Dict, List, Any, Optional
from dataclasses import dataclass, field
from datetime import datetime
import openai # or your LLM provider of choice
from tools.product_tools import search_products, Product
from tools.inventory_tools import check_inventory, get_current_price
from tools.cart_tools import get_cart_contents, add_to_cart
from prompts.system_prompt import CONVERSION_AGENT_PROMPT
@dataclass
class ConversationState:
session_id: str
customer_id: Optional[str]
customer_name: Optional[str]
messages: List[Dict[str, Any]] = field(default_factory=list)
cart_items: List[Dict] = field(default_factory=list)
browse_context: Dict[str, Any] = field(default_factory=dict)
last_product_viewed: Optional[str] = None
conversion_stage: str = "discovery" # discovery | consideration | decision | post_purchase
class ConversionAgent:
def __init__(self, api_key: str, model: str = "gpt-4o"):
self.client = openai.OpenAI(api_key=api_key)
self.model = model
self.tools = self._build_tool_definitions()
def _build_tool_definitions(self) -> List[Dict]:
"""Define the tools the model can call, with strict schemas."""
return [
{
"type": "function",
"function": {
"name": "search_products",
"description": "Search the product catalog by query string. Returns matching products with prices, ratings, and stock status.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "Search terms (e.g., 'waterproof hiking boots')"
},
"category_filter": {
"type": "string",
"description": "Optional category to narrow results"
},
"max_price": {
"type": "number",
"description": "Optional maximum price filter"
},
"sort_by": {
"type": "string",
"enum": ["relevance", "price_asc", "price_desc", "rating"],
"description": "Sorting strategy"
},
"limit": {
"type": "integer",
"description": "Max results to return (default 5)"
}
},
"required": ["query"]
}
}
},
{
"type": "function",
"function": {
"name": "check_inventory",
"description": "Check real-time inventory for a product, optionally filtered by size and color.",
"parameters": {
"type": "object",
"properties": {
"product_id": {"type": "string", "description": "The product ID to check"},
"size": {"type": "string", "description": "Optional size filter"},
"color": {"type": "string", "description": "Optional color filter"}
},
"required": ["product_id"]
}
}
},
{
"type": "function",
"function": {
"name": "get_current_price",
"description": "Get the customer-specific price for a product, including any applicable discounts.",
"parameters": {
"type": "object",
"properties": {
"product_id": {"type": "string"},
"customer_id": {"type": "string", "description": "Optional customer ID for loyalty pricing"}
},
"required": ["product_id"]
}
}
},
{
"type": "function",
"function": {
"name": "get_cart_contents",
"description": "Retrieve the current contents of the customer's shopping cart.",
"parameters": {
"type": "object",
"properties": {
"session_id": {"type": "string"}
},
"required": ["session_id"]
}
}
},
{
"type": "function",
"function": {
"name": "add_to_cart",
"description": "Add a product variant to the customer's cart. Only call when the customer explicitly agrees.",
"parameters": {
"type": "object",
"properties": {
"session_id": {"type": "string"},
"product_id": {"type": "string"},
"variant": {"type": "string", "description": "Variant identifier like '9_Brown'"},
"quantity": {"type": "integer", "description": "Quantity to add (default 1)"}
},
"required": ["session_id", "product_id", "variant"]
}
}
}
]
def _execute_tool(self, tool_name: str, arguments: Dict) -> Any:
"""Dispatch tool calls to the appropriate function."""
if tool_name == "search_products":
return search_products(**arguments)
elif tool_name == "check_inventory":
return check_inventory(**arguments)
elif tool_name == "get_current_price":
return get_current_price(**arguments)
elif tool_name == "get_cart_contents":
return get_cart_contents(**arguments)
elif tool_name == "add_to_cart":
return add_to_cart(**arguments)
else:
return {"error": f"Unknown tool: {tool_name}"}
def process_message(
self,
user_message: str,
state: ConversationState
) -> Dict[str, Any]:
"""
Process a single user message through the agent loop.
Returns the assistant's response and updated conversation state.
"""
# Build the messages array for the LLM call
system_message = self._build_system_message(state)
messages = [{"role": "system", "content": system_message}]
messages.extend(state.messages[-20:]) # Keep context window manageable
messages.append({"role": "user", "content": user_message})
# First LLM call — may request tool usage
response = self.client.chat.completions.create(
model=self.model,
messages=messages,
tools=self.tools,
tool_choice="auto",
temperature=0.7,
max_tokens=600
)
response_message = response.choices[0].message
# Handle tool calls if the model requested them
if response_message.tool_calls:
# Process each tool call
tool_results = []
for tool_call in response_message.tool_calls:
tool_name = tool_call.function.name
arguments = json.loads(tool_call.function.arguments)
result = self._execute_tool(tool_name, arguments)
tool_results.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps(result),
"name": tool_name
})
# Add the assistant's tool-call message and all tool results
messages.append(response_message)
messages.extend(tool_results)
# Second LLM call to synthesize tool results into a natural response
final_response = self.client.chat.completions.create(
model=self.model,
messages=messages,
temperature=0.7,
max_tokens=400
)
assistant_text = final_response.choices[0].message.content
else:
assistant_text = response_message.content
# Validate output before returning
validated_text = self._validate_output(assistant_text)
# Update conversation state
state.messages.append({"role": "user", "content": user_message})
state.messages.append({"role": "assistant", "content": validated_text})
# Detect conversion stage transitions
state.conversion_stage = self._detect_stage(user_message, validated_text, state)
return {
"response": validated_text,
"conversation_state": state,
"conversion_stage": state.conversion_stage
}
def _build_system_message(self, state: ConversationState) -> str:
"""Enrich the system prompt with live context."""
context_blocks = []
if state.customer_name:
context_blocks.append(f"Current customer: {state.customer_name}")
if state.last_product_viewed:
context_blocks.append(f"Last product customer viewed: {state.last_product_viewed}")
if state.cart_items:
item_names = [item.get("name", item.get("product_id")) for item in state.cart_items]
context_blocks.append(f"Items currently in cart: {', '.join(item_names)}")
context_blocks.append(f"Conversion stage: {state.conversion_stage}")
context_blocks.append(f"Session ID: {state.session_id}")
if context_blocks:
context_section = "\n\n## CURRENT CONTEXT\n" + "\n".join(f"- {block}" for block in context_blocks)
return CONVERSION_AGENT_PROMPT + context_section
return CONVERSION_AGENT_PROMPT
def _validate_output(self, text: str) -> str:
"""
Guardrails: ensure the output is safe and compliant.
In production, add price regex validation, link checking, PII scanning.
"""
if not text or len(text.strip()) < 5:
return "I'd be happy to help you find the right gear! What are you looking for today?"
# Strip any hallucinated prices that don't match our catalog
# This is a simplified check — production systems use regex + catalog cross-reference
banned_phrases = ["guaranteed lowest price", "we price match", "best price anywhere"]
for phrase in banned_phrases:
if phrase.lower() in text.lower():
# Replace with compliant language
text = text.replace(phrase, "great value")
return text.strip()
def _detect_stage(self, user_msg: str, assistant_msg: str, state: ConversationState) -> str:
"""
Classify the conversion stage based on conversation signals.
"""
user_lower = user_msg.lower()
# Decision signals
decision_keywords = ["add to cart", "buy", "purchase", "checkout", "order"]
if any(kw in user_lower for kw in decision_keywords):
return "decision"
# Consideration signals
consideration_keywords = ["compare", "versus", "vs", "difference between",
"worth it", "price", "expensive", "alternative"]
if any(kw in user_lower for kw in consideration_keywords):
return "consideration"
# Discovery signals
discovery_keywords = ["looking for", "do you have", "show me", "recommend",
"need", "searching", "help me find"]
if any(kw in user_lower for kw in discovery_keywords):
return "discovery"
# If customer mentioned cart or purchase in previous turns
if state.conversion_stage == "decision":
return "decision"
return state.conversion_stage
# =============================================
# EXAMPLE USAGE
# =============================================
if __name__ == "__main__":
agent = ConversionAgent(api_key="sk-your-key-here")
state = ConversationState(
session_id="sess_xyz789",
customer_id="cust_abc123",
customer_name="Alex",
last_product_viewed="Trailblazer Hiking Boot"
)
# Simulate a conversation
messages = [
"I'm looking for warm jackets for a winter hiking trip in Colorado.",
"The Summit Down Jacket looks nice, but $349 seems steep. Is it worth it?",
"Okay, you've convinced me. Can you add the medium Navy to my cart?"
]
for msg in messages:
print(f"\n{'='*60}")
print(f"USER: {msg}")
result = agent.process_message(msg, state)
print(f"AGENT: {result['response']}")
print(f"STAGE: {result['conversion_stage']}")
state = result['conversation_state']
Step 4: Intent Classification (Optional Enhancement)
For high-volume stores, you may want a fast pre-classifier before the full agent loop. This lets you route simple queries (order status, store hours) to lightweight handlers while reserving the LLM for complex shopping interactions:
# agent/intent_classifier.py
from enum import Enum
from typing import Tuple
import re
class Intent(Enum):
PRODUCT_INQUIRY = "product_inquiry" # "Tell me about the Summit jacket"
PRODUCT_SEARCH = "product_search" # "Show me waterproof boots under $200"
SIZE_FIT = "size_fit" # "Does this run large or small?"
PRICE_QUESTION = "price_question" # "Is there a discount on this?"
COMPARISON = "comparison" # "How does X compare to Y?"
ORDER_STATUS = "order_status" # "Where is my order #12345?"
RETURN_POLICY = "return_policy" # "Can I return if it doesn't fit?"
SHIPPING_INFO = "shipping_info" # "When will this arrive?"
ADD_TO_CART = "add_to_cart" # Explicit cart action
GENERAL_CHAT = "general_chat" # Greetings, thanks, off-topic
OBJECTION = "objection" # Price/value hesitation
class IntentClassifier:
"""
Fast regex + keyword classifier for routing.
For production, consider a fine-tuned small model or embedding-based classifier.
"""
def classify(self, message: str, conversation_stage: str) -> Tuple[Intent, float]:
msg = message.lower().strip()
# Order status patterns
order_patterns = [
r"order\s*(?:#|number|id)?\s*\d+",
r"where\s*(?:is|are)\s*my\s*(?:order|package|delivery)",
r"tracking",
r"shipping\s*(?:status|update)"
]
for pattern in order_patterns:
if re.search(pattern, msg):
return Intent.ORDER_STATUS, 0.95
# Return policy patterns
return_keywords = ["return policy", "can i return", "refund", "exchange",
"money back", "return window", "return period"]
if any(kw in msg for kw in return_keywords):
return Intent.RETURN_POLICY, 0.90
# Cart action
cart_keywords = ["add to cart", "add it", "put it in my cart", "add the"]
if any(kw in msg for kw in cart_keywords):
return Intent.ADD_TO_CART, 0.92
# Comparison
comparison_keywords = ["compare", "versus", " vs ", "difference between",
"which is better", "or the"]
if any(kw in msg for kw in comparison_keywords):
return Intent.COMPARISON, 0.88
# Price objection
objection_keywords = ["expensive", "too much", "worth it", "worth the price",
"overpriced", "cheaper", "budget"]
if any(kw in msg for kw in objection_keywords) and conversation_stage == "consideration":
return Intent.OBJECTION, 0.85
# Size/fit
size_keywords = ["size", "fit", "large", "small", "tight", "loose",
"measurements", "sizing chart", "true to size"]
if any(kw in msg for kw in size_keywords):
return Intent.SIZE_FIT, 0.87
# Product search
search_keywords = ["looking for", "show me", "find me", "do you have",
"recommend", "suggest", "search"]
if any(kw in msg for kw in search_keywords):
return Intent.PRODUCT_SEARCH, 0.86
# Product inquiry (catch-all for product-related questions)
if any(char in msg for char in ["?", "tell me", "what is", "details"]):
return Intent.PRODUCT_INQUIRY, 0.75
return Intent.GENERAL_CHAT, 0.70
Step 5: Building a Response Validator (Guardrails)
A conversion agent handles money — it must never misrepresent prices, link to dead URLs, or make claims your products can't support. The validator runs as a post-processing step on every response:
# guardrails/validator.py
import re
from typing import Dict, List, Optional, Tuple
class ResponseValidator:
"""
Post-generation validation for agent responses.
Catches hallucinations and policy violations before the message reaches the user.
"""
def __init__(self, catalog_price_map: Dict[str, float], brand_voice_rules: Dict = None):
self.price_map = catalog_price_map # {product_name: correct_price}
self.brand_voice_rules = brand_voice_rules or {}
self.banned_terms = [
"guaranteed", "promise", "always works", "never fails",
"cheapest", "lowest price guaranteed"
]
def validate(self, response: str, products_referenced: List[str]) -> Dict:
"""
Run all validation checks. Returns a dict with:
- passed: bool
- corrections: list of applied fixes
- flags: list of warnings for logging
"""
corrections = []
flags = []
# Check 1: Price accuracy
price_check = self._validate_prices(response)