kcg-ai-monitoring/docs/prediction-analysis.md
htlee b37e18d952 docs: prediction-analysis 신규 + 루트/SFR 문서 drift 해소
- docs/prediction-analysis.md 신설 — opus 4.7 독립 리뷰 기반 prediction 구조/방향 심층 분석
  (9개 섹션: 아키텍처·5분 사이클·17 알고리즘·4대 도메인 커버리지·6축 구조 평가·개선 제안 P1~P4·임계값 전수표)
- AGENTS.md / README.md — V001~V016→V030, Python 3.9→3.11+, 14→17 알고리즘 모듈
- docs/architecture.md — /gear-collision 라우트 추가 (26→27 보호 경로)
- docs/sfr-traceability.md — V029→V030, 48→51 테이블, SFR-10 에 GEAR_IDENTITY_COLLISION 추가
- docs/sfr-user-guide.md — 어구 정체성 충돌 페이지 섹션 신설
- docs/system-flow-guide.md — 노드 수 102→115, V030 manifest 미반영 경고
- backend/README.md — "Phase 2 예정" 상태 → 실제 운영 구성 + PR #79 hotfix 요구사항 전면 재작성
2026-04-17 11:20:53 +09:00

251 lines
22 KiB
Markdown

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Prediction 모듈 심층 분석 — 구조·방향 리뷰
**대상:** `prediction/` (Python 3.11+, FastAPI, APScheduler, 59 `.py` 파일)
**작성일:** 2026-04-17
**작성 관점:** opus 4.7 독립 리뷰 — 정밀도 튜닝이 아닌 **방향성·코드 구조**
**전제:** 프로토타입·데모 단계. 정밀도 미흡은 인지된 상태.
---
## TL;DR — 3줄 요약
1. **뼈대는 튼튼하다.** 레이어 분리(algorithms/pipeline/output/db/cache), 순수함수 위주 알고리즘, 카테고리별 dedup 윈도우 분리까지 프로토타입치고는 일관된 설계.
2. **약한 고리는 오케스트레이터.** [scheduler.py run_analysis_cycle()](../prediction/scheduler.py) 한 함수가 700+ 라인, 지역 try/except + `logger.warning` 로 흡수된 실패가 많아 "어디서 깨졌는지 조용히 묻힌다". 상태 누적(모듈 전역 `_transship_pair_history`)도 여기 묶여 있음.
3. **커버리지 매트릭스는 4/4 이지만 UI 비대칭.** prediction 이 생산하는 결과 중 `ILLEGAL_FISHING_PATTERN` 이벤트·환적 의심은 DB·백엔드까지 도달하지만 전용 detection UI 가 없다. prediction 품질 개선과 무관하게 운영자가 쓸 수 없는 상태.
**권고 최우선 3가지** — 신규 알고리즘 추가보다 아래가 선행:
- **P1:** 사이클 스테이지 단위 에러 경계(`_stage(...)` 유틸)로 교체해 실패 스테이지 명시 로깅 + 부분 실패 시에도 후속 단계 진행
- **P1:** 하드코딩 임계값(MID prefix, 커버리지 박스, SOG band, 11 pattern 점수) 을 `correlation_param_models` 패턴처럼 DB/config 로 외부화
- **P1:** 환적 전용 + ILLEGAL_FISHING_PATTERN 전용 프론트 페이지 추가 — 이미 DB·API 는 있음
---
## 1. 아키텍처 레이어 — 책임과 결합도
```
prediction/
├── config.py Pydantic Settings + qualified_table() — SSOT 설정
├── scheduler.py APScheduler + run_analysis_cycle() (단일 엔트리)
├── main.py FastAPI app + /health /status /chat 등
├── fleet_tracker.py 상태 보유 (선단 레지스트리, 어구 정체성)
├── time_bucket.py 안전 지연 12분 + backfill 3 버킷
├── algorithms/ 17개 모듈 — 순수함수 중심
├── pipeline/ 8개 모듈 — 7단계 분류 파이프라인
├── output/ 5개 모듈 — event/violation/kpi/stats/alert
├── db/ 4개 모듈 — snpdb / kcgdb / signal_api / partition_manager
├── cache/ vessel_store.py — 24h sliding window 인메모리
├── chat/ Ollama + RAG 스텁
├── models/ result.py — AnalysisResult dataclass
├── data/ monitoring zones JSON 정적 설정
└── tests/ time_bucket / gear_parent_episode / gear_parent_inference 3종
```
| 레이어 | 책임 | 결합도 평가 |
|---|---|---|
| `config.py` | 환경 + SQL identifier 검증 | ✅ SSOT, `qualified_table()` 로 스키마 주입 공격 방지 |
| `algorithms/` | 탐지 로직 (순수) | ✅ 대부분 `df, params -> dict/tuple`. 상호 의존 적음 |
| `pipeline/` | 7단계 sequential | ✅ `orchestrator.ChineseFishingVesselPipeline.run()` 이 DF 를 그대로 파이프 |
| `output/` | 룰 엔진 + DB write | ✅ 룰을 lambda 리스트(event_generator.RULES)로 선언적 관리 |
| `db/` | Connection pool + SQL | ⚠️ `kcgdb.upsert_results(results)` 가 한 트랜잭션에 전부 묶임 (파티션 unique index 활용은 적절) |
| `cache/vessel_store.py` | 전역 싱글턴, 24h 궤적 인메모리 | ⚠️ 모듈 싱글턴 → 테스트 시 mock 어려움 |
| `fleet_tracker.py` | 레지스트리·어구 정체성 상태 | ⚠️ 싱글턴 + 모듈 전역 캐시 |
| `scheduler.py` | 전체 오케스트레이션 | ❌ **700+ 라인 모놀리식** — 아래 §2 상세 |
---
## 2. 5분 사이클 시퀀스 — [scheduler.py:80-804](../prediction/scheduler.py#L80-L804)
사이클 전체가 **한 함수** 안에서 9단계로 진행된다.
| 단계 | 라인 | 역할 | 실패 처리 |
|---|---|---|---|
| 1. 증분 로드 | [97-106](../prediction/scheduler.py#L97-L106) | `snpdb.fetch_incremental()` → vessel_store merge | 전체 try/except 포함 |
| 2. 정적 보강 | [108-112](../prediction/scheduler.py#L108-L112) | signal-batch API 호출 | 전체 try/except |
| 3. 대상 선별 | [114-128](../prediction/scheduler.py#L114-L128) | SOG/COG 계산 + 0건 시 조기 return | ✅ 조기 반환 |
| 4. 파이프라인 | [122-128](../prediction/scheduler.py#L122-L128) | `ChineseFishingVesselPipeline.run()` | 전체 try/except |
| 5. 선단 분석 | [131-177](../prediction/scheduler.py#L131-L177) | fleet_tracker + gear_identity 충돌 감지 | ⚠️ 내부 try/except 로 warning, 전진 |
| 5.5. 어구 그룹·상관·부모 추론 | [181-229](../prediction/scheduler.py#L181-L229) | polygon_builder + gear_correlation | ⚠️ 내부 try/except, 결과 없이 진행 |
| 5.9. 쌍끌이 후보·판정 | [231-303](../prediction/scheduler.py#L231-L303) | pair_trawl STRONG/PROBABLE/SUSPECT | ⚠️ 내부 try/except |
| 6. 개별 선박 분석 | [305-515](../prediction/scheduler.py#L305-L515) | AnalysisResult 생성 (파이프라인 통과자) | 루프 내 continue |
| 6.5. 경량 분석 | [523-682](../prediction/scheduler.py#L523-L682) | 파이프라인 미통과 중국 MID — `compute_lightweight_risk_score` | 루프 내 try/except |
| 7. 환적 의심 | [685-713](../prediction/scheduler.py#L685-L713) | `detect_transshipment` + `_transship_pair_history` 누적 | 전체 try/except |
| 8. DB upsert | [716-717](../prediction/scheduler.py#L716-L717) | `kcgdb.upsert_results()` + `cleanup_old(48h)` | 전체 try/except |
| 9. 출력 모듈 | [720-745](../prediction/scheduler.py#L720-L745) | violation_classifier → event_generator → kpi_writer → aggregate_hourly/daily → alert_dispatcher | ⚠️ **5개 단계를 한 try/except 로 묶음** → 어디서 실패했는지 단일 warning 으로 흡수 |
| 10. 채팅 컨텍스트 캐싱 | [748-791](../prediction/scheduler.py#L748-L791) | Redis | 전체 try/except |
### 구조적 관찰
- **전체 try/except 는 있다** ([97](../prediction/scheduler.py#L97) ~ [803](../prediction/scheduler.py#L803)) — 치명 실패가 다음 사이클을 막지는 않음
- **그러나 내부 스테이지가 너무 무겁다.** 9번째 출력 단계가 5개 모듈을 한 덩어리로 묶어 `logger.warning('output modules failed (non-fatal): %s', e)` 로 흡수. 어느 모듈이 깨졌는지 디버깅하려면 stacktrace 를 `logger.exception` 으로 바꿔야 함
- **Lazy import** 가 스테이지마다 반복 (`from output.event_generator import ...` 등). 시작 시간 단축에는 도움이지만 import 오류가 **첫 사이클 실행 시점에만 드러남** — 배포 후 5분 지연 발견 경험 다수
### 권고 (사이클 구조 재정비)
```python
def _stage(name: str, fn, *args, required=False, **kwargs):
t0 = time.time()
try:
result = fn(*args, **kwargs)
logger.info('stage %s ok in %.2fs', name, time.time() - t0)
return result
except Exception as e:
logger.exception('stage %s failed: %s', name, e)
if required:
raise
return None
```
- 각 스테이지를 `_stage('pair_detection', _run_pair_detection, ...)` 로 감싸면 실패 스테이지 명시 로깅 + stacktrace + 부분 실패 허용 정책을 일관화.
- 9번 단계의 5개 모듈은 각각 별도 `_stage(...)` 호출로 쪼갤 것.
---
## 3. 알고리즘 카탈로그 (17 모듈 × 담당 도메인)
| 파일 | 주 역할 | 입력 | 출력 | 주요 상수 |
|---|---|---|---|---|
| [dark_vessel.py](../prediction/algorithms/dark_vessel.py) | AIS gap + 11 패턴 점수화 | 선박 DF(timestamp, lat, lon, sog, cog) | (score 0~100, patterns[], tier) | GAP_SUSPICIOUS=6000s, VIOLATION=86400s, KR_COVERAGE box |
| [spoofing.py](../prediction/algorithms/spoofing.py) | 텔레포트·극속·BD09 오프셋 | 선박 DF | spoofing_score 0~1 | EXTREME_SPEED=50kn (주석 기준), fishing max=25kn |
| [risk.py](../prediction/algorithms/risk.py) | 종합 risk + 경량 risk 2종 | DF, zone, is_permitted, 외부 점수 | (risk 0~100, level) | tier: 70+=CRITICAL, 50+=HIGH, 30+=MEDIUM |
| [fishing_pattern.py](../prediction/algorithms/fishing_pattern.py) | UCAF/UCFT gear SOG band | DF, gear | (ucaf, ucft) | PT 2.5~4.5, OT 2.0~4.0, GN 0.5~2.5 |
| [transshipment.py](../prediction/algorithms/transshipment.py) | 5단계 필터 파이프라인 | DF targets, pair_history, zone_fn | list of dict | PROXIMITY ~220m, RENDEZVOUS 90min, WATCH 제외 |
| [location.py](../prediction/algorithms/location.py) | zone 분류, haversine_nm, BD09 | (lat, lon) | zone, dist_nm | 12/24 NM 밴드 |
| [gear_correlation.py](../prediction/algorithms/gear_correlation.py) | 멀티모델 EMA + streak | vessel_store, gear_groups, conn | UPDATE gear_correlation_scores | α_base=0.30, polygon=0.70 |
| [gear_identity.py](../prediction/algorithms/gear_identity.py) | 공존 쌍 추출 (V030/PR #73) | gear_signals | collisions[] | CRITICAL_DIST=50km, COEXIST=3회 |
| [gear_parent_inference.py](../prediction/algorithms/gear_parent_inference.py) | 어구 → 모선 assignment | gear_groups + tracks | parent 후보 + confidence | 2-pass: direct-match → candidates |
| [gear_parent_episode.py](../prediction/algorithms/gear_parent_episode.py) | 에피소드 delineation (first_seen~last_seen) | gear_signals 시계열 | episodes[] | gap tolerance |
| [gear_violation.py](../prediction/algorithms/gear_violation.py) | G-01~G-06 통합 판정 (DAR-03) | DF + zone + pair_result + permits | g_codes[], evidence, score | G-06=20pts, G-02=18pts, G-01=15pts |
| [gear_name_rules.py](../prediction/algorithms/gear_name_rules.py) | 어구 이름 정규표현식 | string | parent_code (Optional) | regex set |
| [pair_trawl.py](../prediction/algorithms/pair_trawl.py) | 쌍끌이 tier 분류 | DF×2, 6h | (pair_detected, tier, pair_type) | PROXIMITY=0.27NM, MIN_SYNC=2h |
| [track_similarity.py](../prediction/algorithms/track_similarity.py) | DTW 궤적 유사도 | DF×2 | 0~1 | - |
| [fleet.py](../prediction/algorithms/fleet.py) | leader/follower/independent | DF, tracker | role | - |
| [polygon_builder.py](../prediction/algorithms/polygon_builder.py) | gear group convex hull | vessel_store, companies | 스냅샷[] | 시간버킷 |
| [vessel_type_mapping.py](../prediction/algorithms/vessel_type_mapping.py) | fishery_code → vessel_type 폴백 | fishery_code | 'TRAWL'/'PT'/... | - |
**관찰:**
- 대부분 **순수 함수** → 재사용·단위테스트 용이. 단 `gear_correlation.run_gear_correlation``conn` 을 받아 DB 를 직접 UPDATE 함 (알고리즘 + I/O 혼재)
- **상수가 모듈 상단에 모여 있는 것은 좋으나 config 외부화는 안 됨**. 현장 운영자가 임계값을 바꾸려면 코드 배포 필요
---
## 4. 4대 도메인 커버리지 매트릭스
| 도메인 | 담당 파일 | 접근 방식 | 구조적 강점 | 구조적 공백 |
|---|---|---|---|---|
| **Dark Vessel** | `dark_vessel.py` + `risk.py` + `compute_dark_suspicion` | gap(100분 임계) + 11 패턴 점수 + 4-tier (CRITICAL≥70) | 패턴 분리(P1~P11) + 한국 커버리지 감점(50pt)으로 자연 gap 구분 | MID/커버리지 박스 하드코딩. 감점 로직은 탐지 누락 위험(coverage box 바깥=자연 gap 가정) — 중국 EEZ 해역 근접 공해에서 의도적 OFF 가 감점 맞을 수 있음 |
| **Gear Tracking** | `gear_correlation` + `gear_identity` + `gear_parent_inference` + `polygon_builder` | 어구 이름 regex → 공존/교체 분기 + 멀티모델 EMA + 모선 추론 + 폴리곤 | 공존(GEAR_IDENTITY_COLLISION)을 1순위 증거로 재정의 (V030). SAVEPOINT 로 PK 충돌 격리 | 어구 신호는 **이름 regex 의존**. 이름이 GEAR_PATTERN 에 맞지 않으면 파이프라인 전체 진입 실패 |
| **Chinese Fishing** | `pipeline/` 7단계 + MID 412/413/414 필터 | preprocess → behavior → resample → feature → classify → cluster → seasonal + rule-based 분류 | 룰 + 군집으로 해석 가능 | MID prefix 하드코딩 2곳 중복. 전체 인구(55k)대비 500척만 파이프라인 통과 — 경량 분석(§6)으로 보완하나 여전히 <10% |
| **Illegal Pattern** | `gear_violation` (G-01~G-06) + `event_generator.RULES` (15+ ) + `transshipment` 5단계 | 임계값 기반 독립 × 카테고리별 dedup 윈도우 | 룰이 lambda 리스트로 선언적 | **UI 미노출** DB `prediction_events` 카테고리 `ILLEGAL_*` 생산되나 전용 detection UI 없음. 운영자는 EventList(`/event-list`) 에서만 조회. 환적도 동일 문제 |
---
## 5. 코드 구조 평가 (6축)
| | 평가 | 근거 |
|---|---|---|
| **관심사 분리** | B+ | algorithms / pipeline / output / db 레이어는 깔끔. scheduler 오케스트레이터가 아니라 **메가 함수** |
| **재사용성** | A- | 17 알고리즘 모듈 ~15개가 순수함수. `run_gear_correlation` conn 혼재 |
| **테스트 가능성** | C+ | unit test 3개만 (time_bucket / gear_parent_episode / gear_parent_inference). `vessel_store` / `fleet_tracker` 싱글턴 integration test 어려움 |
| **에러 격리** | C | 사이클 전체 try/except + 내부 지역 try/except 혼재. 출력 5모듈이 덩어리 실패 지점 특정 불가 |
| **동시성** | A- | `ThreadedConnectionPool(1,5)`, `max_instances=1` 스케줄러 단일 프로세스 가정 하에서 안전 |
| **설정 가능성** | C- | 임계값 대부분 파일 상수. `correlation_param_models` 패턴만 DB 기반 (예외) |
### 주목할 잘된 점
- **Dedup 윈도우 카테고리별 차등** ([event_generator.py:26-39](../prediction/output/event_generator.py#L26-L39)) 5분 boundary 집단 만료를 피하기 위해 33/67/89/127/131/181/367분 **의도적으로 5의 배수 회피**. 기반 탐지의 대표적인 "튜닝 knob" 코드에 명시.
- **gear_identity 공존/교체 분기** ([fleet_tracker.py](../prediction/fleet_tracker.py) 트랜잭션 설계) SAVEPOINT 부분 실패를 사이클 전체 abort 분리. 이전 13h 공백 사고의 재발 방지 설계가 구조에 반영됨.
- **Lightweight path** 파이프라인 통과 못한 중국 MID 경량 경로로 계속 커버 ([scheduler.py:523-682](../prediction/scheduler.py#L523-L682)). "정밀 vs 커버리지" 경로로 나누는 의사결정 자체는 탁월.
### 구조적 채무
- **환경 분기 부재**: `config.py` dev/prod 분기가 없음. `.env` 파일 하나에 의존. 로컬 실행 운영 DB 건드리는 위험 ([config.py:8-22](../prediction/config.py#L8-L22))
- **상태 있는 모듈 전역 변수**: `_transship_pair_history`, `_last_run`, `_scheduler` ([scheduler.py:16-26](../prediction/scheduler.py#L16-L26)). 테스트 격리 어렵고, 재시작 pair 누적 상태 증발
- **DB 쓰기 산재**: `kcgdb.upsert_results` / `save_group_snapshots` / `gear_correlation UPDATE` / `gear_identity UPSERT` / `prediction_events INSERT` 서로 다른 트랜잭션. 사이클 원자성 X 의도적일 있으나 명시 설계 문서 없음
---
## 6. 방향성 진단 — 프로토타입 → MVP → 운영
### 지금 강점
- ** 기반 탐지를 탄탄히 다져둔 토대** 임계값이 드러나 있고 dedup 설계가 명시적. 향후 ML overlay 얹을 "어디에 얹을지" 명확 (dark_suspicion score, transship score, gear_violation score 모두 연속값으로 산출)
- **운영자 의사결정 통합 설계** V030 GEAR_IDENTITY_COLLISION 에서 status(OPEN/REVIEWED/CONFIRMED_ILLEGAL/FALSE_POSITIVE) severity 재계산을 억제하는 패턴 사람 loop back 설계된 유일한 자리. 다른 도메인에도 패턴 확장 가능
### 지금 약점
- **ML 부재** sklearn/torch 없음. 2026 기준 프로토타입으로는 적절하나, sequence anomaly (dark gap 시계열 반복 패턴) behavioral classifier 룰만으론 한계
- **하드코딩 지대**: MID prefix(4개소), KR coverage box, SOG band, 11 패턴 점수, 5단계 transship 임계 모두 " 프로젝트에서 튜닝해야 핫스팟" 인데 DB/config 분리
- **UI 비대칭**: `prediction_events.category='ILLEGAL_FISHING_PATTERN'` 생산되지만 전용 UI 없음. 환적도 동일. 결과적으로 **prediction 이 만드는 가치의 일부가 운영자에게 도달하지 못함**
- **테스트 빈곤**: 17 알고리즘 3개만 유닛테스트. 사이클 단위 integration test 전혀 없음 사이클 회귀가 항상 운영 로그로만 드러남 (13h 공백 사고가 대표 사례)
---
## 7. 구조적 개선 제안 (우선순위별)
### P1 — 지금 해야 할 것 (운영 안정성)
1. **사이클 스테이지 단위 에러 경계** `_stage(name, fn, required=False)` 유틸로 9번 출력 5모듈을 쪼갤 . `logger.exception` 으로 stacktrace 보존. `required=True` `fetch_incremental` 같은 fatal 에만 적용 실패 조기 반환
2. **임계값 외부화** `correlation_param_models` 패턴을 확장해 `detection_params` 테이블 신설 (algo_name, param_key, value, active_from, active_to). 배포 없이 해상도 튜닝 가능. 운영자 권한으로 접근 감사 로그
3. **ILLEGAL_FISHING_PATTERN 전용 페이지** + **환적 전용 페이지** 백엔드 API·DB 이미 존재. 프론트만 GearCollisionDetection 패턴으로 추가 (`PageContainer` + `DataTable` + `Badge intent`)
4. **사이클 부분 원자성 명시** DB 쓰기 경계 문서화 (어디까지가 트랜잭션인지). 최소한 [architecture.md](architecture.md) 또는 신설 `docs/prediction-transactions.md` 다이어그램
### P2 — 다음 (품질 확보)
5. **알고리즘 유닛테스트 커버리지** 17 모듈 최소 10 (dark_vessel 11 패턴 / transshipment 5단계 / gear_violation 6 G-code / spoofing / risk) fixture 기반 테스트. `tests/fixtures/` AIS DF CSV 샘플
6. **DB fixture integration test** testcontainers-python 으로 PostgreSQL 띄워 사이클 실행 + 결과 테이블 assert. CI 에서 돌릴 있도록 데이터 10 x 1h 정도 경량
7. **`vessel_store` / `fleet_tracker` 의존성 주입 개편** 모듈 싱글턴 `AnalysisContext` dataclass 명시 주입. 테스트 mock 가능
8. **MID prefix·커버리지 box 를 `monitoring_zones` JSON 연장** 이미 `data/monitoring_zones.json` 있음. 동일 포맷으로 `mid_prefixes.json` / `kr_coverage.json` 추가
### P3 — 중기 (가치 확장)
9. **ML overlay 타겟 설정** dark_suspicion score (11 패턴 합산) classifier training target 으로 최적. GCN/Transformer **"gap 시퀀스가 의도적인가"** 학습. 유지 + 게이트만 ML 대체 (shadow mode 비교)
10. **correlation 파라미터 MLOps 연동** `correlation_param_models` MLflow 실험 기록 성능 좋은 모델 자동 active 전환
11. **AIS 벤치마크 데이터셋** 한중어업협정 906척 과거 단속 이력 있는 선박을 positive label. 현재 매칭률 53%+ 이므로 샘플 확보 가능. tier precision/recall 산출
### P4 — 장기 (스케일)
12. **multi-process / async** APScheduler + 단일 스레드 한계. 55k 선박 / 2.3M points / 110초 사이클에서 8k 중국 증가 + 한국 확장 5분 주기 완료 불가 예측. asyncio + ray / dask 스테이지 병렬
13. **Event bus 분리** 지금은 `prediction_events` INSERT 동기. outbox 패턴으로 비동기 분리 백엔드/프론트 실시간 push 기반 (WebSocket) 진화 가능
---
## 8. 부록 — 임계값 전수표 (외부화 우선순위)
| 위치 | 상수 | 현재값 | P1 외부화 | 비고 |
|---|---|---|---|---|
| [scheduler.py:28](../prediction/scheduler.py#L28) | `_KR_DOMESTIC_PREFIXES` | `('440','441')` | | 한국 MID |
| [scheduler.py:140,247](../prediction/scheduler.py#L140) | 중국 MID prefix | `'412' '413' '414'` 하드코딩 2곳 | | `mid_prefixes.json` |
| [scheduler.py:256](../prediction/scheduler.py#L256) | pair pool SOG band | `1.5 <= mean_sog <= 5.0` | | 조업 속력대 |
| [dark_vessel.py:6-8](../prediction/algorithms/dark_vessel.py#L6-L8) | GAP 임계 3종 | 6000/10800/86400s | | 100분/3h/24h |
| [dark_vessel.py:11-12](../prediction/algorithms/dark_vessel.py#L11-L12) | `_KR_COVERAGE_LAT/LON` | 32.0~39.5 / 124.0~132.0 | | AIS 수신 박스 |
| [dark_vessel.py:257-359](../prediction/algorithms/dark_vessel.py#L257-L359) | 11 패턴 점수 P1~P11 | 10~30 pt 분산 | | 탐지 정책 튜닝 대상 |
| [event_generator.py:26-39](../prediction/output/event_generator.py#L26-L39) | DEDUP_WINDOWS 12 카테고리 | 33~367분 | | 이미 의도적 |
| [config.py:25-37](../prediction/config.py#L25-L37) | 파이프라인 주기 | 5/24/60/30/12/3 | env | `.env` 이미 가능 |
| [transshipment.py](../prediction/algorithms/transshipment.py) | PROXIMITY / RENDEZVOUS | 0.002deg / 90min | | 환적 민감도 |
| [pair_trawl.py](../prediction/algorithms/pair_trawl.py) | PROXIMITY / SOG_Δ / COG_Δ / MIN_SYNC | 0.27NM / 0.5kn / 10° / 2h | | tier 재분류 기준 |
**P1:** 배포 없이 튜닝 가능해야
**:** 튜닝 자체가 탐지 정책 변경 릴리즈 노트 필요
---
## 9. 관련 파일 인덱스
- 오케스트레이터: [scheduler.py](../prediction/scheduler.py)
- 설정: [config.py](../prediction/config.py)
- 상태 컴포넌트: [fleet_tracker.py](../prediction/fleet_tracker.py), [cache/vessel_store.py](../prediction/cache/vessel_store.py)
- 알고리즘: [algorithms/](../prediction/algorithms/)
- 파이프라인: [pipeline/orchestrator.py](../prediction/pipeline/orchestrator.py)
- 출력: [output/event_generator.py](../prediction/output/event_generator.py), [output/violation_classifier.py](../prediction/output/violation_classifier.py), [output/kpi_writer.py](../prediction/output/kpi_writer.py), [output/stats_aggregator.py](../prediction/output/stats_aggregator.py), [output/alert_dispatcher.py](../prediction/output/alert_dispatcher.py)
- DB: [db/kcgdb.py](../prediction/db/kcgdb.py), [db/snpdb.py](../prediction/db/snpdb.py), [db/partition_manager.py](../prediction/db/partition_manager.py)
연관 운영 문서:
- [architecture.md](architecture.md) 프론트엔드 아키텍처
- [sfr-traceability.md](sfr-traceability.md) SFR 매트릭스
- [system-flow-guide.md](system-flow-guide.md) system-flow 노드 명세
- `backend/README.md` 백엔드 구성 (V030 + PR #79 hotfix 요구사항 명시)
---
## 변경 이력
| 일자 | 내용 |
|---|---|
| 2026-04-17 | 초판 opus 4.7 독립 리뷰. 구조/방향 중심 + 우선순위별 개선 제안 |