[HCR-369] 데이터 타입별 검색#58
Conversation
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! 이 PR은 추천 시스템의 핵심 로직을 개선하여 추천 결과의 다양성을 확보하는 데 중점을 둡니다. 기존에는 특정 상품 타입(예: 모바일 요금제)으로 추천이 쏠리는 경향이 있었으나, 이제는 데이터 타입별로 후보를 분산하여 검색함으로써 보다 균형 잡힌 추천을 제공할 수 있게 됩니다. 또한, 특정 라이브러리(pgvector, aiokafka)가 설치되지 않은 환경에서도 애플리케이션이 안정적으로 동작하도록 의존성 처리를 유연하게 변경했습니다. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
| # 2-1) 타입별 검색으로 후보 풀 분산(최소 1개/타입 유도) | ||
| seen: set[int] = set() | ||
| product_ids: list[int] = [] | ||
| for ptype in MAIN_PRODUCT_TYPES: | ||
| r = await session.execute( | ||
| SEARCH_SIMILAR_BY_TYPE_SQL, | ||
| { | ||
| "query_vec": query_vec, | ||
| "exclude_ids": exclude_ids, | ||
| "k": RETRIEVAL_CANDIDATES_K, | ||
| "ptype": ptype, | ||
| "k": RETRIEVAL_PER_TYPE_K, | ||
| }, | ||
| ) | ||
| rows = result.fetchall() | ||
| product_ids = [r[0] for r in rows] | ||
| for row in r.fetchall(): | ||
| pid = row[0] | ||
| if pid in seen: | ||
| continue | ||
| product_ids.append(pid) | ||
| seen.add(pid) | ||
|
|
||
| # 2-2) 부족분은 기존 전체 검색(가중치 boost 포함 가능)으로 보충 | ||
| if len(product_ids) < RETRIEVAL_CANDIDATES_K: | ||
| if use_type_boost: | ||
| result = await session.execute( | ||
| SEARCH_SIMILAR_WITH_TYPE_BOOST_SQL, | ||
| { | ||
| "query_vec": query_vec, | ||
| "exclude_ids": exclude_ids, | ||
| "k": RETRIEVAL_CANDIDATES_K, | ||
| "boost_type1": boost_type1, | ||
| "boost1": boost1, | ||
| "boost_type2": boost_type2, | ||
| "boost2": boost2, | ||
| }, | ||
| ) | ||
| else: | ||
| result = await session.execute( | ||
| SEARCH_SIMILAR_SQL, | ||
| { | ||
| "query_vec": query_vec, | ||
| "exclude_ids": exclude_ids, | ||
| "k": RETRIEVAL_CANDIDATES_K, | ||
| }, | ||
| ) | ||
| for row in result.fetchall(): | ||
| pid = row[0] | ||
| if pid in seen: | ||
| continue | ||
| product_ids.append(pid) | ||
| seen.add(pid) | ||
| if len(product_ids) >= RETRIEVAL_CANDIDATES_K: | ||
| break |
There was a problem hiding this comment.
이 블록의 후보 상품 검색 로직이 _run_fallback_recommendation 함수(1078-1114행)에서도 거의 동일하게 반복되고 있습니다. 코드 중복은 유지보수를 어렵게 만들고 버그 발생 가능성을 높입니다.
이 로직을 별도의 private 헬퍼 함수로 추출하는 것을 고려해 보세요. 예를 들어, 다음과 같은 함수를 만들 수 있습니다.
async def _retrieve_candidate_products(
session: AsyncSession,
query_vec: list[float],
exclude_ids: list[int],
use_type_boost: bool,
boost_type1: str,
boost1: float,
boost_type2: str,
boost2: float,
) -> list[int]:
# ... 여기에 중복 로직 구현 ...이렇게 하면 _run_recommendation_with_context와 _run_fallback_recommendation 두 곳에서 이 헬퍼 함수를 호출하여 코드를 더 깔끔하고 재사용 가능하게 만들 수 있습니다. _run_fallback_recommendation의 경우 use_type_boost를 False로 전달하고 관련 boost 인자들을 기본값으로 넘기면 됩니다.
| try: | ||
| from pgvector.asyncpg import register_vector | ||
| except Exception: # pragma: no cover | ||
| register_vector = None # type: ignore[assignment] |
There was a problem hiding this comment.
except Exception은 너무 광범위합니다. ImportError와 같이 더 구체적인 예외를 잡는 것이 좋습니다. 이렇게 하면 pgvector 라이브러리가 없을 때 발생하는 ImportError만 처리하고, 다른 예기치 않은 오류는 숨기지 않게 됩니다.
| try: | |
| from pgvector.asyncpg import register_vector | |
| except Exception: # pragma: no cover | |
| register_vector = None # type: ignore[assignment] | |
| try: | |
| from pgvector.asyncpg import register_vector | |
| except ImportError: # pragma: no cover | |
| register_vector = None # type: ignore[assignment] |
| try: | ||
| from aiokafka import AIOKafkaProducer | ||
| except Exception: # pragma: no cover | ||
| # 평가 스크립트/로컬 환경에서 Kafka 의존성이 없을 수 있다. | ||
| # Kafka 미설정 시 publish_recommendation_to_kafka에서 안전하게 스킵한다. | ||
| AIOKafkaProducer = None # type: ignore[assignment] |
There was a problem hiding this comment.
except Exception은 너무 광범위하여 의도치 않은 다른 오류까지 숨길 수 있습니다. aiokafka 라이브러리가 설치되지 않았을 때 발생하는 ImportError만 명시적으로 처리하는 것이 더 안전합니다.
| try: | |
| from aiokafka import AIOKafkaProducer | |
| except Exception: # pragma: no cover | |
| # 평가 스크립트/로컬 환경에서 Kafka 의존성이 없을 수 있다. | |
| # Kafka 미설정 시 publish_recommendation_to_kafka에서 안전하게 스킵한다. | |
| AIOKafkaProducer = None # type: ignore[assignment] | |
| try: | |
| from aiokafka import AIOKafkaProducer | |
| except ImportError: # pragma: no cover | |
| # 평가 스크립트/로컬 환경에서 Kafka 의존성이 없을 수 있다. | |
| # Kafka 미설정 시 publish_recommendation_to_kafka에서 안전하게 스킵한다. | |
| AIOKafkaProducer = None # type: ignore[assignment] |
📝작업 내용
👀변경 사항
🎫 Jira Ticket
#️⃣관련 이슈