DOCS/journey/troubleshooting/250827_claude_news_keyword_imbalance.md
Claude-51124 22557e7132 docs: 오래된 트러블슈팅 아카이브 및 구조 정리
- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동
- book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서)
- 빈 폴더 제거 (journey/assets/*)
2025-11-17 14:06:05 +09:00

4.7 KiB

뉴스 스킬 복수 키워드 검색 시 불균형 문제

발생일시

2025-08-27 17:44 KST

문제 상황

복수 키워드(드라마, IT infrastructure) 검색 시 한 키워드(IT)의 뉴스만 반환되는 문제

증상

  • 입력: 드라마, IT infrastructure 2개 키워드
  • 기대: 드라마 뉴스 + IT 뉴스 균등 분배
  • 실제: IT 뉴스만 20개 반환

원인 분석

현재 로직 (google_news_collector.py)

# 83-91번 라인
for search_term in search_terms:
    news_items = await self._search_single_term(context, search_term, max_items)
    all_news.extend(news_items)  # 각 키워드마다 max_items(20개)씩 추가

unique_news = self._remove_duplicates(all_news)
return unique_news[:max_items]  # 최종 20개로 자름

문제점

  1. 과도한 수집: 각 키워드마다 20개씩 수집 (총 40개)
  2. 단순 병합: 순서대로 병합 후 상위 20개만 반환
  3. 불균형: IT 뉴스가 먼저/많이 수집되면 드라마 뉴스가 잘림

데이터 흐름

드라마 검색 → 20개 수집
IT 검색 → 20개 수집
병합 → 40개
중복 제거 → 35개 (예시)
상위 20개 자름 → IT 뉴스 위주

해결 방안

Option 1: 균등 분배 (권장)

async def search_news(self, keywords: List[str] = None, max_items: int = None) -> List[NewsArticle]:
    search_terms = keywords or self.config.search_terms
    max_items = max_items or self.config.max_items
    
    # 키워드별 균등 할당
    items_per_keyword = max_items // len(search_terms)
    remainder = max_items % len(search_terms)
    
    all_news = []
    
    for idx, search_term in enumerate(search_terms):
        # 나머지 처리 (첫 키워드들에 1개씩 추가)
        keyword_limit = items_per_keyword + (1 if idx < remainder else 0)
        news_items = await self._search_single_term(context, search_term, keyword_limit)
        all_news.extend(news_items)
    
    return all_news  # 이미 균등 분배됨

Option 2: 라운드 로빈 병합

def _merge_round_robin(self, news_lists: List[List[NewsArticle]]) -> List[NewsArticle]:
    """각 리스트에서 번갈아가며 선택"""
    merged = []
    max_len = max(len(lst) for lst in news_lists)
    
    for i in range(max_len):
        for lst in news_lists:
            if i < len(lst):
                merged.append(lst[i])
                if len(merged) >= self.config.max_items:
                    return merged
    
    return merged

Option 3: 가중치 기반 분배

# 환경변수나 파라미터로 키워드별 가중치 설정
keyword_weights = {
    "드라마": 0.5,      # 50%
    "IT infrastructure": 0.5  # 50%
}

for keyword, weight in keyword_weights.items():
    keyword_limit = int(max_items * weight)
    # ...

검증 방법

테스트 스크립트

# /home/admin/ivada_project/skill_news/test_balance.py
import asyncio
from app.services.google_news_collector import GoogleNewsCollector

async def test_keyword_balance():
    collector = GoogleNewsCollector()
    keywords = ["드라마", "IT infrastructure"]
    
    results = await collector.search_news(keywords, max_items=20)
    
    # 키워드별 카운트
    drama_count = sum(1 for r in results if "드라마" in r.title.lower())
    it_count = sum(1 for r in results if "it" in r.title.lower() or "인프라" in r.title.lower())
    
    print(f"드라마 뉴스: {drama_count}개")
    print(f"IT 뉴스: {it_count}개")
    print(f"균형도: {min(drama_count, it_count) / max(drama_count, it_count) * 100:.1f}%")
    
asyncio.run(test_keyword_balance())

즉시 조치

빠른 수정 (Option 1 적용)

# 1. 코드 수정
cd /home/admin/ivada_project/skill_news
vim app/services/google_news_collector.py

# 2. Docker 재시작
docker compose down && docker compose up -d --build

# 3. 테스트
curl -X POST http://localhost:8505/api/news/search \
  -H "Content-Type: application/json" \
  -d '{"keywords": ["드라마", "IT infrastructure"], "max_items": 20}'

영향도

  • 사용자 경험: 다양한 주제의 뉴스를 원하는 사용자 불만족
  • 서비스 품질: 키워드 검색 기능의 신뢰도 하락
  • 확장성: 3개 이상 키워드 시 문제 심화

교훈

  1. 병합 알고리즘은 균형을 고려해야 함
  2. 복수 소스 데이터는 단순 연결보다 인터리빙 필요
  3. 사용자 의도 파악이 중요 (모든 키워드 = 균등 관심)

참고

  • Google News API는 OR 연산자 지원하지만 균형 보장 안됨
  • 키워드별 개별 검색이 더 정확한 결과 제공
  • 향후 사용자별 키워드 선호도 학습 고려

작성: Claude 보고: 황한용님