서버용 모니터를 만들고, 나 자신에게 겨누다
요약
글쓴이는 시차와 복잡한 이벤트 흐름 속에서 중요한 정보만을 선별적으로 전달하는 'Nightwatch'라는 모니터링 시스템을 개발했습니다. 이 시스템은 Cloudflare Worker, GraphQL Analytics API를 활용하여 결정론적 게이트를 거친 후, LLM(Claude Haiku) 호출 여부를 판단하고, 최종적으로 중요도가 높은 이벤트만 Telegram으로 알림을 보냅니다.
핵심 포인트
- 중요한 신호만을 분리하는 'Nightwatch' 모니터링 시스템 구축.
- Cloudflare Worker와 GraphQL API를 활용하여 데이터 수집 및 게이트웨이 구현.
- LLM 호출은 단순히 이벤트를 묘사하기보다, 이벤트의 중요도를 판단하는 데 사용됨.
- 결정론적 로직과 LLM을 결합하여 알림의 정확성과 효율성을 극대화함.
_이 글은 Weekend Challenge: Passion Edition 제출물입니다.
제가 만든 것
저는 Port Harcourt에 있습니다. 이번 월드컵 경기 시작 시간이 새벽 1시, 2시, 때로는 그보다 늦습니다. 경기가 열리는 시점은 토너먼트 일정상 날짜가 하루 차이인데도 불구하고, 저에게는 이미 다음 날입니다. 저는 아르헨티나가 스위스에 3-1로 승리한 8강전 경기를 그렇게 봤습니다. 공식적으로는 7월 11일 경기였지만, Port Harcourt에서는 이미 7월 12일이었습니다. 새벽 4시쯤 잠들었고, 몇 시간 후에 출근했습니다.
제가 면밀히 지켜본 첫 북미 월드컵이며, 시차 계산은 Port Harcourt에서 매우 가혹합니다.
이 챌린지가 시작되기 사흘 전, 저는 workers-monitor를 배포했습니다. 이는 다른 Workers에 문제가 생겼는지 감시하는 Cloudflare Worker입니다. Cron 트리거로 시간당 실행됩니다. Cloudflare의 GraphQL Analytics API에서 플릿 메트릭을 가져옵니다. 결정론적 임계값 게이트가 실제로 문제가 있는지 판단합니다. 이 게이트가 작동했을 때만 Claude Haiku를 호출하여 노이즈 속 신호를 분리합니다. Haiku가 확인해 준 경우에만 Telegram 알림을 보냅니다. 조용한 한 시간 동안은 Anthropic 호출이 전혀 발생하지 않습니다.
github.com/dannwaneri/workers-monitor
이번 챌린지 프롬프트가 '열정에서 영감을 받은 무언가를 만들라'고 떨어졌을 때, 저는 새로운 패턴을 찾지 않았습니다. 방금 증명해낸 것을 활용하기로 했고, 또 무엇이 그러한 절제(restraint)를 받을 자격이 있는지 물었습니다. 잠이 답했습니다.
Nightwatch가 그 결과물입니다: 모양은 같지만 도메인이 다릅니다. 어떤 결정적인 일이 일어났는지 판단하는 게이트가 있고, LLM이 깨어날 가치가 있는지 판단합니다. Telegram이 둘을 통과한 것만 전달합니다.
실제 핵심은 LLM 호출 자체이며, 이는 이벤트를 묘사하는 것이 아닙니다. 템플릿을 사용하면 JSON을 문장으로 바꿀 수 있습니다. 여기서 하는 일은 그 이벤트가 여전히 중요한지 판단하는 것입니다. 4-0 스코어의 네 번째 골과 90분 동점골 모두 기술적으로는 '골' 이벤트이지만, 하나는 잠에서 깨어날 가치가 있고 다른 하나는 그렇지 않습니다. 제 휴대폰의 모든 알림 앱은 이미 점수를 알려줍니다. 그중 어느 것도 제가 신경 써야 할지 물어보지는 않습니다.
데모
8강전은 7월 11일에 끝났고, 준결승은 이 챌린지 마감일 다음 날인 7월 14일부터 시작했습니다. 제가 Nightwatch를 구축하는 동안 실시간 경기를 포착할 수 있는 라이브 매치가 없었습니다.
그래서 이 데모는 실제 배포된 파이프라인을 통해 아르헨티나 대 스위스 3-1의 실제 이벤트 피드를 재생합니다. 진행 중인 라이브 매치를 캡처하는 대신, 동일한 게이트와 동일한 Telegram 전송 방식을 사용합니다. 총 9개의 실제 알림이 발생했으며, 점수판은 모델이 다시 설명하도록 맡기지 않고 코드에서 결정론적으로 계산되었습니다. 따라서 경기를 정확하게 추적합니다: 1-0, 1-1, 2-1, 3-1. 네 개의 이벤트가 높은 중요도(HIGH significance)로 평가되었고 (10분 선제골, 67분 동점골, 72분 레드카드, 연장전 112분 결승골), 다섯 개는 낮은 중요도(LOW)였습니다 (일상적인 카드 4개와 120+1분 보험 골). 깔끔하게 정리하기 위해 반올림하는 것보다 실제 분석을 말씀드리는 편이 낫겠습니다.
코드
github.com/dannwaneri/nightwatch — 이 챌린지 기간 중 처음 커밋된 새로운 저장소입니다.
구축 방법
이것은 ESPN의 문서화되지 않은 공개 스코어보드 엔드포인트(site.api.espn.com/apis/site/v2/sports/soccer/fifa.world/scoreboard)에서 데이터를 가져옵니다. 키나 가입 절차가 필요 없습니다. 비공식적이므로 예고 없이 변경될 수 있으며, 만약 그런 일이 발생하면 전체 폴링이 충돌하는 대신 이벤트별로 실패하도록 파서를 구축했습니다.
게이트 자체: 목표물이나 카드가 연속으로 두 번의 폴링에서 나타나야 확정됩니다. ESPN 피드는 때때로 무언가를 잠시 나열한 후 안정화되는데, 첫 번째 판독을 신뢰했기 때문에 잘못된 '골(GOAL)' 알림은 원하지 않았습니다.
언급할 만한 버그가 하나 있습니다: 초기에 각 알림의 실시간 스코어는 Haiku 자체의 산문에서 나왔고, Haiku는 올바른 정보를 받았을 때조차 가끔 잘못된 숫자를 재진술했습니다. 해결책은 프롬프트 수정이 아니라 구조적인 것이었습니다. 이제 코드는 스코어라인을 직접 계산하여 앞에 붙이고, 모델이 자신의 텍스트에 추가하려는 모든 스코어 형태의 숫자는 제거합니다. 모델은 오직 중요도를 판단하고 무슨 일이 일어났는지 설명하는 역할만 합니다. 관련 수정 사항: 모델에게 실제 선수 이름이 제공되지 않았기 때문에 골에 대한 가상의 이름을 만들었습니다('메시'가 '훌리안 알바레스'가 실제로 득점한 골을 넣었다). 이는 모델이 추측하게 두는 대신 ESPN의 실제 운동선수 데이터를 프롬프트로 전달함으로써 수정되었습니다.
아직 v1인 부분: 당일 경기 일정 자동 검색 기능은 없습니다. 경기 시작 전에 수동으로 POST /track을 사용하여 매치 ID를 전송해야 합니다. 슬립 윈도우 억제(예를 들어, 밤 11시부터 아침 6시 사이의 중요도가 낮은 이벤트에 대한 알림 요청)는 현재 하드코딩된 환경 변수이며, 설정 엔드포인트가 아닙니다. 이는 명세는 되었지만 구축되지 않았습니다. workers-monitor에는 계획된 배포 중에 알림을 억제하기 위한 실제 /maintenance 엔드포인트가 있지만, Nightwatch에는 아직 이에 상응하는 것이 없습니다. 자동화 테스트 스위트도 없으며, 검증은 실제 ESPN 데이터를 대상으로 수동으로 이루어졌습니다.
이 모든 것은 아르헨티나 대 스위스 경기를 보면서 실제로 일어난 일을 바꾸지는 못합니다. 저는 여전히 그 순간 잠을 잃었습니다. 다음번에는 선택할 필요가 없을 것입니다.
시상 부문
이번 라운드에서는 어떤 시상 부문에도 제출하지 않습니다 — 현재 빌드에는 Snowflake, Solana, ElevenLabs 또는 Google AI는 없습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기