引言
传统推荐系统在"冷启动"问题上栽了多少跟头?新用户没有历史行为,新商品没有评分数据,协同过滤和矩阵分解统统束手无策。
LLM的出现为推荐系统带来了新的可能:强大的语义理解能力、丰富的世界知识储备,以及对用户自然语言偏好表达的准确解析。本文将深入探讨LLM增强推荐系统的工程架构,从冷启动到实时个性化的完整实践方案。
一、LLM推荐系统的技术路线
1.1 三种主要范式
LLM推荐系统技术路线
│
├── 1. LLM-as-Recommender(纯LLM推荐)
│ 直接用LLM生成推荐列表
│ 优点:零冷启动,语义理解强
│ 缺点:无法实时,不了解实时库存
│
├── 2. LLM-Enhanced Retrieval(LLM增强检索)
│ LLM处理query,传统算法做检索
│ 优点:可扩展,结合实时数据
│ 缺点:两个系统需要协调
│
└── 3. LLM + Embedding Hybrid(混合架构)
LLM生成语义向量,向量库做ANN检索
优点:语义准确 + 速度快
缺点:向量更新延迟
1.2 冷启动场景的对比
| 场景 | 传统方案 | LLM方案 |
|---|---|---|
| 新用户 | 基于人口统计学推荐 | 自然语言询问偏好 |
| 新商品 | 无法推荐(无行为数据) | 基于商品描述语义推荐 |
| 小众品类 | 稀疏数据,效果差 | 迁移世界知识 |
| 跨域推荐 | 数据隔离,难以迁移 | 语义空间统一 |
二、冷启动解决方案
2.1 对话式偏好采集
from anthropic import Anthropic
from typing import List, Dict, Optional
import json
client = Anthropic()
class ConversationalPreferenceCollector:
"""通过对话采集用户偏好,解决冷启动"""
SYSTEM_PROMPT = """你是一个专业的推荐系统用户偏好分析师。
通过自然对话了解用户偏好,并以结构化JSON格式输出分析结果。
对话原则:
1. 每次只问一个问题
2. 问题要具体、有趣,不要像问卷调查
3. 从用户回答中推断隐性偏好
4. 收集3-5轮信息后,输出完整画像
当你认为信息足够时,输出:
```json
{
"profile_complete": true,
"preferences": {
"explicit": [...], // 用户明确表达的偏好
"inferred": [...], // 从回答中推断的偏好
"exclusions": [...] // 明确不喜欢的
},
"embedding_tags": [...] // 用于向量检索的标签
}
"""
def __init__(self, domain: str = "电商"):
self.domain = domain
self.conversation_history = []
self.collected_profile = None
def start_conversation(self) -> str:
"""开始偏好采集对话"""
initial_prompt = f"请帮我了解用户在{self.domain}领域的偏好,开始采集对话。"
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=500,
system=self.SYSTEM_PROMPT,
messages=[{"role": "user", "content": initial_prompt}]
)
assistant_message = response.content[0].text
self.conversation_history.append({
"role": "assistant",
"content": assistant_message
})
return assistant_message
def process_response(self, user_input: str) -> dict:
"""
处理用户回答
Returns:
{
"question": 下一个问题(如果采集未完成),
"profile": 用户画像(如果采集完成),
"done": bool
}
"""
self.conversation_history.append({
"role": "user",
"content": user_input
})
response = client.messages.create(
model="claude-3-5-sonnet-20241022",
max_tokens=1000,
system=self.SYSTEM_PROMPT,
messages=self.conversation_history
)
assistant_message = response.content[0].text
self.conversation_history.append({
"role": "assistant",
"content": assistant_message
})
# 检查是否完成了画像采集
if "profile_complete" in assistant_message:
try:
# 提取JSON
import re
json_match = re.search(r'\{.*"profile_complete".*\}',
assistant_message, re.DOTALL)
if json_match:
profile = json.loads(json_match.group())
self.collected_profile = profile
return {"question": None, "profile": profile, "done": True}
except:
pass
return {
"question": assistant_message,
"profile": None,
"done": False
}
使用示例
collector = ConversationalPreferenceCollector(domain="书籍") question = collector.start_conversation() print(f"AI: {question}")
模拟用户对话
responses = [ "我喜欢科技和商业类书籍,特别是关于AI和创业的", "最近在读《人工智能:一种现代方法》", "不太喜欢纯理论的书,更喜欢有实际案例的" ]
for user_response in responses: print(f"\n用户: {user_response}") result = collector.process_response(user_response)
if result["done"]:
print("\n✅ 用户画像采集完成:")
print(json.dumps(result["profile"], ensure_ascii=False, indent=2))
break
else:
print(f"AI: {result['question']}")
### 2.2 商品语义向量化
解决新商品冷启动问题:
```python
from openai import OpenAI
import numpy as np
from dataclasses import dataclass
openai_client = OpenAI()
@dataclass
class ProductEmbedding:
product_id: str
title: str
embedding: np.ndarray
metadata: dict
class ProductEmbeddingService:
"""商品语义向量化服务"""
EMBEDDING_MODEL = "text-embedding-3-large"
EMBEDDING_DIM = 3072
def create_product_text(self, product: dict) -> str:
"""将商品信息转化为富语义文本"""
parts = []
if product.get("title"):
parts.append(f"商品名称:{product['title']}")
if product.get("category"):
parts.append(f"类别:{product['category']}")
if product.get("brand"):
parts.append(f"品牌:{product['brand']}")
if product.get("description"):
parts.append(f"描述:{product['description'][:500]}")
if product.get("tags"):
parts.append(f"标签:{', '.join(product['tags'])}")
if product.get("specs"):
specs_str = ', '.join(f"{k}:{v}" for k, v in product['specs'].items())
parts.append(f"规格:{specs_str}")
# 使用LLM生成增强描述(提升语义丰富度)
if len(parts) > 2:
enhanced = self._enhance_description(product)
if enhanced:
parts.append(f"特征总结:{enhanced}")
return '\n'.join(parts)
def _enhance_description(self, product: dict) -> str:
"""使用LLM增强商品描述"""
response = client.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=200,
messages=[{
"role": "user",
"content": f"""请用50字描述这个商品的核心特征和适合人群:
商品:{product.get('title', '')}
类别:{product.get('category', '')}
描述:{product.get('description', '')[:200]}"""
}]
)
return response.content[0].text
def embed_product(self, product: dict) -> ProductEmbedding:
"""生成商品向量"""
text = self.create_product_text(product)
response = openai_client.embeddings.create(
model=self.EMBEDDING_MODEL,
input=text,
encoding_format="float"
)
embedding = np.array(response.data[0].embedding)
return ProductEmbedding(
product_id=product["id"],
title=product.get("title", ""),
embedding=embedding,
metadata={
"category": product.get("category"),
"price": product.get("price"),
"tags": product.get("tags", [])
}
)
def batch_embed(
self,
products: List[dict],
batch_size: int = 20
) -> List[ProductEmbedding]:
"""批量向量化(控制API调用频率)"""
results = []
for i in range(0, len(products), batch_size):
batch = products[i:i+batch_size]
# 批量生成文本
texts = [self.create_product_text(p) for p in batch]
# 批量请求embedding API
response = openai_client.embeddings.create(
model=self.EMBEDDING_MODEL,
input=texts,
encoding_format="float"
)
for j, (product, emb_data) in enumerate(
zip(batch, response.data)
):
results.append(ProductEmbedding(
product_id=product["id"],
title=product.get("title", ""),
embedding=np.array(emb_data.embedding),
metadata={"category": product.get("category")}
))
print(f"已处理 {min(i+batch_size, len(products))}/{len(products)} 个商品")
return results
三、实时个性化推荐
3.1 用户意图解析
class UserIntentParser:
"""解析用户实时意图"""
def parse_query(
self,
query: str,
user_context: dict,
history: List[dict]
) -> dict:
"""
解析用户查询意图
Returns:
{
"intent_type": "search|browse|compare|buy",
"expanded_query": "语义扩展后的查询",
"filters": {"price_max": ..., "category": ...},
"preference_signals": [...]
}
"""
history_summary = ""
if history:
recent = history[-5:]
history_summary = f"\n最近浏览:{', '.join(h.get('title', '') for h in recent)}"
response = client.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=500,
messages=[{
"role": "user",
"content": f"""分析用户的推荐需求,返回JSON格式:
用户查询:{query}
用户等级:{user_context.get('tier', 'standard')}
{history_summary}
返回:
{{
"intent_type": "search|browse|compare|buy",
"expanded_query": "扩展语义后的查询",
"filters": {{}},
"preference_signals": [],
"suggested_categories": []
}}"""
}]
)
try:
return json.loads(response.content[0].text)
except:
return {
"intent_type": "search",
"expanded_query": query,
"filters": {},
"preference_signals": []
}
3.2 推荐理由生成
class RecommendationExplainer:
"""生成个性化推荐理由"""
def generate_explanation(
self,
user_profile: dict,
product: dict,
reason_type: str = "preference"
) -> str:
"""生成推荐理由"""
response = client.messages.create(
model="claude-3-5-haiku-20241022",
max_tokens=100,
messages=[{
"role": "user",
"content": f"""为以下商品生成个性化推荐理由(30字以内,自然口语化):
商品:{product.get('title')}
用户偏好:{', '.join(user_profile.get('preferences', {}).get('explicit', [])[:3])}
推荐原因类型:{reason_type}
示例格式:"因为你喜欢[偏好],这款[产品特点]很适合你""""
}]
)
return response.content[0].text.strip()
四、生产架构设计
4.1 整体系统架构
class LLMRecommendationSystem:
"""完整的LLM推荐系统"""
def __init__(self, vector_store, cache):
self.embedding_service = ProductEmbeddingService()
self.intent_parser = UserIntentParser()
self.explainer = RecommendationExplainer()
self.vector_store = vector_store
self.cache = cache
async def recommend(
self,
user_id: str,
query: str,
context: dict,
top_k: int = 10
) -> List[dict]:
"""主推荐流程"""
# 1. 加载用户画像
user_profile = await self._load_user_profile(user_id)
browse_history = await self._load_history(user_id)
# 2. 解析意图
intent = self.intent_parser.parse_query(
query, context, browse_history
)
# 3. 生成查询向量
query_embedding = self._embed_query(
intent["expanded_query"]
)
# 4. 向量检索
candidates = self.vector_store.search(
query_embedding,
top_k=top_k * 3,
filters=intent.get("filters")
)
# 5. 重排序(基于用户偏好)
reranked = self._rerank_by_preference(
candidates, user_profile
)[:top_k]
# 6. 生成推荐理由
results = []
for product in reranked:
explanation = self.explainer.generate_explanation(
user_profile, product
)
results.append({
**product,
"recommendation_reason": explanation,
"score": product.get("score", 0)
})
return results
def _rerank_by_preference(
self,
candidates: List[dict],
user_profile: dict
) -> List[dict]:
"""基于用户偏好重排序"""
preferences = set(
user_profile.get("preferences", {}).get("explicit", []) +
user_profile.get("preferences", {}).get("inferred", [])
)
exclusions = set(
user_profile.get("preferences", {}).get("exclusions", [])
)
def score(product: dict) -> float:
base_score = product.get("score", 0.5)
tags = set(product.get("metadata", {}).get("tags", []))
pref_match = len(tags & preferences)
excl_match = len(tags & exclusions)
return base_score + pref_match * 0.1 - excl_match * 0.3
return sorted(candidates, key=score, reverse=True)
五、性能优化策略
5.1 关键延迟控制
import asyncio
from functools import lru_cache
class OptimizedRecommender:
"""高性能推荐器"""
# LLM调用缓存(相同意图缓存1小时)
@lru_cache(maxsize=1000)
def _cached_intent_parse(self, query_hash: str) -> dict:
pass
async def fast_recommend(
self,
user_id: str,
query: str,
timeout: float = 2.0
) -> List[dict]:
"""2秒内完成推荐"""
# 并发执行不相互依赖的任务
user_profile_task = asyncio.create_task(
self._load_user_profile(user_id)
)
intent_task = asyncio.create_task(
self._parse_intent_async(query)
)
# 等待两个任务完成
try:
user_profile, intent = await asyncio.wait_for(
asyncio.gather(user_profile_task, intent_task),
timeout=timeout * 0.5 # 各阶段只用一半时间
)
except asyncio.TimeoutError:
# 降级:使用缓存的向量直接检索
user_profile = self._get_default_profile()
intent = {"expanded_query": query, "filters": {}}
# 向量检索(快速)
embedding = self._embed_query(intent["expanded_query"])
candidates = self.vector_store.search(embedding, top_k=20)
# 快速重排(无LLM调用)
reranked = self._fast_rerank(candidates, user_profile)
return reranked[:10]
六、总结
LLM为推荐系统带来的核心价值:
- 零冷启动:通过对话采集偏好,新用户立即获得个性化推荐
- 语义理解:超越关键词匹配,理解用户真实意图
- 新商品推荐:基于语义向量,新商品立即可被推荐
- 推荐可解释性:自动生成自然语言推荐理由,提升用户信任
工程实践要点:
- 将LLM调用集中在低频但高价值的环节(偏好采集、理由生成)
- 实时路径使用向量检索,不依赖LLM保证低延迟
- 用户画像做持久化缓存,避免重复采集
- 设计降级策略,LLM不可用时保持基础推荐能力
LLM推荐系统不是要替代传统协同过滤,而是在冷启动、语义理解、可解释性三个维度上形成有效补充,共同构建更智能的推荐体系。