Skip to content
Learn Agentic AI14 min read0 views

Build a Gift Recommendation Agent: Preference Analysis, Budget Matching, and Purchase Links

Build an AI gift recommendation agent that gathers recipient preferences through conversation, searches a product catalog, filters by budget, and personalizes suggestions — the perfect gift-finding assistant.

Why Build a Gift Recommendation Agent

Finding the right gift requires understanding the recipient's interests, respecting your budget, avoiding duplicates, and navigating thousands of product options. Most people default to generic gifts because the research effort is too high. A gift recommendation agent solves this by conducting a structured preference interview, searching a product catalog, applying budget constraints, and providing personalized recommendations with purchase links.

This tutorial builds a complete gift recommendation system with preference gathering, product search, budget filtering, and personalized scoring.

Project Setup

mkdir gift-agent && cd gift-agent
python -m venv venv && source venv/bin/activate
pip install openai-agents pydantic
mkdir -p src
touch src/__init__.py src/preferences.py src/catalog.py
touch src/recommender.py src/agent.py

Step 1: Preference Model

The preference model captures structured information about the gift recipient.

# src/preferences.py
from pydantic import BaseModel

class RecipientProfile(BaseModel):
    name: str = ""
    relationship: str = ""  # friend, partner, parent, colleague
    age_range: str = ""  # child, teen, young adult, adult, senior
    interests: list[str] = []
    hobbies: list[str] = []
    dislikes: list[str] = []
    occasion: str = ""  # birthday, holiday, anniversary, thank you
    budget_min: float = 0
    budget_max: float = 100

class PreferenceManager:
    def __init__(self):
        self.profiles: dict[str, RecipientProfile] = {}

    def create_profile(self, name: str) -> str:
        self.profiles[name.lower()] = RecipientProfile(name=name)
        return f"Created profile for {name}"

    def update_profile(self, name: str, **kwargs) -> str:
        profile = self.profiles.get(name.lower())
        if not profile:
            return f"No profile found for {name}"
        for key, value in kwargs.items():
            if hasattr(profile, key):
                if isinstance(getattr(profile, key), list):
                    current = getattr(profile, key)
                    if isinstance(value, list):
                        current.extend(value)
                    else:
                        current.append(value)
                else:
                    setattr(profile, key, value)
        return f"Updated {name}'s profile: {kwargs}"

    def get_profile(self, name: str) -> RecipientProfile | None:
        return self.profiles.get(name.lower())

    def get_profile_summary(self, name: str) -> str:
        profile = self.profiles.get(name.lower())
        if not profile:
            return f"No profile for {name}"
        lines = [
            f"Name: {profile.name}",
            f"Relationship: {profile.relationship or 'not set'}",
            f"Age range: {profile.age_range or 'not set'}",
            f"Interests: {', '.join(profile.interests) or 'none yet'}",
            f"Hobbies: {', '.join(profile.hobbies) or 'none yet'}",
            f"Dislikes: {', '.join(profile.dislikes) or 'none yet'}",
            f"Occasion: {profile.occasion or 'not set'}",
            f"Budget: ${profile.budget_min}-${profile.budget_max}",
        ]
        return "\n".join(lines)

pref_manager = PreferenceManager()

Step 2: Product Catalog

# src/catalog.py
from pydantic import BaseModel

class Product(BaseModel):
    id: str
    name: str
    category: str
    price: float
    tags: list[str]  # interest/hobby tags for matching
    description: str
    url: str
    rating: float  # 1.0 to 5.0

PRODUCTS: list[Product] = [
    Product(id="p001", name="Wireless Noise-Canceling Headphones",
            category="electronics", price=89.99,
            tags=["music", "technology", "travel", "podcasts"],
            description="Premium sound quality with 30-hour battery",
            url="https://example.com/headphones", rating=4.7),
    Product(id="p002", name="Gourmet Coffee Sampler Box",
            category="food", price=34.99,
            tags=["coffee", "cooking", "foodie"],
            description="12 single-origin coffees from around the world",
            url="https://example.com/coffee-sampler", rating=4.5),
    Product(id="p003", name="Leather-Bound Journal",
            category="stationery", price=28.00,
            tags=["writing", "reading", "journaling", "art"],
            description="Handcrafted journal with 240 acid-free pages",
            url="https://example.com/journal", rating=4.8),
    Product(id="p004", name="Smart Fitness Tracker",
            category="electronics", price=59.99,
            tags=["fitness", "health", "running", "technology"],
            description="Heart rate, sleep tracking, GPS, waterproof",
            url="https://example.com/fitness-tracker", rating=4.4),
    Product(id="p005", name="Indoor Herb Garden Kit",
            category="home", price=45.00,
            tags=["gardening", "cooking", "plants", "home"],
            description="Self-watering planter with basil, mint, cilantro seeds",
            url="https://example.com/herb-garden", rating=4.6),
    Product(id="p006", name="Board Game Collection",
            category="games", price=39.99,
            tags=["games", "family", "social", "strategy"],
            description="Set of 3 award-winning strategy board games",
            url="https://example.com/board-games", rating=4.7),
    Product(id="p007", name="Portable Watercolor Paint Set",
            category="art", price=32.00,
            tags=["art", "painting", "creative", "travel"],
            description="24 colors in a travel-friendly tin case",
            url="https://example.com/watercolor-set", rating=4.5),
    Product(id="p008", name="Bluetooth Book Light",
            category="electronics", price=24.99,
            tags=["reading", "technology", "books"],
            description="Rechargeable clip-on light with warm and cool modes",
            url="https://example.com/book-light", rating=4.3),
    Product(id="p009", name="Yoga Mat and Block Set",
            category="fitness", price=42.00,
            tags=["yoga", "fitness", "health", "wellness"],
            description="Non-slip mat with cork block and carrying strap",
            url="https://example.com/yoga-set", rating=4.6),
    Product(id="p010", name="Personalized Star Map",
            category="decor", price=55.00,
            tags=["romantic", "art", "home", "personalized"],
            description="Custom star map for any date and location",
            url="https://example.com/star-map", rating=4.9),
    Product(id="p011", name="Cooking Masterclass Subscription",
            category="subscription", price=49.99,
            tags=["cooking", "foodie", "learning"],
            description="3-month access to online cooking classes",
            url="https://example.com/cooking-class", rating=4.4),
    Product(id="p012", name="Noise Machine with Nature Sounds",
            category="home", price=35.00,
            tags=["sleep", "wellness", "relaxation", "health"],
            description="20 sound options with timer and night light",
            url="https://example.com/noise-machine", rating=4.5),
]

def search_products(
    tags: list[str] | None = None,
    category: str = "",
    min_price: float = 0,
    max_price: float = 9999,
) -> list[Product]:
    results = PRODUCTS
    if min_price > 0 or max_price < 9999:
        results = [
            p for p in results
            if min_price <= p.price <= max_price
        ]
    if category:
        results = [
            p for p in results
            if p.category.lower() == category.lower()
        ]
    if tags:
        tag_set = {t.lower() for t in tags}
        results = [
            p for p in results
            if tag_set & {t.lower() for t in p.tags}
        ]
    return results

Step 3: Recommendation Engine

The recommender scores products against the recipient profile.

# src/recommender.py
from src.catalog import Product, search_products
from src.preferences import RecipientProfile

def score_product(
    product: Product, profile: RecipientProfile,
) -> float:
    score = 0.0
    all_interests = set(
        i.lower() for i in profile.interests + profile.hobbies
    )
    product_tags = set(t.lower() for t in product.tags)
    overlap = all_interests & product_tags
    score += len(overlap) * 2.0

    dislikes = set(d.lower() for d in profile.dislikes)
    if dislikes & product_tags:
        score -= 10.0

    score += product.rating * 0.5
    if profile.budget_min <= product.price <= profile.budget_max:
        score += 1.0

    return round(score, 2)

def get_recommendations(
    profile: RecipientProfile, top_n: int = 5,
) -> list[dict]:
    products = search_products(
        tags=profile.interests + profile.hobbies,
        min_price=profile.budget_min,
        max_price=profile.budget_max,
    )
    if not products:
        products = search_products(
            min_price=profile.budget_min,
            max_price=profile.budget_max,
        )

    scored = []
    for product in products:
        s = score_product(product, profile)
        if s > 0:
            scored.append({"product": product, "score": s})

    scored.sort(key=lambda x: x["score"], reverse=True)
    return scored[:top_n]

def format_recommendations(recs: list[dict]) -> str:
    if not recs:
        return "No matching products found."
    lines = ["=== Gift Recommendations ===\n"]
    for i, rec in enumerate(recs, 1):
        p = rec["product"]
        lines.append(f"{i}. {p.name}")
        lines.append(f"   Price: {p.price:.2f} USD | Rating: {p.rating}/5")
        lines.append(f"   {p.description}")
        lines.append(f"   Why: matches tags {', '.join(p.tags)}")
        lines.append(f"   Buy: {p.url}")
        lines.append(f"   Match score: {rec['score']}\n")
    return "\n".join(lines)

Step 4: Assemble the Agent

# src/agent.py
import asyncio
import json
from agents import Agent, Runner, function_tool
from src.preferences import pref_manager
from src.recommender import get_recommendations, format_recommendations

@function_tool
def create_recipient(name: str) -> str:
    """Create a new gift recipient profile."""
    return pref_manager.create_profile(name)

@function_tool
def set_recipient_details(
    name: str,
    relationship: str = "",
    age_range: str = "",
    interests: str = "",
    hobbies: str = "",
    dislikes: str = "",
    occasion: str = "",
    budget_min: float = 0,
    budget_max: float = 100,
) -> str:
    """Update recipient profile details."""
    kwargs: dict = {}
    if relationship:
        kwargs["relationship"] = relationship
    if age_range:
        kwargs["age_range"] = age_range
    if interests:
        kwargs["interests"] = [
            i.strip() for i in interests.split(",")
        ]
    if hobbies:
        kwargs["hobbies"] = [
            h.strip() for h in hobbies.split(",")
        ]
    if dislikes:
        kwargs["dislikes"] = [
            d.strip() for d in dislikes.split(",")
        ]
    if occasion:
        kwargs["occasion"] = occasion
    if budget_min > 0:
        kwargs["budget_min"] = budget_min
    if budget_max != 100:
        kwargs["budget_max"] = budget_max
    return pref_manager.update_profile(name, **kwargs)

@function_tool
def view_recipient(name: str) -> str:
    """View a recipient's profile."""
    return pref_manager.get_profile_summary(name)

@function_tool
def find_gifts(name: str, top_n: int = 5) -> str:
    """Get gift recommendations for a recipient."""
    profile = pref_manager.get_profile(name)
    if not profile:
        return f"No profile found for {name}"
    recs = get_recommendations(profile, top_n)
    return format_recommendations(recs)

gift_agent = Agent(
    name="Gift Recommendation Agent",
    instructions="""You are a thoughtful gift recommendation agent.
Help users find the perfect gift by gathering information about
the recipient through friendly conversation. Ask about:
1. Who the gift is for (relationship, age)
2. Their interests and hobbies
3. Things they dislike or already have
4. The occasion and budget

After gathering enough info, use the find_gifts tool to
generate personalized recommendations. Explain why each
suggestion matches the recipient. Be warm and helpful.""",
    tools=[
        create_recipient, set_recipient_details,
        view_recipient, find_gifts,
    ],
)

async def main():
    result = await Runner.run(
        gift_agent,
        "I need a gift for my friend Sarah. She's into yoga, "
        "cooking, and reading. Budget is 30 to 50 dollars. "
        "It's for her birthday. She doesn't like electronics.",
    )
    print(result.final_output)

if __name__ == "__main__":
    asyncio.run(main())

The agent creates Sarah's profile, records her interests and dislikes, applies the budget filter, and recommends matching products — excluding electronics because of her stated dislike — with purchase links and explanations for each suggestion.

See AI Voice Agents Handle Real Calls

Book a free demo or calculate how much you can save with AI voice automation.

Extending the System

Real product data. Replace the static catalog with API calls to Amazon Product Advertising API, Etsy, or a web scraping service. The scoring and filtering logic remains the same.

Gift history. Add a past_gifts field to the profile and filter out previously given items. This prevents the agent from recommending something the recipient already has.

Seasonal awareness. Add seasonal product tags and boost scores for seasonally appropriate gifts. A cozy blanket scores higher in December than in July.

FAQ

How does the scoring algorithm decide which gifts are best?

The algorithm assigns points based on three factors: tag overlap between the recipient's interests and the product's tags (2 points per match), product rating (0.5 multiplied by rating), and budget fit (1 bonus point if the price falls within the stated budget). Products matching any of the recipient's dislikes receive a 10-point penalty, effectively removing them from recommendations.

Can the agent learn from previous gift successes?

Yes. Add a rate_gift tool that records whether the recipient liked a previous gift. Store these ratings and use them to adjust the scoring weights over time. If the recipient consistently loves cooking-related gifts, boost the weight for the "cooking" tag. This creates a personalized scoring model that improves with each gift-giving occasion.

How would I handle multiple recipients at once?

The PreferenceManager already supports multiple profiles keyed by name. Ask the agent to find gifts for each person in sequence, and it will maintain separate profiles and generate independent recommendations. You could add a compare_gifts tool that ensures no two recipients get the same item if you are buying for a group event.


#GiftRecommendation #AIAgent #Python #ECommerce #Personalization #AgenticAI #LearnAI #AIEngineering

Share this article
C

CallSphere Team

Expert insights on AI voice agents and customer communication automation.

Try CallSphere AI Voice Agents

See how AI voice agents work for your industry. Live demo available -- no signup required.