User Feedback Loop 설계 및 개발 - 100-hours-a-week/12-marong-Wiki GitHub Wiki

📍 장소 선호 업데이트 프로그램 개발

preference_update.py를 통해 사용자가 좋아요를 누른 데이터를 기반으로 장소 선호 벡터 업데이트

for user_id in user_ids:
  chroma_user_id = f"user_{user_id}"
  print(f"Processing user_id: {user_id} with chroma_user_id: {chroma_user_id}")
  
  vibe_doc = vibelikes_collection.get(ids=[chroma_user_id], include=["embeddings"])
  menu_doc = menulikes_collection.get(ids=[chroma_user_id], include=["embeddings"])
  start, end = get_last_week_range()
  
  # 별도 선호 벡터가 없는 경우 기본 값은 영벡터
  vibe_embed_vector = np.zeros(768)
  menu_embed_vector = np.zeros(768)
  
  if len(vibe_doc["embeddings"]) > 0:
      vibe_embed_vector = np.array(vibe_doc["embeddings"][0])
      
  if len(menu_doc["embeddings"]) > 0:
      menu_embed_vector = np.array(menu_doc["embeddings"][0])
  ...
  • Backend DB 기반으로 지난 주 장소에 대해 좋아요를 누른 사용자를 조회하여 별도 장소 선호 벡터가 없는 경우 기본 값은 영벡터로 부여
  • Chroma DB에서 벡터가 조회되는 경우 해당 벡터가 유저의 장소, 메뉴 선호 벡터
  • 사용자가 좋아요를 누른 장소의 상호명을 바탕으로 review_collection, menu_collection에서 임베딩 벡터 조회


분기 처리를 통해 MySQL의 Transaction과 유사하게 업데이트 과정 안정성 강화

condition1 = (len(review_result["embeddings"]) > 0 and len(vibe_like_history["ids"]) == 0)
        condition2 = (len(menu_result["embeddings"]) > 0 and len(menu_like_history["ids"]) == 0)
        # print(f"Processing place: {place_name}, conditions: {condition1}, {condition2}")
        
        if condition1 and condition2:
            vibe_vec = np.array(review_result["embeddings"][0])
            menu_vec = np.array(menu_result["embeddings"][0])
            ...
  • 리뷰 컬렉션과 메뉴 컬렉션 모두에서 해당 **상호명(place_name)**에 대한 임베딩 벡터가 조회되어야 함
  • 사용자가 해당 장소에 대해 리뷰 또는 메뉴 좋아요 기록이 없는 경우에만, 아래 벡터가 업데이트됨:
장소 분위기 선호 벡터
메뉴 선호 벡터
  • 업데이트 방식: → 각 임베딩 벡터의 **10% 비중(weight)**을 반영하여 선호 벡터에 누적
# 변화량 출력
  def safe_cosine_similarity(a, b):
    norm_a = np.linalg.norm(a)
    norm_b = np.linalg.norm(b)
    if norm_a == 0 or norm_b == 0:
        return None
    return np.dot(a, b) / (norm_a * norm_b)

  def l2_distance(a, b):
    return np.linalg.norm(a - b)

  vibe_cos = safe_cosine_similarity(original_vibe_vector, vibe_embed_vector)
  menu_cos = safe_cosine_similarity(original_menu_vector, menu_embed_vector)

  vibe_l2 = l2_distance(original_vibe_vector, vibe_embed_vector)
  menu_l2 = l2_distance(original_menu_vector, menu_embed_vector)

  print(f"[user_id: {user_id}] vibe_cos_sim={vibe_cos if vibe_cos is not None else '값 없음'}, vibe_l2={vibe_l2:.4f}")
  print(f"[user_id: {user_id}] menu_cos_sim={menu_cos if menu_cos is not None else '값 없음'}, menu_l2={menu_l2:.4f}")
  • 선호 벡터는 RecommendPlace 추천 클래스 객체의 속성으로 초기화되어 추천 로직에 반영됨
  • 향후 Chroma DB에서 검색 시 유저의 선호 벡터가 없는 경우 더미 벡터인 영벡터로 초기화, 선호 벡터가 있는 경우 이를 유저의 임베딩 벡터로 반환
⚠️ **GitHub.com Fallback** ⚠️