파이프라인을 쪼개다: AI 피드봇을 빠르게 느끼게 만든 방법
AI 뉴스봇 시리즈 후속편입니다. 요약하면, 코딩 에이전트 Harry와 함께 27개 소스에서 AI 뉴스를 모아 LLM으로 랭킹하고 웹앱에 퍼블리시하는 피드를 만들었습니다.
피드가 하루 세 번 돌고 있었습니다. 아침 9시, 점심, 저녁 7시. 결과물은 나쁘지 않았습니다.
그런데 실행과 실행 사이에는 피드가 죽은 것처럼 보였습니다.
AI 세계는 빠르게 움직입니다. 논문이 나오고, 모델이 출시되고, 블로그 글이 올라오는 동안 피드는 몇 시간째 같은 30개 아이템만 보여주고 있었습니다. 다음 실행 때쯤이면 “속보”는 이미 옛날 뉴스가 돼 있었습니다.
당연한 해결책은 더 자주 돌리는 것이지만 그렇게 하면 문제가 줄줄이 생깁니다.
”더 자주 돌리면 되지”가 안 되는 이유
풀 파이프라인은 한 번 돌 때마다 하는 일이 꽤 많습니다.
- 27개 소스 크롤링: RSS, 사이트맵, API 엔드포인트
- 본문 추출: 더 나은 요약을 위한 전문 가져오기
- LLM 처리: Claude가 분류하고, 점수 매기고, 요약 생성
- 랭킹: 슬롯 기반 알고리즘으로 상위 아이템 선정
- 퍼블리시: GitHub에 커밋, Vercel 자동 배포
이걸 30분마다 돌리면 이렇게 됩니다.
- LLM 비용 폭발: 매번 분류랑 요약에 토큰 소모
- 소스 부담: RSS 피드를 하루 48번 때리는 건 예의가 아닙니다 (레이트 리밋에 걸리기도 합니다)
- 스냅샷 비대화: 실행마다 런타임 아티팩트가 쌓여서 레포가 빠르게 커짐
- 수확체감: 30분 사이에 바뀐 게 없으면 같은 콘텐츠를 또 처리하는 데 돈을 쓰는 셈
제가 Harry에게 실제로 보낸 메시지입니다.
오늘 UX 개선할 수 있는 걸 발견했어. 하루 세 번은 업데이트가 너무 느린데, 단순히 스케줄을 줄이면 GitHub에 스냅샷이 너무 많이 쌓이고 LLM이랑 연동하고 있으니까 레이트 리밋 문제도 생겨. 그리고 지금 같은 배치를 run id로 다루고 있는데, 실행이 많아지면 이걸 유지할 수 있을지 모르겠어. 소스한테 너무 자주 크롤링하는 것도 피해야 하고.
이 엔지니어링 챌린지랑 UX 갭 이해해?
Harry는 바로 알아들었습니다.
100%: 정확히 핵심 긴장을 짚고 있어요: UX는 빠른 신선함을 원하고, 인프라는 통제된 비용/노이즈를 원해요. 그리고 맞아요, 지금 “풀 실행 = 스냅샷 + LLM + 크롤링” 모델은 빈도를 높이면 스케일이 안 돼요.
Harry가 5가지 개선안을 내놓았는데 한꺼번에 하기엔 너무 많았습니다. 그래서 제가 더 깔끔한 구조를 제안했습니다.
좋은 아이디어가 하나 있어. 데이터 레벨을 두 층으로 나눠서 현재 것을 Tier 0 (느리지만 LLM 라벨링이나 리랭킹 같은 풍부한 데이터)으로 유지하고, 새로 Tier 1을 만들어서 짧은 수집 시간에 후처리를 줄이면 어때? Tier 0은 웹에서 직접 가져오지 않고 Tier 1 데이터를 소스로 쓰는 거지.
Harry의 반응은 이랬습니다.
아주 좋은 아이디어예요. 여러 개 임시 수정하는 것보다 훨씬 깔끔해요.
2-Tier 설계
아키텍처 방향은 명확했습니다. 파이프라인을 속도와 비용이 다른 두 층으로 나누자. 각 티어의 역할과 데이터 흐름은 제가 정의했고 Harry가 실제 코드로 구현했습니다.
Tier-1 (빠른 레인), 30분마다 실행:
- 소스에서 원시 데이터 수집 (쿨다운 적용: 최근에 가져온 소스는 캐시 재사용)
- 가벼운 결정론적 스코어링 (LLM 없음)
- 소스 평판, 최신성, 키워드 신호 기반 “빠른 점수” 산출
- 결과: 거의 비용 없이, 자주 업데이트되는 신선한 스냅샷
Tier-0 (딥 레인), 하루 3번 실행:
- Tier-1의 결과를 입력으로 사용 (크롤링 중복 없음)
- 풀 LLM 분류, 요약 생성, 리랭킹 수행
- 큐레이션된 고품질 피드 생산
- 결과: 빈도는 낮지만 훨씬 스마트한 “제대로 된” 결과물
피드 API는 둘을 블렌딩합니다. Tier-0의 큐레이션 아이템이 뼈대를 이루고 그 상단에 Tier-1의 신선한 아이템이 섞여 들어갑니다. 신선한 아이템에는 뱃지를 붙여서 아직 예비 점수라는 걸 유저가 알아볼 수 있게 해뒀습니다.
낭비 안 하게 만들기
2-Tier 분할이 큰 그림이었다면, 이걸 실제로 효율적으로 만드는 데는 작은 결정이 몇 가지 더 필요했습니다.
컬렉터 쿨다운
소스마다 쿨다운이 있어서 N분 이내에 이미 가져온 소스는 빠른 레인이 다시 요청하지 않고 이전 데이터를 재사용합니다. 그래서 Tier-1이 하루 48번 돌아도 크롤링을 48번 하지는 않습니다. 대부분은 캐시를 재사용하고 쿨다운이 끝난 소스만 다시 가져옵니다.
무변경 스킵
비싼 Tier-0 파이프라인을 돌리기 전에, 마지막 풀 실행 이후로 입력이 실제로 바뀌었는지 먼저 확인합니다. Tier-1이 같은 아이템 세트를 뱉었으면 (어떤 소스에서도 새 콘텐츠가 없으면) 풀 실행을 통째로 건너뜁니다. LLM 호출도, 새 아티팩트도, 돈 낭비도 없습니다.
리텐션 압축
실행이 많아지면 레포에 스냅샷이 쌓이기 때문에 압축 정책을 추가했습니다.
- Tier-1 스냅샷: 3일간 상세 보관, 이후 하루 하나로 압축
- Tier-0 스냅샷: 7일간 상세 보관, 이후 압축
디버깅용 히스토리는 남기면서도 레포는 가볍게 유지됩니다.
결정론적 실행 순서
풀 실행은 이제 수집 → Tier-1 빌드 → Tier-0 실행 (Tier-1을 입력으로)이라는 엄격한 순서를 따릅니다. 병렬 경로가 없으니 어떤 데이터가 어디로 흘러가는지 모호할 일도 없습니다.
운영 레이어
만드는 것과 잘 돌아가는지 아는 것은 다른 문제입니다. 그래서 커맨드 하나로 확인하는 일일 운영 요약도 추가했습니다.
- 지난 24시간 Tier-0, Tier-1 실행 횟수
- 각 티어의 최신 아이템 수
- 수집 상태 분포 (성공, 쿨다운 스킵, 에러 각각 몇 건)
이 요약 덕분에 2-Tier 시스템은 “아마 잘 돌아가겠지”에서 “잘 돌아가는 게 보여”로 바뀌었습니다.
점검
하루가 끝날 무렵, 아침에 보낸 메시지를 다시 꺼내 Harry에게 물었습니다. “Have we achieved this goal?”
Harry의 체크리스트는 이랬습니다.
- “하루 3번은 너무 느려” → ✅ Tier-1 빠른 레인 30분마다 + 피드 신선함 블렌딩
- “실행 늘리면 GitHub 스냅샷 비대화” → ✅ 리텐션/압축 정책
- “높은 빈도에서 LLM 레이트 리밋/비용” → ✅ Tier-1은 LLM 없이 가볍게, Tier-0은 무변경 스킵
- “실행 많아지면 Run-id 의미 깨짐” → ✅ ingest_batch_id 계보 추가
- “소스에 너무 자주 크롤링” → ✅ 컬렉터 쿨다운 + 재사용 동작
아침에 던진 걱정 모두에 저녁에는 구체적인 해결책이 붙어 있었습니다. 뿌듯한 하루였습니다.
유저한테 실제로 뭐가 달라졌나
이전: 피드 업데이트 하루 3번. 사이사이는 오래된 콘텐츠.
이후: 소스에서 발행된 지 30분 안에 신선한 아이템이 등장합니다. LLM 큐레이션이 아닌 가벼운 알고리즘 점수지만 일단 화면에는 나옵니다. 풀 실행이 돌면 제대로 분류되고 리랭킹됩니다.
LLM 예산은 거의 그대로입니다. 소스 크롤링은 쿨다운 덕분에 총 요청 수가 줄어 오히려 감소했고 레포도 압축 덕분에 덜 커집니다. 그러면서도 피드는 살아 있는 느낌을 줍니다.
패턴
AI 기반 프로덕트에서 계속 보이는 패턴이 있습니다. 첫 버전은 잘 돌아가는데 명확한 UX 갭이 있습니다. 해결책으로는 더 많은 컴퓨팅을 쏟아붓기보다 아키텍처를 영리하게 짜는 편이 훨씬 효과적입니다.
본능적으로는 항상 “더 많이 돌려”가 먼저 떠오르지만 답은 보통 “다른 것들을 다른 속도로 돌려”에 가깝습니다.
Tier-1은 신선함을, Tier-0은 품질을 담당합니다. 둘 다 혼자서는 양쪽을 잘 못 합니다. 전체 사용자 경험을 채우면서도 총비용을 낮게 유지하려면 둘이 손을 잡아야 합니다.
피드는 llm-digest.com에서 보실 수 있습니다. 이전 이야기가 궁금하다면 만든 과정, 스케일링, 읽는 법 가르치기, 지능 축적하기 편을 읽어보세요.