← 블로그 목록
LLM뉴스필터링프롬프트엔지니어링FastAPIPython뉴스품질AI

LLM으로 뉴스 품질 자동 점수화하기 — 1점~100점 루브릭 설계

2026년 4월 16일

뉴스 소스를 늘리다 보면 반드시 품질 문제가 생깁니다.

코인 투자 관련 정보를 수집하다 보면 기술적 분석 기사와 관련 없는 일반 기업 실적 기사가 섞입니다. 단순 키워드 필터는 두 가지 문제를 동시에 만납니다 — 있어야 할 기사를 놓치고, 없어도 될 기사를 통과시킵니다.

이 글에서는 기사 제목과 본문 일부를 LLM에 넘겨 1~100점을 매기는 방식으로 전환한 과정을 설명합니다. 구현 예시는 코인 투자 정보 서비스 twojaking.com의 실제 프롬프트 코드를 기반으로 합니다.


키워드 필터의 한계

코인 심볼 포함 기사만 통과시키는 방식은 빠르게 한계가 왔습니다.

  • 부정문 형태의 관련 없는 기사도 통과
  • CPI 발표, 금리 결정처럼 실제로 중요한 거시경제 기사는 키워드가 없어서 탈락
  • 심볼이 들어간 기술적 분석 기사는 전량 통과

키워드는 단어의 존재만 확인하고 기사의 의미는 판단하지 못합니다.


점수 시스템 설계

점수는 두 축의 곱으로 계산합니다.

최종 점수 = 경제관련성 점수 × 실시간성 점수 / 100

경제관련성 점수

지금 경제 상황을 파악하는 데 이 기사가 얼마나 유용한가.

점수해당 기사
90~100암호화폐 가격 급변·대규모 청산·ETF 자금흐름, 중앙은행 금리 결정, GDP·CPI·PPI·PCE 발표, 유가 10% 이상 급변
70~89암호화폐 규제 조치(SEC·CFTC·DOJ), 주가지수 1% 이상 등락, 에너지 공급 차질, 주요국 휴전·전쟁 합의
50~69거시경제 지표(고용·무역수지), 환율 급변, 에너지 소폭 변동, 금·원자재 가격
20~49암호화폐 해킹·보안, 온체인 지표, 금융 기업 실적
1~19비금융 기업 실적·인사·M&A(자동차·방산·소매·IT·의약품), 사회·문화·스포츠

실시간성 점수

이 기사가 얼마나 지금 일어나는 일인가.

점수해당 기사
90~100지금 막 발생·확인된 사건, 오늘 발표된 수치
50-89진행 중인 협상·정책 움직임, 오늘-이번 주 데이터
20~49분석·해설 기사 (사건은 실시간이지만 해석 위주)
1~19예측·전망, 오피니언, 기술적 분석(차트·패턴·지지선·저항선)

상한선 제약

두 축만 쓰면 특정 유형 기사가 과도한 점수를 받습니다. 그래서 유형별 상한선을 별도로 지정합니다.

  • 암호화폐 기술적 분석(저항선·강세장 진입·폭등 전망): 최대 45점
  • 예측·전망·시나리오만 담긴 기사: 최대 45점
  • 오피니언·칼럼·특정인 의견만: 최대 35점
  • 비금융 기업 실적·인사·M&A: 최대 20점
  • 암호화폐 보안(스캠 동결·해킹 수사): 최대 30점
  • 문화·스포츠·사회·환경: 최대 10점
  • 부동산·임대·모기지: 최대 25점
  • 군사·외교 (에너지·금리 수치 충격 없는 경우): 최대 45점

실제 프롬프트 구조

app/prompts/news.pybuild_classification_messages가 이 루브릭을 그대로 담습니다.

def build_classification_messages(items: list[dict]) -> tuple[list[AIMessage], dict[str, str]]:
    # 기사 관련도 점수 산정 (1~100). 출처 + 제목 + 본문 일부를 보고 판단.
    # Returns: (messages, idx_to_real) — idx_to_real은 {1: real_id, ...} 매핑
    idx_to_real: dict[str, str] = {}
    articles_text = ""
    for i, item in enumerate(items, 1):
        real_id = item.get("link_hash", item.get("id", ""))
        idx_to_real[str(i)] = real_id
        title = item.get("title", item.get("raw_title", ""))
        content = (item.get("summary") or item.get("raw_content") or "")[:150]
        source = item.get("source_label", "")
        prefix = f"[{source}] " if source else ""
        if content:
            articles_text += f"[{i}] {prefix}제목: {title} / 내용: {content}\n"
        else:
            articles_text += f"[{i}] {prefix}제목: {title}\n"

    prompt = (
        # 점수 목적: 지금 이 순간 경제 상황을 파악하는 데 얼마나 유용한가 (1~100)
        # 최종점수 = 경제관련성 점수 x 실시간성 점수 / 100
        # ... (루브릭 전체 포함)
        "기사 목록:\n" + articles_text
    )
    return [
        AIMessage(role="system", content="You are a financial news relevance scorer. Respond only in JSON."),
        AIMessage(role="user", content=prompt),
    ], idx_to_real

인덱스 치환 패턴을 사용한 이유가 있습니다. LLM에 UUID를 직접 전달하면 ID를 변형하거나 포맷이 오염되는 hallucination이 발생합니다. 순번(1, 2, 3, …)만 전달하고 응답에서도 순번으로 받은 뒤, 서비스 레이어에서 idx_to_real로 역치환합니다.

AI 응답의 일관성을 위해 temperature=0.1로 낮게 설정했습니다. 같은 기사를 두 번 넣어도 비슷한 점수가 나오는 것이 중요합니다.


Threshold 65를 정한 과정

점수 시스템을 만들었으면 어디서 자를지도 결정해야 합니다.

1단계: ground truth 데이터 구축

실제 수집된 기사 중에서 직접 보여줘야 할 기사와 안 보여줘도 될 기사를 수작업으로 분류했습니다. 12개의 통과 기사와 13개의 차단 기사로 ground truth를 구성했습니다.

2단계: 점수 분포 확인

test_news_classifier.py로 샘플을 돌려 통과해야 할 기사와 차단해야 할 기사의 점수 분포를 확인했습니다.

3단계: DB 전체 기사 대상 평가

test_news_classifier_db.py로 DB에 쌓인 실제 기사 수백 개를 대상으로 실행했습니다. 결과적으로 65 미만 기사의 약 80%가 실제로 불필요한 기사였고, 65 이상 기사는 대부분 의미 있었습니다.

65보다 낮추면 노이즈가 증가하고, 높이면 중요한 기사가 탈락합니다. 이 트레이드오프를 확인한 후 65를 선택했습니다.

65 미만 기사도 DB에는 저장됩니다. visible=false로 저장하기 때문에 나중에 threshold를 변경하면 즉시 재적용할 수 있습니다.


소스 관리

뉴스 소스는 코드에 하드코딩하지 않고 Supabase의 news_sources 테이블에서 관리합니다.

  • rss_url: RSS 피드 URL
  • parser_type: rss 또는 html
  • parser_config: HTML 스크래핑용 CSS 선택자
  • is_active: 활성화 여부

RSS를 제공하지 않는 사이트는 HTML 파서로 직접 수집합니다. DB에서 관리하기 때문에 소스 추가·비활성화 시 코드 배포 없이 처리할 수 있습니다.

점수 시스템 덕분에 소스를 공격적으로 늘릴 수 있게 됐습니다. 소스 품질이 낮아 70%가 필터링되더라도 65점 이상만 노출되기 때문에 추가 비용이 없습니다. 소스가 많을수록 중요한 기사를 놓칠 확률이 낮아집니다.


결과

키워드 필터에서 LLM 점수 시스템으로 전환한 이후 세 가지가 개선됐습니다.

  1. CPI 발표, 금리 결정 같은 키워드 없는 거시경제 기사가 정상 노출됩니다
  2. 기술적 분석·예측 기사가 상한선에 걸려 노출 우선순위가 낮아집니다
  3. 소스를 추가할 때 품질 걱정 없이 진행할 수 있습니다

다음 단계는 이렇게 필터링된 기사들을 사건 단위로 묶어 타임라인으로 보여주는 클러스터링 파이프라인입니다.

0

댓글 0

Ctrl+Enter