In Day 15, we built a recommendation workflow with LangGraph by separating the system into multiple stages such as retrieval, ranking, filtering, and recommendation. However, that workflow still assumed that every user already had enough historical data for the system to understand their preferences. In real recommendation systems, this assumption often fails. A new user may have no reviews, no ratings, and no interaction history at all. This is known as the cold-start problem. Today, we will build a more advanced recommendation workflow with LangGraph that can detect cold-start users and route them to a different recommendation strategy based on explicit preferences or fallback recommendations.
START
↓
Load User History
↓
Has History?
/ \
No Yes
| |
v v
Cold Start Build User Profile
Strategy |
\ /
\ /
→ FAISS Retrieval
↓
Filter Seen Items
↓
Top-K Recommendations
↓
END
Define the LangGraph State:
from typing import TypedDict
import numpy as np
class RecommendationState(TypedDict):
user_id: str
query: str
user_history: list
seen_items: list
user_embedding: list[float]
candidates: list
recommendations: list
Cold-Start Strategy:
def build_cold_start_profile(state):
query = state["query"]
embedding = embedding_model.encode(
[query],
normalize_embeddings=True,
convert_to_numpy=True,
)
embedding = embedding.astype("float32")
print("Cold-start user detected")
print(f"Using explicit preference: {query}")
return {
"user_embedding": embedding[0].tolist()
}
Build the LangGraph:
from langgraph.graph import (
StateGraph,
START,
END,
)
builder = StateGraph(
RecommendationState
)
builder.add_node(
"load_history",
load_user_history,
)
builder.add_node(
"cold_start_profile",
build_cold_start_profile,
)
builder.add_node(
"history_profile",
build_history_profile,
)
builder.add_node(
"retrieve",
retrieve_candidates,
)
builder.add_node(
"filter_seen",
filter_seen_items,
)
Test a Cold-Start User:
result = graph.invoke({
"user_id": "NEW_USER_001",
"query": (
"I want gentle moisturizing "
"skincare for sensitive skin."
),
"user_history": [],
"seen_items": [],
"user_embedding": [],
"candidates": [],
"recommendations": [],
})
Conclusion
Today, we extended our LangGraph recommendation workflow to handle one of the most common real-world recommendation problems: cold start. Instead of assuming that every user already has enough interaction history, our workflow first checks whether historical data is available and then routes the user to a suitable recommendation strategy.
For returning users, we build a profile from previously liked products and use that profile to retrieve similar items. For new users, we fall back to explicit preferences and convert the current request directly into an embedding. Both paths eventually use the same FAISS retrieval stage, which keeps the system modular while allowing different users to follow different recommendation paths.
The key idea is that LangGraph is not only useful for organizing steps. It becomes more valuable when the workflow needs to make decisions based on state.
Reference: