파이프라인 오케스트레이션
GitHub Trending, RSS, Hugging Face Blog를 순차적으로 수집하고, 정규화·중복 제거·랭킹·메시지 포맷팅까지 하나의 흐름으로 묶는 메인 파이프라인입니다. 실시간 수집이 실패하더라도 캐시 데이터를 다시 읽도록 구성해, 브리핑 발행이 끊기지 않게 설계했습니다.
매일 쏟아지는 AI/개발 소식을 직접 찾지 않아도, 필요한 이슈만 자동으로 수집·정리·선별해서 Discord로 보내주는 브리핑 자동화 프로젝트입니다.

AI_automation은 AI/개발 관련 뉴스와 기술 글을 매일 직접 확인하는 반복 작업을 줄이기 위해 만든 프로젝트입니다. GitHub Trending, RSS, Hugging Face Blog처럼 형식이 다른 여러 소스를 수집한 뒤, 데이터를 하나의 공통 구조로 정리하고, 중복 제거와 우선순위 선별을 거쳐 Discord 브리핑 메시지로 자동 발행하도록 구성했습니다.
단순히 링크를 모아 보내는 수준이 아니라, 같은 이슈가 여러 채널에서 들어오더라도 하나로 정리하고, 메인 이슈와 주목할 소식을 나눠서 보여주도록 설계했습니다. 또한 특정 소스 수집이 실패했을 때도 이전 정상 캐시를 다시 사용하게 만들어, 실제 자동화 환경에서도 흐름이 끊기지 않도록 안정성을 고려했습니다.
GitHub Trending, RSS, Hugging Face Blog를 각각 수집한 뒤 하나의 브리핑 파이프라인으로 통합했습니다.
소스마다 다른 필드 구조와 날짜 형식을 Article 모델 기준으로 맞춰 후처리 로직을 일관되게 재사용할 수 있게 했습니다.
같은 링크뿐 아니라 제목 유사도까지 비교해 중복을 줄이고, 시간·주제·소스 다양성을 고려해 최종 기사만 선별합니다.
Vercel Cron으로 주기 실행하고 Discord Webhook으로 발행하며, 수집 실패 시 캐시를 재사용하도록 구성했습니다.

수집 단계에서는 소스별 fetcher가 데이터를 가져오고, 이후 normalize → deduplicate → ranking → formatting 단계를 거쳐 최종 Discord 메시지를 생성합니다. 배포 환경에서는 Vercel Cron이 FastAPI 엔드포인트를 주기적으로 호출하며, 수집 실패 시에는 직전 캐시를 fallback으로 사용하도록 설계했습니다.
GitHub Trending, RSS, Hugging Face Blog를 순차적으로 수집하고, 정규화·중복 제거·랭킹·메시지 포맷팅까지 하나의 흐름으로 묶는 메인 파이프라인입니다. 실시간 수집이 실패하더라도 캐시 데이터를 다시 읽도록 구성해, 브리핑 발행이 끊기지 않게 설계했습니다.
단순히 같은 URL만 제거하는 방식이 아니라, 제목 정규화와 유사도 비교까지 같이 사용해 비슷한 뉴스가 여러 소스에서 들어와도 브리핑에는 한 번만 남도록 정제한 로직입니다.
단순 최신순이 아니라 시간 가중치, 주제 키워드, 소스 가중치, GitHub 스타 수를 함께 반영해 점수를 만들고, 같은 주제와 같은 소스가 과도하게 몰리지 않도록 선별하는 로직입니다.
FastAPI 엔드포인트에서 시크릿 검증 후 파이프라인을 실행하고, Vercel Cron이 이 경로를 주기적으로 호출합니다. 서버리스 환경에서 실행 결과를 JSON으로 바로 확인할 수 있도록 구성했습니다.
문제 — GitHub Trending, RSS, Hugging Face Blog는 제목 구조, 설명 길이, 날짜 형식, 품질 정보가 모두 달랐습니다. 어떤 소스는 published_at이 명확했지만, 어떤 소스는 시간 정보가 불완전했고, 어떤 글은 메타데이터가 풍부하지만 어떤 글은 거의 제목과 링크만 있는 경우도 있었습니다. 이 상태로는 같은 후처리 로직을 적용하기 어려웠고, 랭킹이나 정렬 기준도 흔들릴 수밖에 없었습니다.
해결 — 모든 수집 결과를 공통 `Article` 스키마로 정규화한 뒤, 이후 단계는 이 스키마만 바라보도록 구조를 분리했습니다. 날짜는 가능한 경우 UTC 기준으로 통일하고, 누락된 값은 기본값 또는 보정 규칙을 두어 파이프라인이 깨지지 않게 만들었습니다.
수집 소스가 달라도 후처리 흐름은 동일하게 유지할 수 있게 되었고, 정렬/랭킹/중복 제거 로직을 공통으로 재사용할 수 있는 구조를 만들었습니다.
문제 — AI/개발 이슈는 하나의 주제가 여러 채널에서 동시에 다뤄지는 경우가 많았습니다. 단순 URL 비교만 하면 링크가 다른데 내용은 거의 같은 기사들이 중복으로 들어왔고, 반대로 제목만 조금 다른 유사 글도 따로 남게 되었습니다. 이렇게 되면 제한된 브리핑 공간 안에 비슷한 소식만 반복해서 들어가게 되고, 전체 정보 다양성이 떨어졌습니다.
해결 — 중복 제거를 URL 기준에서 끝내지 않고, 제목 정규화와 fuzzy similarity 비교까지 함께 사용했습니다. 먼저 canonical URL로 1차 중복을 제거하고, 이후 normalize_title 결과를 기준으로 유사 제목끼리 다시 걸러 중복 이슈를 줄였습니다.
브리핑 한 번에 들어가는 기사 수는 같더라도, 실제로는 더 다양한 주제를 담을 수 있게 되었고 같은 이슈가 반복 노출되는 문제를 줄일 수 있었습니다.
문제 — 처음에는 단순히 최신 기사 위주로 정렬했지만, 실제로는 방금 올라온 글이 항상 중요한 것은 아니었습니다. 어떤 글은 너무 짧거나 정보량이 부족했고, 어떤 글은 같은 주제 기사만 연속으로 들어와 브리핑이 한쪽으로 치우치기도 했습니다. 특히 GitHub Trending은 스타 수나 저장 반응이 중요할 수 있고, RSS는 발행 시간 외에도 주제성이 중요할 수 있어서 단순 최신순만으로는 부족했습니다.
해결 — 시간 가중치, 주제 키워드, 소스 가중치, GitHub 스타 수 같은 요소를 함께 반영해 점수를 계산하고, 선택 단계에서는 주제 다양성과 소스 편중도까지 같이 제어했습니다.
단순히 가장 최근 글이 아니라, 실제로 브리핑에 넣었을 때 읽을 만한 가치가 있는 글이 더 안정적으로 선별되었고, 메인 이슈와 주목할 소식의 균형도 좋아졌습니다.
문제 — 자동화 프로젝트는 사람이 중간에 직접 확인하지 않기 때문에, 특정 외부 사이트 응답이 느리거나 구조가 바뀌는 순간 전체 브리핑 발행이 실패할 수 있습니다. 특히 아침 정해진 시간에 발행하는 구조에서는 한 번의 네트워크 오류나 파싱 실패가 바로 '오늘 브리핑 없음'으로 이어질 수 있어서 안정성 문제가 컸습니다.
해결 — 소스별 수집을 개별 try-except로 감싸고, 실시간 수집이 실패했을 경우 이전 정상 데이터를 캐시에서 불러오는 fallback 경로를 추가했습니다. 또한 실행 결과를 JSON으로 남기고 상태를 확인할 수 있게 해, 서버리스 환경에서도 디버깅이 가능하도록 했습니다.
외부 소스 상태가 일시적으로 흔들려도 전체 브리핑 발행이 완전히 끊기지 않는 구조를 만들었고, 실제 운영 관점에서 더 안정적인 자동화 파이프라인으로 개선할 수 있었습니다.