AI Automation

AI Briefing Automation

매일 쏟아지는 AI/개발 소식을 직접 찾지 않아도, 필요한 이슈만 자동으로 수집·정리·선별해서 Discord로 보내주는 브리핑 자동화 프로젝트입니다.

AI Automation Example

소개

AI_automation은 AI/개발 관련 뉴스와 기술 글을 매일 직접 확인하는 반복 작업을 줄이기 위해 만든 프로젝트입니다. GitHub Trending, RSS, Hugging Face Blog처럼 형식이 다른 여러 소스를 수집한 뒤, 데이터를 하나의 공통 구조로 정리하고, 중복 제거와 우선순위 선별을 거쳐 Discord 브리핑 메시지로 자동 발행하도록 구성했습니다.

단순히 링크를 모아 보내는 수준이 아니라, 같은 이슈가 여러 채널에서 들어오더라도 하나로 정리하고, 메인 이슈와 주목할 소식을 나눠서 보여주도록 설계했습니다. 또한 특정 소스 수집이 실패했을 때도 이전 정상 캐시를 다시 사용하게 만들어, 실제 자동화 환경에서도 흐름이 끊기지 않도록 안정성을 고려했습니다.

핵심 기능

멀티 소스 수집 자동화

GitHub Trending, RSS, Hugging Face Blog를 각각 수집한 뒤 하나의 브리핑 파이프라인으로 통합했습니다.

공통 스키마 정규화

소스마다 다른 필드 구조와 날짜 형식을 Article 모델 기준으로 맞춰 후처리 로직을 일관되게 재사용할 수 있게 했습니다.

중복 제거 및 우선순위 선별

같은 링크뿐 아니라 제목 유사도까지 비교해 중복을 줄이고, 시간·주제·소스 다양성을 고려해 최종 기사만 선별합니다.

자동 발행 + 실패 복구

Vercel Cron으로 주기 실행하고 Discord Webhook으로 발행하며, 수집 실패 시 캐시를 재사용하도록 구성했습니다.

아키텍처

AI Automation Architecture

수집 단계에서는 소스별 fetcher가 데이터를 가져오고, 이후 normalize → deduplicate → ranking → formatting 단계를 거쳐 최종 Discord 메시지를 생성합니다. 배포 환경에서는 Vercel Cron이 FastAPI 엔드포인트를 주기적으로 호출하며, 수집 실패 시에는 직전 캐시를 fallback으로 사용하도록 설계했습니다.

핵심 코드 스니펫

main.py

파이프라인 오케스트레이션

GitHub Trending, RSS, Hugging Face Blog를 순차적으로 수집하고, 정규화·중복 제거·랭킹·메시지 포맷팅까지 하나의 흐름으로 묶는 메인 파이프라인입니다. 실시간 수집이 실패하더라도 캐시 데이터를 다시 읽도록 구성해, 브리핑 발행이 끊기지 않게 설계했습니다.

app/processor/deduplicator.py

중복 제거 로직

단순히 같은 URL만 제거하는 방식이 아니라, 제목 정규화와 유사도 비교까지 같이 사용해 비슷한 뉴스가 여러 소스에서 들어와도 브리핑에는 한 번만 남도록 정제한 로직입니다.

app/ranking/news_ranker.py

랭킹과 다양성 제어

단순 최신순이 아니라 시간 가중치, 주제 키워드, 소스 가중치, GitHub 스타 수를 함께 반영해 점수를 만들고, 같은 주제와 같은 소스가 과도하게 몰리지 않도록 선별하는 로직입니다.

api/cron.py / vercel.json

배포용 Cron 엔드포인트

FastAPI 엔드포인트에서 시크릿 검증 후 파이프라인을 실행하고, Vercel Cron이 이 경로를 주기적으로 호출합니다. 서버리스 환경에서 실행 결과를 JSON으로 바로 확인할 수 있도록 구성했습니다.

문제점 & 해결방법

CASE 01

소스마다 데이터 구조와 시간 기준이 달라, 하나의 기준으로 묶기 어려웠던 문제

문제 GitHub Trending, RSS, Hugging Face Blog는 제목 구조, 설명 길이, 날짜 형식, 품질 정보가 모두 달랐습니다. 어떤 소스는 published_at이 명확했지만, 어떤 소스는 시간 정보가 불완전했고, 어떤 글은 메타데이터가 풍부하지만 어떤 글은 거의 제목과 링크만 있는 경우도 있었습니다. 이 상태로는 같은 후처리 로직을 적용하기 어려웠고, 랭킹이나 정렬 기준도 흔들릴 수밖에 없었습니다.

  • 소스마다 날짜 형식이 달라 시간 구간 필터링 기준이 일관되지 않았음
  • 제목/설명/태그 등 필드 유무가 달라 동일 모델로 다루기 어려웠음
  • 메타데이터가 빈약한 소스는 점수 계산에서 불리하거나 오류를 유발할 수 있었음

해결 모든 수집 결과를 공통 `Article` 스키마로 정규화한 뒤, 이후 단계는 이 스키마만 바라보도록 구조를 분리했습니다. 날짜는 가능한 경우 UTC 기준으로 통일하고, 누락된 값은 기본값 또는 보정 규칙을 두어 파이프라인이 깨지지 않게 만들었습니다.

  • source, title, url, summary, sort_time, topic 같은 필드를 가진 공통 Article 모델 설계
  • normalize_articles 단계에서 소스별 차이를 흡수하고 후처리 로직은 Article 기준으로만 동작하게 분리
  • 불완전한 메타데이터가 들어와도 전체 파이프라인이 실패하지 않도록 기본값과 예외 처리 추가
Outcome

수집 소스가 달라도 후처리 흐름은 동일하게 유지할 수 있게 되었고, 정렬/랭킹/중복 제거 로직을 공통으로 재사용할 수 있는 구조를 만들었습니다.

CASE 02

같은 이슈가 여러 채널에서 겹쳐 들어와 브리핑 품질이 떨어지는 문제

문제 AI/개발 이슈는 하나의 주제가 여러 채널에서 동시에 다뤄지는 경우가 많았습니다. 단순 URL 비교만 하면 링크가 다른데 내용은 거의 같은 기사들이 중복으로 들어왔고, 반대로 제목만 조금 다른 유사 글도 따로 남게 되었습니다. 이렇게 되면 제한된 브리핑 공간 안에 비슷한 소식만 반복해서 들어가게 되고, 전체 정보 다양성이 떨어졌습니다.

  • 동일 이슈가 원문/요약문/재배포 링크 형태로 여러 번 수집됨
  • URL이 달라도 제목이나 내용이 매우 유사한 경우가 많았음
  • 중복 뉴스가 남으면 사용자가 체감하는 브리핑 밀도가 낮아짐

해결 중복 제거를 URL 기준에서 끝내지 않고, 제목 정규화와 fuzzy similarity 비교까지 함께 사용했습니다. 먼저 canonical URL로 1차 중복을 제거하고, 이후 normalize_title 결과를 기준으로 유사 제목끼리 다시 걸러 중복 이슈를 줄였습니다.

  • canonicalize_url로 추적 파라미터가 섞인 URL 차이를 줄여 실질 같은 링크를 하나로 정리
  • normalize_title로 특수문자, 대소문자, 불필요 표현을 정리해 제목 비교 정확도 향상
  • Sequence 기반 유사도 비교를 추가해 제목만 조금 다른 중복 기사도 필터링
Outcome

브리핑 한 번에 들어가는 기사 수는 같더라도, 실제로는 더 다양한 주제를 담을 수 있게 되었고 같은 이슈가 반복 노출되는 문제를 줄일 수 있었습니다.

CASE 03

최신순만으로는 '읽을 가치가 높은 이슈'가 잘 뽑히지 않는 문제

문제 처음에는 단순히 최신 기사 위주로 정렬했지만, 실제로는 방금 올라온 글이 항상 중요한 것은 아니었습니다. 어떤 글은 너무 짧거나 정보량이 부족했고, 어떤 글은 같은 주제 기사만 연속으로 들어와 브리핑이 한쪽으로 치우치기도 했습니다. 특히 GitHub Trending은 스타 수나 저장 반응이 중요할 수 있고, RSS는 발행 시간 외에도 주제성이 중요할 수 있어서 단순 최신순만으로는 부족했습니다.

  • 최신 기사만 우선하면 품질이 낮은 글도 상위에 올라올 수 있었음
  • 같은 주제 또는 같은 출처가 연속 선택되어 브리핑이 편향될 수 있었음
  • GitHub, RSS, HF Blog는 중요도를 판단하는 기준이 서로 달랐음

해결 시간 가중치, 주제 키워드, 소스 가중치, GitHub 스타 수 같은 요소를 함께 반영해 점수를 계산하고, 선택 단계에서는 주제 다양성과 소스 편중도까지 같이 제어했습니다.

  • 최신성 + 품질 지표를 합친 점수 기반 정렬 로직으로 변경
  • 같은 소스가 과도하게 반복되지 않도록 source count 제한 적용
  • 같은 토픽이 연속 선택되지 않도록 topic diversity 조건 추가
Outcome

단순히 가장 최근 글이 아니라, 실제로 브리핑에 넣었을 때 읽을 만한 가치가 있는 글이 더 안정적으로 선별되었고, 메인 이슈와 주목할 소식의 균형도 좋아졌습니다.

CASE 04

자동화 환경에서는 외부 수집 실패가 곧 전체 발행 실패로 이어질 수 있는 문제

문제 자동화 프로젝트는 사람이 중간에 직접 확인하지 않기 때문에, 특정 외부 사이트 응답이 느리거나 구조가 바뀌는 순간 전체 브리핑 발행이 실패할 수 있습니다. 특히 아침 정해진 시간에 발행하는 구조에서는 한 번의 네트워크 오류나 파싱 실패가 바로 '오늘 브리핑 없음'으로 이어질 수 있어서 안정성 문제가 컸습니다.

  • 외부 소스 응답 지연 또는 HTML 구조 변경으로 수집 실패 가능
  • 한 소스 실패가 전체 파이프라인 중단으로 이어질 수 있었음
  • 정해진 시간에 자동 발행해야 하므로 수동 개입이 어려움

해결 소스별 수집을 개별 try-except로 감싸고, 실시간 수집이 실패했을 경우 이전 정상 데이터를 캐시에서 불러오는 fallback 경로를 추가했습니다. 또한 실행 결과를 JSON으로 남기고 상태를 확인할 수 있게 해, 서버리스 환경에서도 디버깅이 가능하도록 했습니다.

  • collect_source 단위로 예외를 격리해 한 소스 실패가 전체 파이프라인을 멈추지 않게 처리
  • 정상 수집 시 캐시 저장, 실패 시 최신 캐시 재사용 구조로 안정성 확보
  • Cron 엔드포인트 실행 결과에 ok / sent_to_discord / preview_path 등을 포함해 상태 확인 가능하게 구성
Outcome

외부 소스 상태가 일시적으로 흔들려도 전체 브리핑 발행이 완전히 끊기지 않는 구조를 만들었고, 실제 운영 관점에서 더 안정적인 자동화 파이프라인으로 개선할 수 있었습니다.

기술 개선 결과

중복 감소
브리핑 다양성 개선
같은 이슈 반복 노출을 줄이고 더 다양한 기사 선별
자동 발행
반복 작업 제거
매일 수동으로 뉴스 확인하던 흐름을 자동화
안정성 확보
실패 복구 구조
수집 실패 시 캐시 fallback으로 발행 중단 방지