| name | item-to-item |
| description | Use when user needs to find similar items. Triggers on: similar items, related content, related products, more like this, similar products, related articles, content-based recommendation, you may also like. |
Item-to-Item Recommendation
Find similar items based on content similarity — power "Customers also viewed" and "Related articles" features.
When to Activate
Activate this skill when:
- User needs "More like this" functionality
- User mentions "similar products", "related articles", "you may also like"
- User wants to build content-based recommendations
- User's recommendation should be item-centric (not user-centric)
Do NOT activate when:
- User needs personalized recommendations based on user history → use
user-to-item
- User needs semantic search → use
semantic-search
- User needs to find duplicates → use
duplicate-detection
Interactive Flow
Step 1: Understand Item Type
"What type of items are you recommending?"
| Item Type | Key Features | Embedding Strategy |
|---|
| Products | Title, description, specs | Text + optional image |
| Articles | Title, content | Text (title + abstract) |
| Videos | Title, description, thumbnail | Text + keyframe |
| Music | Title, artist, genre | Text + audio features |
Which item type? (or describe)
Step 2: Determine Similarity Scope
"Should similar items be in the same category?"
A) Same category only: Similar phones among phones
B) Cross-category: Phone might recommend case, charger
C) Configurable: User chooses at runtime
Step 3: Confirm Configuration
"Based on your requirements:
- Embedding: Title + Description (BGE-large)
- Category filter: Optional at search time
- Diversity: Enabled (avoid too-similar results)
Proceed? (yes / adjust [what])"
Core Concepts
Mental Model: Store Shelf Arrangement
Think of item-to-item as arranging a store shelf:
- Put similar products near each other
- When customer picks one, show nearby items
- "If you like this, check out these neighbors"
┌─────────────────────────────────────────────────────────┐
│ Item-to-Item Recommendation │
│ │
│ Current Item: iPhone 15 Pro │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Get Embedding │ │
│ │ from storage │ │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
│ │ Vector Search │ Find nearest neighbors │
│ │ (exclude self) │ in embedding space │
│ └────────┬────────┘ │
│ │ │
│ ▼ │
│ Similar Items: │
│ ┌─────┬─────┬─────┬─────┐ │
│ │iPh15│Pix 8│Sam24│iPh14│ │
│ │0.95 │0.87 │0.85 │0.82 │ (similarity scores) │
│ └─────┴─────┴─────┴─────┘ │
│ │
│ "Customers who viewed iPhone 15 Pro also viewed..." │
└─────────────────────────────────────────────────────────┘
Item-to-Item vs User-to-Item
| Aspect | Item-to-Item | User-to-Item |
|---|
| Input | Current item | User profile |
| Logic | Find similar items | Match user preferences |
| Use case | "Related products" | "For you" homepage |
| Cold start | No issue | Needs user data |
| Personalization | None | High |
Implementation
from pymilvus import MilvusClient, DataType
from sentence_transformers import SentenceTransformer
class ItemToItemRecommender:
def __init__(self, uri: str = "./milvus.db"):
self.client = MilvusClient(uri=uri)
self.model = SentenceTransformer('BAAI/bge-large-en-v1.5')
self.collection_name = "item_to_item"
self._init_collection()
def _init_collection(self):
if self.client.has_collection(self.collection_name):
return
schema = self.client.create_schema()
schema.add_field("id", DataType.VARCHAR, is_primary=True, max_length=64)
schema.add_field("title", DataType.VARCHAR, max_length=1024)
schema.add_field("description", DataType.VARCHAR, max_length=65535)
schema.add_field("category", DataType.VARCHAR, max_length=256)
schema.add_field("tags", DataType.ARRAY, element_type=DataType.VARCHAR, max_capacity=20, max_length=64)
schema.add_field("embedding", DataType.FLOAT_VECTOR, dim=1024)
index_params = self.client.prepare_index_params()
index_params.add_index(field_name="embedding", index_type=, metric_type=)
index_params.add_index(field_name=, index_type=)
.client.create_collection(
collection_name=.collection_name,
schema=schema,
index_params=index_params
)
():
texts = [ item items]
embeddings = .model.encode(texts).tolist()
data = []
item, emb (items, embeddings):
data.append({
: item[],
: item[],
: item[],
: item.get(, ),
: item.get(, []),
: emb
})
.client.insert(collection_name=.collection_name, data=data)
() -> :
results = .client.get(
collection_name=.collection_name,
ids=[item_id],
output_fields=[, ]
)
results:
[]
embedding = results[][]
category = results[][]
filter_expr =
same_category category:
filter_expr +=
similar = .client.search(
collection_name=.collection_name,
data=[embedding],
=filter_expr,
limit=limit,
output_fields=[, , , ]
)
[{: hit[][],
: hit[][],
: hit[][],
: hit[]}
hit similar[]]
() -> :
embedding = .model.encode(text).tolist()
filter_expr = category
results = .client.search(
collection_name=.collection_name,
data=[embedding],
=filter_expr filter_expr ,
limit=limit,
output_fields=[, , ]
)
[{: hit[][],
: hit[][],
: hit[][],
: hit[]} hit results[]]
() -> :
candidates = .get_similar(item_id, limit=limit * )
selected = []
candidate candidates:
is_diverse =
s selected:
candidate[] > diversity_threshold s[] == candidate[]:
is_diverse =
is_diverse:
selected.append(candidate)
(selected) >= limit:
selected
recommender = ItemToItemRecommender()
recommender.add_items([
{: , : , : , : , : [, ]},
{: , : , : , : , : []},
{: , : , : , : , : [, ]},
{: , : , : , : , : [, ]},
])
similar = recommender.get_similar(, limit=)
()
item similar:
()
similar_phones = recommender.get_similar(, limit=, same_category=)
diverse = recommender.get_diverse_similar(, limit=)
Optimization Strategies
1. Multi-Feature Fusion
def create_rich_embedding(self, item: dict) -> list:
"""Combine text and image features"""
text = f"{item['title']} {item['description']}"
text_emb = self.text_model.encode(text)
if item.get("image_path"):
image = Image.open(item["image_path"])
image_emb = self.image_model.encode(image)
return np.concatenate([text_emb * 0.7, image_emb * 0.3]).tolist()
return text_emb.tolist()
2. Popularity Boost
def get_similar_with_popularity(self, item_id: str, limit: int = 10) -> list:
"""Boost popular items in recommendations"""
similar = self.get_similar(item_id, limit=limit * 2)
for item in similar:
popularity = self.get_item_popularity(item["id"])
item["final_score"] = item["score"] * 0.8 + popularity * 0.2
similar.sort(key=lambda x: x["final_score"], reverse=True)
return similar[:limit]
3. Business Rules
def apply_business_rules(self, item_id: str, similar: list) -> list:
"""Apply business rules to recommendations"""
current_item = self.get_item(item_id)
filtered = []
for item in similar:
if not item.get("in_stock", True):
continue
if item.get("price", 0) > current_item.get("price", 0) * 1.5:
continue
filtered.append(item)
return filtered
Common Pitfalls
❌ Pitfall 1: Including Self in Results
Problem: Item recommends itself
Fix: Always exclude current item
filter_expr = f'id != "{item_id}"'
❌ Pitfall 2: Too Similar Results
Problem: All recommendations are basically the same
Fix: Add diversity threshold
if similarity > 0.95:
continue
❌ Pitfall 3: Ignoring Availability
Problem: Recommending out-of-stock items
Fix: Add availability filter
filter_expr = f'id != "{item_id}" and in_stock == true'
❌ Pitfall 4: Only Text-Based
Problem: Visually similar items not recommended
Fix: Include image embeddings for visual products
When to Level Up
| Need | Upgrade To |
|---|
| Personalized recommendations | user-to-item |
| Include multiple modalities | Combine text + image |
| Real-time updates | Add streaming pipeline |
| A/B testing | Track click-through rates |
References
- Vertical guides:
verticals/
- User recommendations:
rec-system:user-to-item
- Embedding models:
core:embedding