Codex Lab 환경(iran-airstrike-replay-codex)에서 검증 완료된 어구 모선 자동 추론 + 검토 워크플로우 전체를 이식. ## Python (prediction/) - gear_parent_inference(1,428줄): 다층 점수 모델 (correlation + name + track + prior bonus) - gear_parent_episode(631줄): Episode 연속성 (Jaccard + 공간거리) - gear_name_rules: 모선 이름 정규화 + 4자 미만 필터 - scheduler: 추론 호출 단계 추가 (4.8) - fleet_tracker/kcgdb: SQL qualified_table() 동적화 - gear_correlation: timestamp 필드 추가 ## DB (database/migration/ 012~015) - 후보 스냅샷, resolution, episode, 라벨 세션, 제외 관리 테이블 9개 + VIEW 2개 ## Backend (Java) - 12개 DTO/Controller (ParentInferenceWorkflowController 등) - GroupPolygonService: parent_resolution LEFT JOIN + 15개 API 메서드 ## Frontend - ParentReviewPanel: 모선 검토 대시보드 - vesselAnalysis: 10개 신규 API 함수 + 6개 타입 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
694 lines
21 KiB
Markdown
694 lines
21 KiB
Markdown
# Gear Parent Inference Workflow V2
|
|
|
|
## 문서 목적
|
|
|
|
이 문서는 lab 환경의 어구 모선 추적 워크플로우를 v1 운영 override 중심 구조에서,
|
|
`평가 데이터 축적 + 후보 제외 관리 + 기간형 정답 라벨 추적` 중심 구조로 재정의하는 설계서다.
|
|
|
|
대상 범위는 아래와 같다.
|
|
|
|
- `kcg_lab` 스키마
|
|
- `backend-lab` (`192.168.1.20:18083`)
|
|
- `prediction-lab` (`192.168.1.18:18091`)
|
|
- 로컬 프론트 `yarn dev:lab`
|
|
|
|
운영 `kcg` 스키마와 기존 데모 동작은 이번 설계 단계에서 변경하지 않는다.
|
|
|
|
현재 구현 기준으로는 v2 Phase 1 저장소/API가 이미 lab에 반영되어 있고, 그 위에 `015_gear_parent_episode_tracking.sql`과 `prediction/algorithms/gear_parent_episode.py`를 통해 `episode continuity + prior bonus` 계층이 추가되었다. 이 문서는 여전히 워크플로우 설계서지만, 사람 판단 저장소와 자동 추론 저장소 분리 원칙은 현재 코드의 실제 기준이기도 하다.
|
|
|
|
## 배경
|
|
|
|
현재 v1은 자동 추론 결과와 사람 판단이 같은 저장소에 섞여 있다.
|
|
|
|
- `확정`은 `gear_group_parent_resolution`을 `MANUAL_CONFIRMED`로 덮어쓴다.
|
|
- `24시간 제외`는 특정 그룹에서 후보 1개를 24시간 숨긴다.
|
|
- 자동 추론은 계속 돌지만, 수동 판단이 최종 상태를 override한다.
|
|
|
|
이 구조는 단기 운용에는 편하지만, 아래 목적에는 맞지 않는다.
|
|
|
|
- 사람이 보면서 모델 가중치와 후보 생성 품질을 평가
|
|
- 정답/오답 사례를 데이터셋으로 축적
|
|
- 충분한 정확도 확보 후 자동화 또는 LLM 연결
|
|
|
|
따라서 v2에서는 `자동 추론`, `사람 라벨`, `후보 제외`를 분리한다.
|
|
|
|
## 핵심 목표
|
|
|
|
1. 자동 추론 상태는 계속 독립적으로 유지한다.
|
|
2. 사람 판단은 override가 아니라 별도 라벨/제외 데이터로 저장한다.
|
|
3. 그룹 단위 오답 라벨은 `1일 / 3일 / 5일` 기간형 후보 제외로 관리한다.
|
|
4. 전역 후보 제외는 모든 어구 그룹에서 동일 MMSI를 후보군에서 제거한다.
|
|
5. 정답 라벨은 `1일 / 3일 / 5일` 세션으로 만들고, 활성 기간 동안 자동 추론 결과를 별도 추적 로그로 남긴다.
|
|
6. 알고리즘은 DB exclusion/label 정보를 읽어 다음 cycle부터 바로 반영한다.
|
|
7. 향후 threshold 튜닝, 가산점 실험, LLM 연결 평가에 쓰일 수 있는 정량 지표를 만든다.
|
|
|
|
## 용어
|
|
|
|
- 자동 추론
|
|
- `gear_parent_inference`가 계산한 현재 cycle의 후보 점수와 추천 결과
|
|
- 그룹 제외
|
|
- 특정 `group_key + sub_cluster_id`에서 특정 후보 MMSI를 일정 기간 후보군에서 제거
|
|
- 전역 후보 제외
|
|
- 특정 MMSI를 모든 어구 그룹의 모선 후보군에서 제거
|
|
- 정답 라벨 세션
|
|
- 특정 어구 그룹에 대해 “이 MMSI가 정답 모선”이라고 사람이 지정하고, 일정 기간 자동 추론 결과를 추적하는 세션
|
|
- 라벨 추적
|
|
- 정답 라벨 세션 활성 기간 동안 자동 추론이 정답 후보를 어떻게 rank/score하는지 누적 저장하는 기록
|
|
|
|
## 현재 v1의 한계
|
|
|
|
### 1. `확정`이 평가 라벨이 아니라 운영 override다
|
|
|
|
- 현재 `CONFIRM`은 resolution을 `MANUAL_CONFIRMED`로 덮어쓴다.
|
|
- 이 경우 자동 추론의 실제 성능과 사람 판단이 섞여, 나중에 모델 정확도를 평가하기 어렵다.
|
|
|
|
### 2. `24시간 제외`는 기간과 범위가 너무 좁다
|
|
|
|
- 현재는 그룹 단위 24시간 mute만 가능하다.
|
|
- `1/3/5일`처럼 길이를 다르게 두고 비교할 수 없다.
|
|
- “이 MMSI는 아예 모선 후보 대상이 아니다”라는 전역 규칙을 넣을 수 없다.
|
|
|
|
### 3. 백데이터 축적 구조가 없다
|
|
|
|
- 현재는 review log는 남지만, “정답 후보가 cycle별로 몇 위였는지”, “점수가 어떻게 변했는지”, “후보군에 들어왔는지”를 체계적으로 저장하지 않는다.
|
|
|
|
### 4. 장기 세션에 대한 그룹 스코프가 약하다
|
|
|
|
- 현재 그룹 기준은 `group_key + sub_cluster_id`다.
|
|
- 기간형 라벨/제외를 도입하면 subcluster 재편성 리스크를 고려해야 한다.
|
|
|
|
## v2 설계 원칙
|
|
|
|
### 1. 자동 추론 저장소는 그대로 유지한다
|
|
|
|
아래 기존 저장소는 계속 자동 추론 전용으로 유지한다.
|
|
|
|
- `gear_group_parent_candidate_snapshots`
|
|
- `gear_group_parent_resolution`
|
|
- `gear_group_parent_review_log`
|
|
|
|
단, `review_log`의 의미는 “UI action audit”로 바꾸고, 더 이상 최종 라벨 저장소로 보지 않는다.
|
|
|
|
### 2. 사람 판단은 새 저장소로 분리한다
|
|
|
|
사람이 내린 판단은 아래 두 축으로 분리한다.
|
|
|
|
- 제외 축
|
|
- 이 그룹에서 제외
|
|
- 전체 후보 제외
|
|
- 정답 축
|
|
- 기간형 정답 라벨 세션
|
|
|
|
### 3. 제외는 후보 생성 이후의 gating layer로 둔다
|
|
|
|
전역 후보 제외는 raw correlation이나 원시 선박 분류를 지우지 않는다.
|
|
|
|
- `gear_correlation_scores`는 계속 쌓는다.
|
|
- exclusion은 parent inference candidate set에서만 hard filter로 적용한다.
|
|
|
|
이렇게 해야 원시 모델 출력과 사람 개입의 차이를 비교할 수 있다.
|
|
|
|
### 4. 라벨 세션 동안 자동 추론은 계속 돈다
|
|
|
|
정답 라벨 세션이 활성화되어도 자동 추론은 그대로 수행한다.
|
|
|
|
- UI의 기본 검토 대기에서는 숨길 수 있다.
|
|
- 하지만 prediction은 계속 candidate snapshot과 tracking record를 남긴다.
|
|
|
|
### 5. lab에서는 override보다 평가를 우선한다
|
|
|
|
v2 이후 lab에서 사람 버튼은 기본적으로 자동 resolution을 덮어쓰지 않는다.
|
|
|
|
- 운영 override가 필요해지면 추후 별도 action으로 분리한다.
|
|
- lab의 기본 목적은 평가 데이터 생성이다.
|
|
|
|
## 사용자 액션 재정의
|
|
|
|
### `정답 라벨`
|
|
|
|
의미:
|
|
|
|
- 해당 어구 그룹의 정답 모선으로 특정 MMSI를 지정
|
|
- `1일 / 3일 / 5일` 중 하나의 기간 동안 자동 추론 결과를 추적
|
|
|
|
동작:
|
|
|
|
1. `gear_parent_label_sessions`에 active session 생성
|
|
2. 다음 cycle부터 prediction이 이 그룹에 대한 추적 로그를 `gear_parent_label_tracking_cycles`에 누적
|
|
3. 기본 review queue에서는 해당 그룹을 숨기고, 별도 `라벨 추적` 목록으로 이동
|
|
4. 세션 종료 후에는 completed label dataset으로 남음
|
|
|
|
중요:
|
|
|
|
- 자동 resolution은 계속 자동 상태를 유지
|
|
- 점수에 수동 가산점/감점은 넣지 않음
|
|
|
|
### `이 그룹에서 제외`
|
|
|
|
의미:
|
|
|
|
- 해당 어구 그룹에서만 특정 후보 MMSI를 일정 기간 후보군에서 제외
|
|
|
|
기간:
|
|
|
|
- `1일`
|
|
- `3일`
|
|
- `5일`
|
|
|
|
동작:
|
|
|
|
1. `gear_parent_candidate_exclusions`에 `scope_type='GROUP'` row 생성
|
|
2. 다음 cycle부터 해당 그룹의 candidate set에서 제거
|
|
3. 다른 그룹에는 영향 없음
|
|
4. 기간이 끝나면 자동으로 inactive 처리
|
|
|
|
용도:
|
|
|
|
- 이 후보는 이 어구 그룹의 모선이 아니라고 사람이 판단한 경우
|
|
- 단기/중기 관찰을 위해 일정 기간만 빼고 싶을 때
|
|
|
|
### `전체 후보 제외`
|
|
|
|
의미:
|
|
|
|
- 특정 MMSI는 모든 어구 그룹에서 모선 후보 대상이 아님
|
|
|
|
동작:
|
|
|
|
1. `gear_parent_candidate_exclusions`에 `scope_type='GLOBAL'` row 생성
|
|
2. prediction candidate generation에서 모든 그룹에 대해 hard filter
|
|
3. 해제 전까지 계속 적용
|
|
|
|
초기 정책:
|
|
|
|
- 전역 후보 제외는 기본적으로 기간 없이 active 상태 유지
|
|
- 수동 `해제` 전까지 유지
|
|
|
|
용도:
|
|
|
|
- 패턴 분류상 선박으로 들어왔지만 실제 모선 후보가 아니라고 판단한 AIS
|
|
- 잘못된 유형의 신호가 반복적으로 후보군에 유입되는 경우
|
|
|
|
### `해제`
|
|
|
|
의미:
|
|
|
|
- 활성 그룹 제외, 전역 제외, 정답 라벨 세션을 조기 종료
|
|
|
|
동작:
|
|
|
|
- exclusion/session row에 `released_at`, `released_by` 또는 `status='CANCELLED'`를 기록
|
|
- 다음 cycle부터 알고리즘 적용 대상에서 빠짐
|
|
|
|
## DB 설계
|
|
|
|
### 1. `gear_parent_candidate_exclusions`
|
|
|
|
역할:
|
|
|
|
- 그룹 단위 제외와 전역 후보 제외를 모두 저장
|
|
- active list의 단일 진실원
|
|
|
|
권장 컬럼:
|
|
|
|
```sql
|
|
CREATE TABLE kcg_lab.gear_parent_candidate_exclusions (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
scope_type VARCHAR(16) NOT NULL, -- GROUP | GLOBAL
|
|
group_key VARCHAR(100), -- GROUP scope에서만 사용
|
|
sub_cluster_id SMALLINT,
|
|
candidate_mmsi VARCHAR(20) NOT NULL,
|
|
reason_type VARCHAR(32) NOT NULL, -- GROUP_WRONG_PARENT | GLOBAL_NOT_PARENT_TARGET
|
|
duration_days INT, -- GROUP scope는 1|3|5, GLOBAL은 NULL 허용
|
|
active_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
active_until TIMESTAMPTZ, -- GROUP scope는 필수, GLOBAL은 NULL 가능
|
|
released_at TIMESTAMPTZ,
|
|
released_by VARCHAR(100),
|
|
actor VARCHAR(100) NOT NULL,
|
|
comment TEXT,
|
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
권장 인덱스:
|
|
|
|
- `(scope_type, candidate_mmsi)`
|
|
- `(group_key, sub_cluster_id, active_from DESC)`
|
|
- `(released_at, active_until)`
|
|
|
|
조회 규칙:
|
|
|
|
active exclusion은 아래 조건으로 판단한다.
|
|
|
|
```sql
|
|
released_at IS NULL
|
|
AND active_from <= NOW()
|
|
AND (active_until IS NULL OR active_until > NOW())
|
|
```
|
|
|
|
### 2. `gear_parent_label_sessions`
|
|
|
|
역할:
|
|
|
|
- 특정 그룹에 대한 정답 라벨 세션 저장
|
|
|
|
권장 컬럼:
|
|
|
|
```sql
|
|
CREATE TABLE kcg_lab.gear_parent_label_sessions (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
group_key VARCHAR(100) NOT NULL,
|
|
sub_cluster_id SMALLINT NOT NULL,
|
|
label_parent_mmsi VARCHAR(20) NOT NULL,
|
|
label_parent_name VARCHAR(200),
|
|
label_parent_vessel_id INT REFERENCES kcg_lab.fleet_vessels(id) ON DELETE SET NULL,
|
|
duration_days INT NOT NULL, -- 1 | 3 | 5
|
|
active_from TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
active_until TIMESTAMPTZ NOT NULL,
|
|
status VARCHAR(20) NOT NULL DEFAULT 'ACTIVE', -- ACTIVE | EXPIRED | CANCELLED
|
|
actor VARCHAR(100) NOT NULL,
|
|
comment TEXT,
|
|
anchor_snapshot_time TIMESTAMPTZ,
|
|
anchor_center_point geometry(Point, 4326),
|
|
anchor_member_mmsis JSONB NOT NULL DEFAULT '[]'::jsonb,
|
|
metadata JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
|
|
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
설명:
|
|
|
|
- `anchor_*` 컬럼은 기간형 라벨 동안 subcluster가 재편성될 가능성에 대비한 보조 식별자다.
|
|
- phase 1에서는 실제 매칭은 `group_key + sub_cluster_id`를 기본으로 쓰고, anchor 정보는 저장만 한다.
|
|
|
|
### 3. `gear_parent_label_tracking_cycles`
|
|
|
|
역할:
|
|
|
|
- 활성 정답 라벨 세션 동안 cycle별 자동 추론 결과 저장
|
|
- 향후 정확도 지표의 기준 데이터
|
|
|
|
권장 컬럼:
|
|
|
|
```sql
|
|
CREATE TABLE kcg_lab.gear_parent_label_tracking_cycles (
|
|
id BIGSERIAL PRIMARY KEY,
|
|
label_session_id BIGINT NOT NULL REFERENCES kcg_lab.gear_parent_label_sessions(id) ON DELETE CASCADE,
|
|
observed_at TIMESTAMPTZ NOT NULL,
|
|
candidate_snapshot_observed_at TIMESTAMPTZ,
|
|
auto_status VARCHAR(40),
|
|
top_candidate_mmsi VARCHAR(20),
|
|
top_candidate_name VARCHAR(200),
|
|
top_candidate_score DOUBLE PRECISION,
|
|
top_candidate_margin DOUBLE PRECISION,
|
|
candidate_count INT NOT NULL DEFAULT 0,
|
|
labeled_candidate_present BOOLEAN NOT NULL DEFAULT FALSE,
|
|
labeled_candidate_rank INT,
|
|
labeled_candidate_score DOUBLE PRECISION,
|
|
labeled_candidate_pre_bonus_score DOUBLE PRECISION,
|
|
labeled_candidate_margin_from_top DOUBLE PRECISION,
|
|
matched_top1 BOOLEAN NOT NULL DEFAULT FALSE,
|
|
matched_top3 BOOLEAN NOT NULL DEFAULT FALSE,
|
|
evidence_summary JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
설명:
|
|
|
|
- 전체 후보 상세는 기존 `gear_group_parent_candidate_snapshots`를 그대로 사용한다.
|
|
- 여기에는 지표 계산에 직접 필요한 값만 요약 저장한다.
|
|
|
|
### 4. 기존 `gear_group_parent_review_log` 재사용
|
|
|
|
새 action 이름 예시:
|
|
|
|
- `LABEL_PARENT`
|
|
- `EXCLUDE_GROUP`
|
|
- `EXCLUDE_GLOBAL`
|
|
- `RELEASE_EXCLUSION`
|
|
- `CANCEL_LABEL`
|
|
|
|
즉, 별도 audit table를 또 만들기보다 기존 review log를 action log로 재사용한다.
|
|
|
|
## prediction 변경 설계
|
|
|
|
### 적용 지점
|
|
|
|
핵심 변경 지점은 [gear_parent_inference.py](/Users/lht/work/devProjects/iran-airstrike-replay-codex/prediction/algorithms/gear_parent_inference.py), [fleet_tracker.py](/Users/lht/work/devProjects/iran-airstrike-replay-codex/prediction/fleet_tracker.py), [polygon_builder.py](/Users/lht/work/devProjects/iran-airstrike-replay-codex/prediction/algorithms/polygon_builder.py) 중 `gear_parent_inference.py`가 중심이다.
|
|
|
|
### 1. active exclusion load
|
|
|
|
cycle 시작 시 아래 두 집합을 읽는다.
|
|
|
|
- `global_excluded_mmsis`
|
|
- `group_excluded_mmsis[(group_key, sub_cluster_id)]`
|
|
|
|
적용 위치:
|
|
|
|
- `_build_candidate_scores()`에서 candidate union 이후, 실제 scoring 전에 hard filter
|
|
|
|
규칙:
|
|
|
|
- GLOBAL exclusion은 모든 그룹에 적용
|
|
- GROUP exclusion은 해당 그룹에만 적용
|
|
- exclusion된 후보는 candidate snapshot에도 남기지 않음
|
|
|
|
중요:
|
|
|
|
- raw correlation score는 그대로 계산/저장
|
|
- exclusion은 parent inference candidate set에서만 적용
|
|
|
|
### 2. active label session load
|
|
|
|
cycle 시작 시 현재 unresolved/active gear group에 매칭되는 active label session을 읽는다.
|
|
|
|
phase 1 매칭 기준:
|
|
|
|
- `group_key`
|
|
- `sub_cluster_id`
|
|
|
|
phase 2 보강 기준:
|
|
|
|
- member overlap
|
|
- center distance
|
|
- anchor snapshot similarity
|
|
|
|
### 3. tracking cycle write
|
|
|
|
각 그룹의 자동 추론이 끝난 뒤, active label session이 있으면 `gear_parent_label_tracking_cycles`에 1 row를 쓴다.
|
|
|
|
기록 항목:
|
|
|
|
- 현재 auto top-1 후보
|
|
- auto top-1 점수/격차
|
|
- 후보 수
|
|
- 라벨 대상 MMSI가 현재 후보군에 존재하는지
|
|
- 존재한다면 rank/score/pre-bonus score
|
|
- top1/top3 일치 여부
|
|
|
|
### 4. resolution 저장 원칙 변경
|
|
|
|
v2 이후 lab에서는 아래를 원칙으로 한다.
|
|
|
|
- 자동 resolution은 자동 추론만 반영
|
|
- 사람 라벨은 resolution을 덮어쓰지 않음
|
|
|
|
즉 아래 legacy 상태는 새로 만들지 않는다.
|
|
|
|
- `MANUAL_CONFIRMED`
|
|
- `MANUAL_REJECT`
|
|
|
|
기존 row는 읽기 전용으로 남겨둘 수 있지만, v2 새 액션은 이 상태를 만들지 않는다.
|
|
|
|
### 5. exclusion이 적용된 경우의 상태 전이
|
|
|
|
후보 pruning 이후:
|
|
|
|
- 후보가 남으면 기존 자동 상태 전이 사용
|
|
- top1이 제외되어 후보가 비면 `NO_CANDIDATE`
|
|
- top1이 제외되어 top2가 승격되면 새 top1 기준으로 `AUTO_PROMOTED / REVIEW_REQUIRED / UNRESOLVED` 재판정
|
|
|
|
## backend API 설계
|
|
|
|
### 1. 정답 라벨 세션 생성
|
|
|
|
`POST /api/vessel-analysis/groups/{groupKey}/parent-inference/{subClusterId}/label-session`
|
|
|
|
request:
|
|
|
|
```json
|
|
{
|
|
"selectedParentMmsi": "412333326",
|
|
"durationDays": 3,
|
|
"actor": "analyst-01",
|
|
"comment": "수동 확인"
|
|
}
|
|
```
|
|
|
|
response:
|
|
|
|
- 생성된 label session
|
|
- 현재 active label summary
|
|
|
|
### 2. 그룹 후보 제외 생성
|
|
|
|
`POST /api/vessel-analysis/groups/{groupKey}/parent-inference/{subClusterId}/candidate-exclusions`
|
|
|
|
request:
|
|
|
|
```json
|
|
{
|
|
"candidateMmsi": "412333326",
|
|
"scopeType": "GROUP",
|
|
"durationDays": 3,
|
|
"actor": "analyst-01",
|
|
"comment": "이 그룹에서는 오답"
|
|
}
|
|
```
|
|
|
|
### 3. 전역 후보 제외 생성
|
|
|
|
`POST /api/vessel-analysis/parent-inference/candidate-exclusions`
|
|
|
|
request:
|
|
|
|
```json
|
|
{
|
|
"candidateMmsi": "412333326",
|
|
"scopeType": "GLOBAL",
|
|
"actor": "analyst-01",
|
|
"comment": "모든 어구에서 모선 후보 대상 제외"
|
|
}
|
|
```
|
|
|
|
### 4. exclusion 해제
|
|
|
|
`POST /api/vessel-analysis/parent-inference/candidate-exclusions/{id}/release`
|
|
|
|
### 5. label session 종료
|
|
|
|
`POST /api/vessel-analysis/parent-inference/label-sessions/{id}/cancel`
|
|
|
|
### 6. active exclusion 조회
|
|
|
|
`GET /api/vessel-analysis/parent-inference/candidate-exclusions?status=ACTIVE&scopeType=GLOBAL`
|
|
|
|
용도:
|
|
|
|
- “대상 선박이 어느 어구에서 제외중인지” 목록 관리
|
|
- 운영자 관리 화면
|
|
|
|
### 7. active label tracking 조회
|
|
|
|
`GET /api/vessel-analysis/parent-inference/label-sessions?status=ACTIVE`
|
|
|
|
`GET /api/vessel-analysis/parent-inference/label-sessions/{id}/tracking`
|
|
|
|
### 8. 기존 review/detail API 확장
|
|
|
|
기존 `GroupParentInferenceDto`에 아래 요약을 추가한다.
|
|
|
|
- `activeLabelSession`
|
|
- `groupExclusionCount`
|
|
- `hasGlobalExclusionCandidate`
|
|
- `availableActions`
|
|
|
|
`ParentInferenceCandidateDto`에는 아래를 추가한다.
|
|
|
|
- `isExcludedInGroup`
|
|
- `isExcludedGlobally`
|
|
- `activeExclusionIds`
|
|
|
|
## 프론트엔드 설계
|
|
|
|
### 버튼 재구성
|
|
|
|
현재:
|
|
|
|
- `확정`
|
|
- `24시간 제외`
|
|
|
|
v2:
|
|
|
|
- `정답 라벨`
|
|
- `이 그룹에서 제외`
|
|
- `전체 후보 제외`
|
|
- `해제`
|
|
|
|
### 기간 선택
|
|
|
|
`정답 라벨`과 `이 그룹에서 제외`는 버튼 클릭 후 아래 중 하나를 고르게 한다.
|
|
|
|
- `1일`
|
|
- `3일`
|
|
- `5일`
|
|
|
|
### 우측 모선 검토 패널 변화
|
|
|
|
- 후보 카드 상단 action area를 아래처럼 재구성
|
|
- `정답 라벨`
|
|
- `이 그룹에서 제외`
|
|
- `전체 후보 제외`
|
|
- 현재 후보에 active exclusion이 있으면 badge 표시
|
|
- `이 그룹 제외 중`
|
|
- `전체 후보 제외 중`
|
|
- 현재 그룹에 active label session이 있으면 summary box 표시
|
|
- 라벨 MMSI
|
|
- 남은 기간
|
|
- 최근 top1 일치율
|
|
|
|
### 새 목록
|
|
|
|
- `검토 대기`
|
|
- active label session이 없는 그룹만 기본 표시
|
|
- `라벨 추적`
|
|
- active label session이 있는 그룹
|
|
- `제외 대상 관리`
|
|
- active group/global exclusions
|
|
|
|
### 지도 표시 원칙
|
|
|
|
- active label session 그룹은 기본 review 색과 다른 badge 색을 사용
|
|
- globally excluded candidate는 raw correlation 패널에서는 참고로 보일 수 있지만, parent-review actionable candidate 목록에서는 숨김
|
|
|
|
## 지표 설계
|
|
|
|
정답 라벨 세션을 기반으로 최소 아래 지표를 계산한다.
|
|
|
|
### 핵심 지표
|
|
|
|
- top1 exact match rate
|
|
- top3 hit rate
|
|
- labeled candidate mean rank
|
|
- labeled candidate mean score
|
|
- time-to-first-top1
|
|
- session duration 동안 top1 일치 지속률
|
|
|
|
### 보정/실험 지표
|
|
|
|
- `412/413` 가산점 적용 전후 top1/top3 uplift
|
|
- pre-bonus score 대비 final score uplift
|
|
- global exclusion 적용 전후 오탐 감소량
|
|
- group exclusion 이후 대체 top1 품질 변화
|
|
|
|
### 운영 준비 지표
|
|
|
|
- auto-promoted 후보 중 라벨과 일치하는 비율
|
|
- high-confidence (`>= 0.72`) 구간 calibration
|
|
- label session 종료 시점 기준 `실무 참고 가능` threshold
|
|
|
|
## 단계별 구현 순서
|
|
|
|
### Phase 1. DB/Backend 계약
|
|
|
|
- 마이그레이션 추가
|
|
- `gear_parent_candidate_exclusions`
|
|
- `gear_parent_label_sessions`
|
|
- `gear_parent_label_tracking_cycles`
|
|
- backend DTO/API 추가
|
|
- 기존 `CONFIRM/REJECT/RESET`는 lab UI에서 숨기고 legacy로만 남김
|
|
|
|
### Phase 2. prediction 연동
|
|
|
|
- active exclusion load
|
|
- candidate pruning
|
|
- active label session load
|
|
- tracking cycle write
|
|
|
|
### Phase 3. 프론트 UI 전환
|
|
|
|
- 버튼 재구성
|
|
- 기간 선택 UI
|
|
- 라벨 추적 목록
|
|
- 제외 대상 관리 화면
|
|
|
|
### Phase 4. 지표와 리포트
|
|
|
|
- label session summary endpoint
|
|
- exclusion usage summary endpoint
|
|
- 실험 리포트 화면 또는 문서 산출
|
|
|
|
## 마이그레이션 전략
|
|
|
|
### 기존 v1 상태 처리
|
|
|
|
- `MANUAL_CONFIRMED`, `MANUAL_REJECT`는 새로 생성하지 않는다.
|
|
- 기존 row는 history로 남긴다.
|
|
- 필요하면 one-time migration으로 legacy `MANUAL_CONFIRMED`를 `expired label session`으로 변환할 수 있다.
|
|
|
|
### 운영 영향 제한
|
|
|
|
- v2는 우선 `kcg_lab`에만 적용
|
|
- 운영 `kcg` 반영 전에는 사람이 직접 누르는 흐름과 tracking 지표가 충분히 쌓여야 함
|
|
|
|
## 수용 기준
|
|
|
|
### 기능 기준
|
|
|
|
- 그룹 제외가 다음 cycle부터 해당 그룹에서만 적용된다.
|
|
- 전역 후보 제외가 다음 cycle부터 모든 그룹에 적용된다.
|
|
- active exclusion list가 DB/API/UI에서 동일하게 보인다.
|
|
- 정답 라벨 세션 동안 cycle별 tracking row가 누락 없이 쌓인다.
|
|
|
|
### 데이터 기준
|
|
|
|
- label session당 최소 아래 값이 저장된다.
|
|
- top1 후보
|
|
- labeled candidate rank
|
|
- labeled candidate score
|
|
- candidate count
|
|
- observed_at
|
|
- exclusion row에는 scope, duration, actor, comment, active 기간이 남는다.
|
|
|
|
### 평가 기준
|
|
|
|
- `412/413` 가산점, threshold, exclusion 정책 변경 전후를 label session 데이터로 비교 가능해야 한다.
|
|
- 일정 기간 후 “자동 top1을 운영 참고값으로 써도 되는지”를 정량으로 판단할 수 있어야 한다.
|
|
|
|
## 열린 이슈
|
|
|
|
### 1. 그룹 스코프 안정성
|
|
|
|
`group_key + sub_cluster_id`가 며칠 동안 완전히 안정적인지 추가 확인이 필요하다.
|
|
|
|
현재 권장:
|
|
|
|
- phase 1은 기존 키를 그대로 사용
|
|
- 대신 `anchor_snapshot_time`, `anchor_center_point`, `anchor_member_mmsis`를 저장
|
|
|
|
### 2. 전역 후보 제외의 기간 정책
|
|
|
|
현재 제안은 “수동 해제 전까지 유지”다.
|
|
|
|
이유:
|
|
|
|
- 전역 제외는 단기 오답보다 “이 AIS는 parent candidate class가 아님”에 가깝다.
|
|
|
|
필요 시 추후 `1/3/5일` 옵션을 추가할 수 있다.
|
|
|
|
### 3. raw correlation UI 노출
|
|
|
|
전역 제외된 후보를 모델 패널에서 완전히 숨길지, `참고 제외` badge만 붙여 남길지는 사용성 확인이 필요하다.
|
|
|
|
현재 권장은 아래다.
|
|
|
|
- parent-review actionable 후보 목록에서는 숨김
|
|
- raw model/correlation 참고 패널에서는 badge와 함께 유지
|
|
|
|
## 권장 결론
|
|
|
|
v2의 핵심은 `사람 판단을 자동 추론의 override가 아니라 평가 데이터로 축적하는 것`이다.
|
|
|
|
따라서 다음 구현 우선순위는 아래가 맞다.
|
|
|
|
1. exclusion/label DB 추가
|
|
2. prediction candidate gating + tracking write
|
|
3. UI 액션 재정의
|
|
4. 지표 산출
|
|
|
|
그 다음 단계에서만 threshold 자동화, 가산점 조정, LLM 연결을 검토하는 것이 안전하다.
|