KAVEN = Korean AI-based Vigilance for Event Navigation
Kaven은 AIS/ADS-B/뉴스/소셜 데이터를 수집하고, LLM 분석 + dedup 후 텔레그램 알림/로그 저장까지 수행하는 지정학 조기경보 시스템입니다.
현재 버전: 0.0.04
버전 정책:
- 모든 업데이트 시 버전을 올리고(
0.0.01부터 시작), 릴리스 노트/알림 헤더/로그 메타데이터에 동일 버전을 표시합니다.
- 감시 구역/피드/키워드 설정 파일화 — 기존에 코드에 하드코딩되어 있던 AIS·ADS-B 감시 구역, 뉴스 RSS/키워드, 소셜 검색어를 전부
src/kaven/config.json(선택) 로 외부화했습니다. 각 항목에enabled플래그를 두어 선택적 활성화/비활성화 가능. src/kaven/config_loader.py신규 모듈: 설정 파일 없으면 내장 기본값 fallback.KAVEN_CONFIG환경변수로 경로 override.src/kaven/config.example.json샘플 파일 제공 (바브엘만데브, 동유럽 공역, 연합뉴스 등enabled:false예시 포함)./configAPI 엔드포인트 신규: 현재 로드된 설정(enabled/disabled 수 포함)을 JSON으로 조회.- 4개 collector 전부 리팩터링 — 런타임에 설정을 읽어 활성 항목만 수집.
- Codex 리뷰 수정사항 반영 — ruff 11 errors → 0,
pyproject.toml(ruff/mypy 설정),requirements.txt/requirements-dev.txt추가, social_collectorSEARXNG_URL환경변수화. - 테스트:
tests/test_config_loader.py8건 신규 (설정 로드/필터/비활성/오류복원).
- 일일 분석 리포트 자동 생성 (
/report) — JSONL 로그에서 지역별/카테고리별/자산별 집계 + 마크다운 브리핑을 자동 생성. LLM 없이 규칙 기반으로 동작하므로 API 키 불필요.GET /report,GET /report/{YYYYMMDD},GET /report/dates엔드포인트 추가. - 인터랙티브 분쟁 지도 (
/map) — globe.gl 3D 지구본에 실시간 이벤트 데이터를 severity별 색상 마커로 표시. 기존 하드코딩 시각화를GET /map/dataAPI 기반으로 교체. 클릭 줌, 자동 회전 지원. - 지역별 분쟁 현황 가이드 (
/guide) — 9개 감시구역(호르무즈, 대만, 한반도 등)의 현재 severity + 설명 + 7일 히스토리 차트.GET /guide,GET /guide/{region}?days=7엔드포인트 추가. - 프론트엔드 전면 리뉴얼 — 단일 테이블 → 탭 기반 다크 테마 SPA (Dashboard / Report / Map / Guide). Severity 뱃지, 통계 카드, 반응형 레이아웃.
report_generator.py모듈 신규 추가 + 테스트 6건 (tests/test_report_generator.py).
- 이슈 #7 정책 반영: Convex 원격 업로드를
CONVEX_SITE_URL기반 opt-in으로 전환. - 하드코딩 엔드포인트 제거,
CONVEX_EVENT_PATH환경변수 지원. - 정책 회귀 테스트
tests/test_kaven_convex_policy.py9건 추가. .gitignore신규 추가.
- 사용자 알림 문구의
Maven표기를Kaven으로 통일했습니다. - 실행 로그 파일명을
kaven_YYYYMMDD.jsonl로 전환했고, 구버전maven_*.jsonl은 읽기 호환을 유지합니다. - 텔레그램 경보 헤더/긴급 경보에 버전(
v0.0.01)이 표시됩니다. /health응답과 런타임 로그 메타데이터에 버전 필드를 포함합니다.
- 수집기(collectors): AIS, ADS-B, 뉴스, 소셜 데이터 수집
- 분석기(analyzer): OpenAI 호환(로컬 포함) → Gemini → Anthropic → 규칙기반 폴백 순으로 이벤트 분석
- 중복 제거(kaven dedup): 텍스트/수치/출처 URL 기반 유사 이벤트 병합
- 알림(signal_generator): Severity 기준 텔레그램 발송
- 저장: 로컬 JSONL 로그 + Convex 업로드 시도(실패해도 로컬 유지)
src/kaven/kaven.py: 메인 실행기 (--once,--watch)src/kaven/collectors/: 데이터 수집기src/kaven/analyzer.py: LLM 분석 엔진src/kaven/signal_generator.py: 알림 발송webapp/backend/app.py: FastAPI APIwebapp/frontend/index.html: 정적 대시보드tests/: dedup/로그 리플레이 테스트
v0.0.04부터: 감시 구역, RSS 피드, 키워드는 모두 설정 파일로 관리됩니다. 기본값은 코드에 내장되어 있어 설정 파일이 없어도 바로 동작합니다. 커스터마이즈하려면
src/kaven/config.example.json을src/kaven/config.json으로 복사해서 편집하세요. 자세한 내용은 §13 설정(Configuration) 참조.
- 파일:
src/kaven/collectors/ais_collector.py - 설정 키:
ais_zones - 기본 감시 지역:
- 호르무즈 해협
- 말라카 해협
- 미설정 시 동작:
AISSTREAM_API_KEY가 없으면 시뮬레이션 모드로 동작 (활성화된 zone만)
- 파일:
src/kaven/collectors/adsb_collector.py - 설정 키:
adsb_zones - 기본 감시 공역:
- 중동(이란·이라크·걸프)
- 대만 해협
- 한반도
- 인증:
- OpenSky는
OPENSKY_USERNAME/OPENSKY_PASSWORDBasicAuth 사용 - 인증값 없으면 비인증 모드(rate limit 더 엄격)
- OpenSky는
- 파일:
src/kaven/collectors/news_collector.py - 설정 키:
news_feeds,news_keywords - 기본 소스:
- Reuters/AP/BBC RSS
- 로컬 SearxNG(
SEARXNG_URL, 기본http://localhost:8080)
- 파일:
src/kaven/collectors/social_collector.py - 설정 키:
social_keywords - 소스:
- SearxNG 검색 우선 (
SEARXNG_URL) - PinchTab 브라우저 폴백 (
PINCHTAB_URL, 기본http://localhost:9867)
- SearxNG 검색 우선 (
- 파일:
src/kaven/analyzer.py - 출력 스키마 핵심 필드:
event,severity(1-5),category,signal,confidence,affected_assets,source_url,event_time등
- 분석 경로 우선순위:
- OpenAI 호환 API (
OPENAI_BASE_URL설정 시; 로컬 LLM 가능) - Gemini API
- Anthropic API
- 규칙 기반
_fallback_analysis
- OpenAI 호환 API (
- Python 3.11+
aiohttp,feedparser,websockets- FastAPI + Uvicorn (웹 API)
- Pytest (테스트)
- Docker / docker-compose (배포 스캐폴드)
- systemd 서비스 파일 제공 (
deploy/systemd/kaven.service)
python3 -m venv .venv
source .venv/bin/activate
pip install -U pip
pip install aiohttp feedparser websockets fastapi uvicorn pytestKaven은 src/kaven/.env를 자동 로드합니다.
cat > src/kaven/.env <<'ENV'
# ===== 수집 =====
OPENSKY_USERNAME=
OPENSKY_PASSWORD=
AISSTREAM_API_KEY=
SEARXNG_URL=http://localhost:8080
# ===== 분석 =====
OPENAI_BASE_URL=
OPENAI_API_KEY=
OPENAI_MODEL=
GEMINI_API_KEY=
ANTHROPIC_API_KEY=
# ===== 알림 =====
TELEGRAM_BOT_TOKEN=
TELEGRAM_CHAT_ID=
TELEGRAM_TOPIC_MAVEN=5052
TELEGRAM_USER_DM=
# ===== 원격 백업 (opt-in, 이슈 #7 정책) =====
# 기본은 로컬 로그만 저장하며 외부 전송은 완전 비활성화됩니다.
# 필요한 경우에만 CONVEX_SITE_URL을 명시적으로 설정해 opt-in 하세요.
# CONVEX_EVENT_PATH는 경로 override (기본 /addKavenRun).
CONVEX_SITE_URL=
CONVEX_EVENT_PATH=/addKavenRun
# ===== 기타 =====
OPENCLAW_GATEWAY_URL=http://localhost:18789
ENV보안 주의:
.env는 절대 Git에 커밋하지 마세요. 운영 주의 (이슈 #7 정책):CONVEX_SITE_URL을 명시적으로 설정하지 않으면 이벤트 payload는 어떤 외부 엔드포인트로도 전송되지 않습니다. 하드코딩된 Convex endpoint는 제거되었으며, 원격 백업이 필요한 경우에만 opt-in 하세요.
# 1회 실행
python src/kaven/kaven.py --once
# 감시 모드 (기본 5분)
python src/kaven/kaven.py --watch
# 감시 모드 (예: 10분)
python src/kaven/kaven.py --watch --interval 10docker run --rm -d --name searxng -p 8080:8080 searxng/searxng
curl -s "http://localhost:8080/search?q=Hormuz&format=json" | headmacOS + Colima 사용 시 먼저
colima start후 Docker 명령 실행.
cd deploy/docker
docker compose up -d
docker compose psuvicorn webapp.backend.app:app --reload --port 8000python -m http.server 8080 --directory webapp/frontend접속: http://127.0.0.1:8080
- 실행 로그:
src/kaven/logs/kaven_YYYYMMDD.jsonl(구버전maven_*.jsonl도 읽기 호환) - dedup 캐시:
src/kaven/logs/sent_cache.json
CHAT_ID가 아니라TELEGRAM_CHAT_ID를 써야 함.env는 루트가 아니라src/kaven/.env에 있어야 자동 로드됨- SearxNG 미구동 시 뉴스/소셜 수집 저하
CONVEX_SITE_URL 미설정 — 외부 전송 스킵로그: 정상 동작입니다. 기본 정책(이슈 #7)상 외부 전송은 opt-in이며, 로컬 로그는 이미 저장되어 있습니다. 원격 백업이 필요하면CONVEX_SITE_URL을 설정하세요.Convex 저장 실패 (로컬 로그는 유지)가 떠도 로컬 로그는 정상 저장됨 (원격 실패는 예외로 전파되지 않음)
- 텔레그램 봇 생성,
TELEGRAM_CHAT_ID/토픽 ID 확인, DM 설정, 오류 해결은 아래 문서를 참고하세요. - 문서:
docs/telegram-faq.md
# 전체 테스트
make test
# Kaven 핵심 테스트
make test-kaven
# 직접 실행
pytest -q이 프로젝트는 MIT License를 사용합니다.
- 개인/상업적 사용, 수정, 재배포를 자유롭게 허용합니다.
- 단, 배포 시 저작권 고지와 라이선스 본문(
LICENSE)을 포함해야 합니다.
- 이슈 또는 변경 목적을 먼저 정리합니다.
- 기능 브랜치를 생성합니다.
- 코드 변경 후
pytest -q를 통과시킵니다. - 문서(README/운영 가이드)도 함께 업데이트합니다.
- PR에 변경 이유/테스트 결과/운영 영향도를 명확히 작성합니다.
src/kaven/README.mdwebapp/README.mddocs/release-notes.mddocs/webapp-checklist.mddocs/telegram-faq.mddeploy/systemd/kaven.servicedeploy/docker/docker-compose.yml
v0.0.04부터 감시 구역, RSS 피드, 뉴스·소셜 키워드를 JSON 설정 파일로 관리합니다.
탐색 순서:
KAVEN_CONFIG환경변수가 가리키는 경로src/kaven/config.json(기본)- 파일 없으면 내장 기본값 사용 (기존 동작과 완전 호환)
cp src/kaven/config.example.json src/kaven/config.json
# 편집 후 재시작
python src/kaven/kaven.py --once{
"ais_zones": [
{"id": "hormuz", "name": "호르무즈 해협", "enabled": true,
"lat_min": 25.5, "lat_max": 27.0, "lon_min": 56.0, "lon_max": 57.5, "baseline_ships": 50},
{"id": "malacca", "name": "말라카 해협", "enabled": false, ...}
],
"adsb_zones": [
{"id": "middle_east", "name": "중동", "enabled": true,
"lat_min": 24.0, "lat_max": 38.0, "lon_min": 44.0, "lon_max": 62.0}
],
"news_feeds": [
{"id": "reuters_world", "name": "Reuters World", "enabled": true,
"url": "https://feeds.reuters.com/Reuters/worldNews"}
],
"news_keywords": [
{"id": "iran_military", "query": "Iran military", "enabled": true}
],
"social_keywords": [
{"id": "iran_hormuz", "query": "Iran Hormuz", "enabled": true}
]
}true(기본): 해당 구역/피드/키워드 활성화false: 수집에서 제외하지만 설정 파일에는 유지 (나중에 다시 활성화 가능)- 플래그 미지정 시
true로 간주
# 웹 API로 현재 활성/비활성 항목 조회
curl http://127.0.0.1:8000/config설정 파일에 특정 섹션만 포함해도 됩니다. 예를 들어 AIS 구역만 커스터마이즈하고 싶으면:
{
"ais_zones": [
{"id": "bab_el_mandeb", "name": "바브엘만데브", "enabled": true,
"lat_min": 12.0, "lat_max": 14.0, "lon_min": 42.5, "lon_max": 44.0}
]
}→ ais_zones만 치환되고 나머지(adsb_zones, news_feeds, 등)는 내장 기본값 사용.