# Intent 리뷰 큐 조회/라벨링 API 구현 **작성일**: 2025-11-16 **작성자**: admin **관련 문서**: [390_human_in_the_loop_intent_learning.md](../300_architecture/390_human_in_the_loop_intent_learning.md) --- ## 1. 목적 관리자가 `IntentReviewQueue`의 `status='pending'` 항목을 조회하고, `true_intent`를 라벨링하여 `status='confirmed'` 또는 `status='corrected'`로 변경할 수 있는 REST API를 구현한다. **기대 효과**: 사용자 피드백과 에러 케이스를 수집한 리뷰 큐를 관리자가 라벨링하여, 이후 재학습 배치 스크립트에서 활용할 수 있게 한다. --- ## 2. 관리자 시나리오 ### 2.1 리뷰 큐 조회 1. 관리자가 "오늘 처리해야 할 리뷰 항목"을 조회 2. `status='pending'` 필터링 3. 페이지네이션 지원 (기본 20개, 최대 100개) 4. 정렬: `created_at DESC` (최신순) ### 2.2 라벨링 1. 관리자가 특정 항목의 `message`, `predicted_intent`, `alternatives`를 확인 2. 올바른 의도를 판단하여 `true_intent` 입력 3. `status` 업데이트: - `predicted_intent == true_intent` → `status='confirmed'` - `predicted_intent != true_intent` → `status='corrected'` ### 2.3 통계 조회 - 전체 리뷰 큐 수 - `status`별 집계 (pending/confirmed/corrected) - 의도별 분포 --- ## 3. API 스펙 ### 3.1 GET /api/intent-review/queue **목적**: 리뷰 큐 조회 (필터링/페이지네이션) **Query Parameters**: - `status` (optional): `pending` | `confirmed` | `corrected` (기본값: `pending`) - `page` (optional): 페이지 번호 (기본값: 1) - `limit` (optional): 페이지 크기 (기본값: 20, 최대: 100) - `intent` (optional): 의도 필터 (예: `calendar_query`) **Response**: ```json { "items": [ { "id": 1, "conversation_log_id": 123, "user_id": "00000000-0000-0000-0000-000000000001", "message": "오늘 일정 정리해줘", "predicted_intent": "calendar_event", "predicted_confidence": 0.55, "alternatives": [ {"intent": "calendar_query", "score": 0.52} ], "user_feedback": "down", "error": false, "status": "pending", "true_intent": null, "created_at": "2025-11-16T07:00:00Z", "updated_at": "2025-11-16T07:00:00Z" } ], "total": 42, "page": 1, "limit": 20, "total_pages": 3 } ``` ### 3.2 PUT /api/intent-review/{id}/label **목적**: 리뷰 항목 라벨링 **Request Body**: ```json { "true_intent": "calendar_query" } ``` **Response**: ```json { "success": true, "id": 1, "status": "corrected", "true_intent": "calendar_query", "updated_at": "2025-11-16T08:00:00Z" } ``` **로직**: - `predicted_intent == true_intent` → `status='confirmed'` - `predicted_intent != true_intent` → `status='corrected'` - `updated_at` 자동 갱신 ### 3.3 GET /api/intent-review/stats **목적**: 리뷰 큐 통계 **Response**: ```json { "total": 100, "by_status": { "pending": 42, "confirmed": 35, "corrected": 23 }, "by_intent": { "calendar_query": 15, "calendar_event": 12, "email_query": 8 } } ``` --- ## 4. 구현 계획 ### 4.1 코드 구조 (원칙 준수) ``` app/ ├── models/ │ └── intent_review_model.py # IntentReviewQueue 모델 (state/database.py에서 이동) ├── state/ │ └── intent_review_repository.py # CRUD 캡슐화 (신규) └── router/ └── intent_review_endpoint.py # API 엔드포인트 (신규) ``` ### 4.2 작업 순서 1. **TDD: 테스트 작성** (`tests/test_intent_review_api.py`) - 리뷰 큐 조회/필터링/페이지네이션 - 라벨링 (confirmed/corrected 상태 전이) - 통계 집계 2. **모델 분리** (`app/models/intent_review_model.py`) - `app/state/database.py`의 `IntentReviewQueue` 이동 - Base import 경로 정리 3. **Repository 생성** (`app/state/intent_review_repository.py`) - `get_review_queue(status, page, limit, intent_filter)` - `label_review_item(id, true_intent)` - `get_review_stats()` 4. **DB 테이블 생성** - `create_tables()` 호출 또는 마이그레이션 스크립트 - 기존 테이블 존재 여부 확인 로직 5. **API 엔드포인트 구현** (`app/router/intent_review_endpoint.py`) - FastAPI router 정의 - Pydantic 스키마 (Request/Response) - JWT 인증 (관리자 권한 검증 필요 시) 6. **기존 코드 리팩토링** - `app/state/conversation_repository.py`: 새 repository 사용 - `app/router/feedback_handler.py`: 새 repository 사용 --- ## 5. 인증/권한 **현재**: JWT 기반 `get_current_user` 사용 **향후**: 관리자 권한 체크 추가 고려 (현재는 모든 인증된 사용자 허용) --- ## 6. 체크리스트 - [x] 테스트 작성 완료 - [x] 모델 분리 완료 (`app/models/intent_review_model.py`) - [x] Repository 구현 완료 (`app/state/intent_review_repository.py`) - [x] DB 테이블 생성 확인 (`scripts/create_intent_review_queue_table.py` 실행 완료) - [x] API 엔드포인트 구현 완료 (`app/router/intent_review_endpoint.py`) - [x] 기존 코드 리팩토링 완료 (`conversation_repository`, `feedback_handler`) - [x] 통합 테스트 통과 (Repository 테스트 6개 통과) - [x] 실제 API 테스트 통과 (통계/조회/라벨링 모두 성공) --- ## 7. 실제 API 테스트 결과 **테스트 일시**: 2025-11-17 **테스트 환경**: rb8001 컨테이너 (로컬) ### 테스트 결과 1. **GET /api/intent-review/stats** ✅ ```json { "total": 2, "by_status": {"pending": 1, "confirmed": 0, "corrected": 1}, "by_intent": {"calendar_event": 2} } ``` 2. **GET /api/intent-review/queue?status=pending** ✅ - 페이지네이션 정상 작동 - 필터링 정상 작동 - JSON 응답 형식 정확 3. **PUT /api/intent-review/{id}/label** ✅ ```json { "success": true, "id": 2, "status": "corrected", "true_intent": "calendar_query", "updated_at": "2025-11-17T02:26:05.148402" } ``` - `predicted_intent != true_intent` → `status='corrected'` 정상 작동 ### Repository 테스트 결과 - `test_get_review_queue_pending_only`: ✅ 통과 - `test_get_review_queue_pagination`: ✅ 통과 - `test_get_review_queue_intent_filter`: ✅ 통과 - `test_label_review_item_confirmed`: ✅ 통과 - `test_label_review_item_corrected`: ✅ 통과 - `test_get_review_stats`: ✅ 통과 ## 8. 교훈 - TDD로 관리자 시나리오 전체를 안전하게 검증 - 모델/리포지토리 분리로 책임 명확화 및 확장성 확보 - DB 테이블 생성/마이그레이션을 함께 처리하여 배포 시 "테이블 없음" 오류 방지 - 코드 작성 원칙 준수: 모델은 `app/models/`, Repository는 `app/state/`로 분리