🔥 0
0
Lesson 8 of 10 25 min +225 XP

Product Comparison Agent

"Should I get the iPhone or Samsung?" "Which laptop is better for video editing?" Comparison questions are complex - the answer depends on what matters to the customer.

Bad Comparison

"Phone A has 8GB RAM. Phone B has 12GB RAM. Phone A costs $999. Phone B costs $1099."

Just specs, no insight

Good Comparison

"For photography, Phone A's 50MP sensor and larger pixels capture better low-light shots. Phone B wins on video with 8K recording. Given you mentioned travel photography, I'd recommend A."

Personalized insight

Understanding the Comparison Request

from openai import OpenAI
from typing import Dict, List, Optional
import json

client = OpenAI()

def parse_comparison_request(query: str) -> Dict:
    """Extract products and use case from comparison query"""

    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": """Parse the product comparison request. Return JSON with:
- products: List of specific products mentioned (or product types if generic)
- use_case: What they want to use it for (if mentioned)
- priorities: What features seem important to them
- budget: Any budget constraints mentioned
- comparison_type: "specific" (named products) or "category" (find best in category)

Examples:
"iPhone 15 vs Samsung S24" → specific products
"best laptop for video editing under $1500" → category search"""
            },
            {"role": "user", "content": query}
        ],
        response_format={"type": "json_object"}
    )

    return json.loads(response.choices[0].message.content)


# Examples
query1 = "Compare the MacBook Pro M3 and Dell XPS 15 for video editing"
parsed1 = parse_comparison_request(query1)
# {
#   "products": ["MacBook Pro M3", "Dell XPS 15"],
#   "use_case": "video editing",
#   "priorities": ["performance", "video rendering"],
#   "budget": null,
#   "comparison_type": "specific"
# }

query2 = "What's the best wireless earbuds under $150 for working out?"
parsed2 = parse_comparison_request(query2)
# {
#   "products": [],
#   "use_case": "working out",
#   "priorities": ["durability", "sweat resistance", "secure fit"],
#   "budget": 150,
#   "comparison_type": "category"
# }

The Comparison Agent

from pinecone import Pinecone

class ProductComparisonAgent:
    def __init__(self, product_index, openai_client, db):
        self.index = product_index
        self.client = openai_client
        self.db = db

    def compare(self, query: str) -> Dict:
        """Handle a product comparison request"""

        # Step 1: Parse the request
        parsed = parse_comparison_request(query)

        # Step 2: Get the products to compare
        if parsed["comparison_type"] == "specific":
            products = self._get_specific_products(parsed["products"])
        else:
            products = self._find_products_for_category(
                use_case=parsed["use_case"],
                budget=parsed.get("budget"),
                priorities=parsed.get("priorities", [])
            )

        if len(products) < 2:
            return self._handle_insufficient_products(parsed, products)

        # Step 3: Extract comparable specs
        comparison_data = self._extract_comparison_data(products)

        # Step 4: Generate comparison with recommendation
        response = self._generate_comparison(
            products=products,
            comparison_data=comparison_data,
            use_case=parsed.get("use_case"),
            priorities=parsed.get("priorities", [])
        )

        return {
            "response": response,
            "products": products,
            "comparison_data": comparison_data
        }

    def _get_specific_products(self, product_names: List[str]) -> List[Dict]:
        """Retrieve specific products by name using semantic search"""

        products = []
        for name in product_names:
            # Semantic search to find the product
            query_embedding = self.client.embeddings.create(
                model="text-embedding-3-small",
                input=name
            ).data[0].embedding

            results = self.index.query(
                vector=query_embedding,
                top_k=1,
                include_metadata=True
            )

            if results.matches:
                product_data = self._enrich_product(results.matches[0].metadata)
                products.append(product_data)

        return products

    def _find_products_for_category(
        self,
        use_case: str,
        budget: Optional[float],
        priorities: List[str]
    ) -> List[Dict]:
        """Find top products for a category based on use case"""

        # Build search query combining use case and priorities
        search_query = f"Best product for {use_case}. " + " ".join(priorities)

        query_embedding = self.client.embeddings.create(
            model="text-embedding-3-small",
            input=search_query
        ).data[0].embedding

        # Build filter
        filter_dict = {"in_stock": True}
        if budget:
            filter_dict["price"] = {"$lte": budget}

        results = self.index.query(
            vector=query_embedding,
            top_k=10,
            include_metadata=True,
            filter=filter_dict
        )

        # Enrich and return top 3-5 products
        products = [
            self._enrich_product(m.metadata)
            for m in results.matches[:5]
        ]

        return products

    def _enrich_product(self, product_data: Dict) -> Dict:
        """Enrich product with full specs and reviews"""

        product_id = product_data.get("product_id") or product_data.get("id")

        # Get full product details from database
        full_product = self.db.get_product(product_id)

        if full_product:
            return {
                **product_data,
                "specifications": full_product.specifications,
                "reviews_summary": self._summarize_reviews(product_id),
                "pros_cons": self._extract_pros_cons(product_id)
            }

        return product_data

Extracting Comparable Specifications

def _extract_comparison_data(self, products: List[Dict]) -> Dict:
    """Extract and align specifications for comparison"""

    # Get all unique spec keys
    all_specs = set()
    for product in products:
        specs = product.get("specifications", {})
        all_specs.update(specs.keys())

    # Build comparison matrix
    comparison = {
        "products": [p["name"] for p in products],
        "prices": [p.get("price", "N/A") for p in products],
        "ratings": [p.get("rating", "N/A") for p in products],
        "specs": {}
    }

    for spec_key in all_specs:
        comparison["specs"][spec_key] = [
            p.get("specifications", {}).get(spec_key, "N/A")
            for p in products
        ]

    # Add pros/cons
    comparison["pros"] = [p.get("pros_cons", {}).get("pros", []) for p in products]
    comparison["cons"] = [p.get("pros_cons", {}).get("cons", []) for p in products]

    return comparison


def _extract_pros_cons(self, product_id: str) -> Dict:
    """Extract pros and cons from reviews using RAG"""

    # Get review chunks for this product
    results = self.index.query(
        vector=self._embed(f"pros and cons of product {product_id}"),
        top_k=10,
        filter={"product_id": product_id, "type": "review"}
    )

    reviews_text = "\n".join([m.metadata.get("text", "") for m in results.matches])

    response = self.client.chat.completions.create(
        model="gpt-4o-mini",
        messages=[
            {
                "role": "system",
                "content": """Extract pros and cons from these product reviews.
Return JSON with:
- pros: List of 3-5 most mentioned positives
- cons: List of 3-5 most mentioned negatives
Be specific and cite frequency if notable."""
            },
            {"role": "user", "content": reviews_text}
        ],
        response_format={"type": "json_object"}
    )

    return json.loads(response.choices[0].message.content)

Generating the Comparison

def _generate_comparison(
    self,
    products: List[Dict],
    comparison_data: Dict,
    use_case: Optional[str],
    priorities: List[str]
) -> str:
    """Generate a helpful comparison with recommendation"""

    # Format products for LLM
    products_context = self._format_products_for_comparison(products, comparison_data)

    use_case_context = f"\nCustomer's use case: {use_case}" if use_case else ""
    priorities_context = f"\nCustomer's priorities: {', '.join(priorities)}" if priorities else ""

    response = self.client.chat.completions.create(
        model="gpt-4o",
        messages=[
            {
                "role": "system",
                "content": """You are a product comparison expert. Create a helpful, personalized comparison.

Structure your response:
1. **Quick Summary** - One sentence on the key difference
2. **Comparison Table** - Format as markdown table with key specs
3. **Deep Dive** - For each major category (performance, value, etc.), explain which wins and why
4. **Pros & Cons** - Bullet points for each product
5. **Recommendation** - Clear recommendation based on the customer's use case

Guidelines:
- Focus on specs that matter for the use case
- Explain technical specs in plain language
- Be honest about trade-offs
- If it's close, say so
- Give a clear recommendation with reasoning"""
            },
            {
                "role": "user",
                "content": f"""Compare these products:

{products_context}
{use_case_context}
{priorities_context}

Provide a comparison and recommendation."""
            }
        ],
        temperature=0.7
    )

    return response.choices[0].message.content


def _format_products_for_comparison(self, products: List[Dict], comparison_data: Dict) -> str:
    """Format products into structured text for LLM"""

    formatted = []

    for i, product in enumerate(products):
        product_info = f"""
### {product['name']}
**Price:** ${product.get('price', 'N/A')}
**Rating:** {product.get('rating', 'N/A')}/5 ({product.get('review_count', 0)} reviews)

**Specifications:**
"""
        for spec, values in comparison_data["specs"].items():
            product_info += f"- {spec}: {values[i]}\n"

        if comparison_data["pros"][i]:
            product_info += "\n**Pros (from reviews):**\n"
            for pro in comparison_data["pros"][i][:3]:
                product_info += f"- {pro}\n"

        if comparison_data["cons"][i]:
            product_info += "\n**Cons (from reviews):**\n"
            for con in comparison_data["cons"][i][:3]:
                product_info += f"- {con}\n"

        formatted.append(product_info)

    return "\n---\n".join(formatted)

Example Comparison Output

Customer: "Compare the Sony WH-1000XM5 and Apple AirPods Max for someone who takes a lot of video calls"
Agent:
**Quick Summary:** For video calls, the Sony WH-1000XM5 edges ahead with better microphone quality and longer battery life, while the AirPods Max offers tighter Apple ecosystem integration. **Comparison Table:** | Feature | Sony WH-1000XM5 | AirPods Max | |---------|-----------------|-------------| | Price | $349 | $549 | | Battery | 30 hours | 20 hours | | ANC | Excellent | Excellent | | Microphones | 4 beamforming | 9 (but Apple-only features) | | Weight | 250g | 384g | | Call Quality Rating | 4.8/5 | 4.5/5 | **For Video Calls:** The Sony XM5 wins here. Its 4 beamforming microphones with advanced wind noise reduction keep your voice crystal clear. Customers specifically mention "colleagues say I sound great on Zoom" (47 reviews). The lighter weight also helps for all-day wear during meeting-heavy days. The AirPods Max has excellent microphones too, but many of its advanced features (like Spatial Audio on calls) only work with Apple devices. If your work laptop is Windows, you'll miss out. **Pros & Cons:** **Sony WH-1000XM5:** - Pros: Superior call quality, 30-hour battery, folds flat, works great with any device - Cons: Touch controls take getting used to, no premium case included **AirPods Max:** - Pros: Seamless Apple switching, premium build, Spatial Audio for media - Cons: Heavier, shorter battery, $200 more expensive, Smart Case doesn't protect headband **My Recommendation:** For video calls, go with the **Sony WH-1000XM5**. You'll save $200, get 10 more hours of battery, and get better call quality across all your devices. The AirPods Max makes sense only if you're deeply in the Apple ecosystem and value its seamless device switching more than raw call performance.

Handling Edge Cases

def _handle_insufficient_products(self, parsed: Dict, products: List[Dict]) -> Dict:
    """Handle when we can't find enough products to compare"""

    if len(products) == 0:
        return {
            "response": f"""I couldn't find the products you mentioned. Could you:
1. Check the spelling of the product names
2. Be more specific (e.g., "MacBook Pro 14-inch M3" instead of "MacBook")
3. Or describe what you're looking for and I'll find options to compare""",
            "products": [],
            "comparison_data": None
        }

    if len(products) == 1:
        product = products[0]
        return {
            "response": f"""I found **{product['name']}**, but couldn't find the other product(s) to compare.

Would you like me to:
1. Tell you about {product['name']}
2. Find similar products to compare it with
3. Search for a different product""",
            "products": products,
            "comparison_data": None
        }


def _handle_incompatible_products(self, products: List[Dict]) -> Optional[str]:
    """Warn if comparing very different product types"""

    categories = [p.get("category", "").split(">")[0] for p in products]
    unique_categories = set(categories)

    if len(unique_categories) > 1:
        return f"""Note: You're comparing products from different categories ({', '.join(unique_categories)}).
I'll do my best, but they serve different purposes. Let me know if you'd like suggestions for similar products instead."""

    return None

Key Takeaways

  • Use case is everything - The best product depends on what it's for
  • Extract real pros/cons from reviews - User feedback is more valuable than marketing copy
  • Give a clear recommendation - Don't just present data, guide the decision
  • Explain the trade-offs - Help customers understand what they'd gain or lose

Next up: Hybrid Search Strategies - Combine keyword and semantic search for the best of both worlds.

🧠 Quick Quiz

Test your understanding of this lesson.

1

What makes a good product comparison different from a simple feature list?

2

How should the agent handle comparing products with different feature sets?

3

Why is understanding the customer's use case critical for comparisons?

Order History Lookup Agent