
FastF1과 Claude를 사용하여 자연어 F1 텔레메트리 분석기를 구축한 방법
요약
FastF1 데이터와 Claude Sonnet을 결합하여 자연어로 F1 텔레메트리를 분석하는 'F1 Analyst Pro' 구축 방법을 소개합니다. 벡터 검색 대신 의도 탐지와 SQL 쿼리를 활용한 Intent-Based RAG 아키텍처를 통해 구조화된 데이터를 정확하게 분석합니다.
핵심 포인트
- FastF1 데이터를 Supabase에 저장하여 구조화된 컨텍스트 구축
- 표준 RAG 대신 의도 탐지 및 SQL 쿼리를 사용하는 Intent-Based RAG 방식 채택
- Claude Sonnet을 활용해 자연어 질문을 정교한 데이터 분석으로 변환
- Streamlit과 Plotly를 이용한 대화형 데이터 시각화 구현
"McLaren의 Ferrari 언더컷이 성공했나요?"라고 물어보고 실제 랩(lap) 데이터에 기반한 진짜 답변을 얻으세요.
문제점
Formula 1 분석은 데이터는 풍부하지만, 엔지니어가 아닌 사람들에게는 도구가 부족합니다. 레이스 주간을 취재하는 기자는 타이밍 시트(timing sheets), 텔레메트리(telemetry) 내보내기, 전략 브리핑, 그리고 개인 메모 등을 다뤄야 합니다. 이 모든 것들은 서로 다른 형식이며, 서로 연동되지도 않습니다.
저는 기자(또는 기술적 호기심이 있는 모든 F1 팬)가 평범한 스페인어(plain Spanish)로 질문을 던지면, 환각된 요약이나 Wikipedia의 사실이 아닌, 해당 레이스 주간의 실제 FastF1 랩 데이터에 근거한 답변을 얻을 수 있는 무언가를 만들고 싶었습니다.
그 결과물은 F1 Analyst Pro입니다. 이는 FastF1 세션 데이터를 Supabase에 입력하고, SQL 쿼리를 통해 구조화된 컨텍스트(context)를 구축한 뒤, 이를 Claude Sonnet으로 보내 자연어 분석을 수행하는 Streamlit 채팅 앱입니다.
라이브 앱: f1-analyst.streamlit.app
리포지토리: github.com/luc45hn/f1-analyst-pro
기술 스택
- FastF1 — 공식 F1 텔레메트리 데이터 (랩, 차량 데이터, 날씨, 레이스 컨트롤 메시지)
- Claude Sonnet (claude-sonnet-4-6) — 쿼리를 해석하고 분석을 생성하는 LLM (대규모 언어 모델)
- Supabase + PostgreSQL — 입력된 세션 데이터를 저장하며, 컨텍스트를 구축하기 위해 런타임 시 쿼리됨
- Streamlit — Streamlit Cloud에 배포된 프론트엔드
- Plotly — 텔레메트리 트레이스(telemetry traces), 타이어 마모(tyre degradation), 페이스 분석을 위한 대화형 차트
아키텍처: 의도 기반 RAG (Intent-Based RAG)
이것은 표준적인 벡터 검색 (Vector Search) RAG가 아닙니다. 임베딩 (embeddings), 코사인 유사도 (cosine similarity), 문서 청크 (document chunks)가 전혀 사용되지 않습니다. 대신, 시스템은 **의도 탐지 (intent detection) + 구조화된 SQL 쿼리 (structured SQL queries)**를 사용하여 컨텍스트 (context)를 구축합니다. 이는 랩 타임 (lap times)과 같은 구조화된 테이블 데이터 (structured tabular data)에 훨씬 더 효과적으로 작동합니다.
흐름은 다음과 같습니다:
사용자 질의 (User query)
↓
의도 탐지 (Intent detection) (키워드 매칭 + 정규 표현식)
...
의도 탐지 (Intent Detection)
에이전트 (agent)는 사용자가 무엇을 묻고 있는지 탐지하여 관련 데이터만 가져옵니다:
prompt_lower = unicodedata.normalize("NFD", prompt.lower()) # 악센트 무시
wants_qualy = any(w in prompt_lower for w in ["clasificacion", "qualifying", "q1", "q2", "q3", "pole"])
...
컨텍스트 구축 (Building Context)
각 의도는 특정 SQL 쿼리를 트리거합니다. 레이스 (race) 질의의 경우, 에이전트는 스틴트 요약 (stint summaries), 피트 스톱 분석 (pit stop analysis), 주요 순간 (key moments), 그리고 레이스 사고 (race incidents)를 가져옵니다:
if wants_race or load_all:
race_id = self.db.get_session_id(year, gp_name, "R")
if race_id:
...
이 컨텍스트는 시스템 프롬프트 (system prompt)가 아니라 사용자 메시지의 일부로서 Claude에게 전달되며, 대화 턴 (conversation turn)에 주입되어 모델이 이를 바탕으로 추론할 수 있게 합니다.
FastF1을 이용한 데이터 수집 (Data Ingestion)
FastF1이 중추적인 역할을 합니다. 세션 (session)을 로드하는 방법은 간단합니다:
import fastf1
fastf1.Cache.enable_cache("cache/")
...
각 세션은 확장된 필드와 함께 Supabase로 수집됩니다:
lap_record = {
"driver": lap["Driver"],
"lap_number": int(lap["LapNumber"]),
...
랩(lap)당 position 정보를 보유하는 것이 언더컷/오버컷 (undercut/overcut) 분석을 가능하게 하는 핵심입니다. 이를 통해 드라이버가 경쟁 상대에 비해 정확히 언제 순위를 얻거나 잃었는지 추적할 수 있습니다.
텔레메트리 트레이스 (Telemetry Traces)
가장 시각적으로 인상적인 기능입니다. 사용자가 텔레메트리 (telemetry)를 요청하면, 앱은 Claude를 호출하기 전에 Plotly 차트를 생성합니다. 그런 다음 모델에게 차트가 존재함을 알려주어, 모델이 분석 시 해당 차트를 참조할 수 있도록 합니다.
# chart_builder.py
def plot_telemetry_trace(laps_data, gp_name, year, drivers, session_type="Q", qualifying_segment=None):
fig = make_subplots(rows=4, cols=1, shared_xaxes=True,
...
핵심 통찰(Key insight): 모델이 차트를 직접 생성하는 것이 아닙니다. 차트는 실제 데이터로부터 생성되며, 모델에게는 "Q2에서 COL과 GAS를 비교하는 텔레메트리 차트가 생성되었습니다"라고 알려줍니다. 이를 통해 채팅창에 존재하지 않는 matplotlib 코드가 나타나는 환각(Hallucination) 현상을 방지할 수 있습니다.
언더컷/오버컷 분석 (Undercut/Overcut Analysis)
제가 가장 자랑스럽게 생각하는 기능 중 하나입니다. 랩 단위(lap-by-lap) 위치 데이터가 주어지면, 시스템은 인접한 드라이버들 사이의 피트 스톱(pit stop) 상호작용을 감지하고 판결을 내립니다:
def get_pit_stop_analysis(self, session_id: int, position_window=3, lap_window=5):
# 각 피트 스톱에 대해, ±3 순위 이내의 라이벌을 찾습니다.
# 또한 ±5 랩 이내에 피트 스톱을 한 드라이버를 찾습니다.
...
2026년 모나코 레이스에서 다음과 같은 분석이 자동으로 생성되었습니다:
"28랩에서 HAD를 상대로 한 HAM의 언더컷(undercut)은 이번 레이스에서 가장 결정적인 움직임이었습니다. 델타(Delta): +6.0초. Ferrari가 먼저 진입하여 훨씬 앞서서 빠져나왔습니다 — 교과서적인 언더컷 실행입니다."
주요 순간 감지 (Key Moments Detection)
시스템은 요청받지 않아도 레이스 데이터에서 이상 징후(anomalies)를 자동으로 표시합니다:
def get_key_moments(self, session_id: int) -> pd.DataFrame:
# TRACK_LIMITS: deleted=True인 랩
# PACE_ANOMALY: 그린 트랙(green track)에서 lap_time > 드라이버 중앙값 * 1.15
...
이 기능 덕분에 앱은 레이스 요약에서 다음과 같이 선제적으로 언급할 수 있었습니다: "RUS는 73랩에서 11개 순위를 잃었습니다 — 이번 레이스에서 단일 랩 기준 가장 큰 순위 하락입니다."
레이스 컨트롤 메시지를 통한 레이스 사고 (Race Incidents from Race Control Messages)
FastF1은 레이스(Race) 및 스프린트(Sprint) 세션에 대한 race_control_messages를 제공합니다. 시스템은 이를 흡수하여 트랙 위에서의 위치와 공식 순위 간의 불일치(discrepancies)를 교차 참조합니다:
# 최종 순위를 변경한 페널티(penalties)를 감지합니다.
incidents = db.get_race_incidents(race_id)
discrepancies = db.get_position_discrepancies(race_id)
...
이는 Colapinto가 트랙 위에서는 8위(P8)로 결승선을 통과했으나, 황색기(yellow flag) 위반으로 경기 후 10위(P10)로 페널티를 받은 2026년 바르셀로나 사례를 포착했습니다. 에이전트는 어떠한 수동 노트 없이도 이를 정확하게 설명했습니다.
작동 모습
비용 제어 (Cost Control)
각 쿼리당 Claude API 사용 비용은 대략 $0.02~$0.04 정도가 발생합니다. 비용이 무분별하게 발생하는 것을 방지하기 위해, Supabase에 사용자별 일일 한도를 저장해 두었습니다:
DAILY_COST_LIMIT_USD = 3.00
def check_daily_cost(self, user_email: str) -> float:
...
한도에 도달하면 에이전트는 쿼리를 차단하고 친절한 메시지를 표시하며, 조용히 실패(silent failure)하지 않도록 설계되었습니다.
향후 계획
- 아르헨티나 드라이버 Baltazar Leguizamón을 위한 NASCAR 버전 (O'Reilly Auto Parts Series 2026) — 데이터 소스는 다르지만 동일한 RAG (Retrieval-Augmented Generation) 패턴 적용
- 다중 시즌 역사적 비교
- 레이스 시뮬레이션 페이스 분석 (FP2 롱 런 방법론 — 현재 보정 중)
체험하기
앱은 현재 라이브 상태이며 (일일 사용 한도 내에서) 무료로 체험할 수 있습니다:
🏎️ f1-analyst.streamlit.app
📦 github.com/luc45hn/f1-analyst-pro
FastF1, Claude Sonnet, Supabase, Streamlit, 그리고 수많은 퀄리파잉 세션 시청으로 밤을 지새운 노력으로 만들어졌습니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Dev.to AI tag의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기

