docs: Intent 리뷰 큐 API 구현 완료 및 테스트 결과 반영
- 251116_admin_intent_review_queue_api_implementation.md: 실제 API 테스트 결과 추가 - 390_human_in_the_loop_intent_learning.md: 체크리스트 업데이트 (라벨링 API 완료 표시)
This commit is contained in:
parent
d12143a78f
commit
b65881f835
@ -145,6 +145,10 @@
|
||||
- [x] 리뷰 큐 규칙이 테스트(`test_intent_review_queue.py`)로 고정되어 있는가
|
||||
- [x] 프론트/슬랙에서 피드백을 1~2클릭으로 보낼 수 있는가
|
||||
- [x] 피드백 API가 실패해도 사용자 경험이 깨지지 않는가
|
||||
- [ ] 라벨링/재시드용 뷰·배치 스크립트가 준비되어 있는가
|
||||
- [x] 라벨링/조회용 API 엔드포인트가 준비되어 있는가 (2025-11-17 완료)
|
||||
- `GET /api/intent-review/queue`: 리뷰 큐 조회 (필터링/페이지네이션)
|
||||
- `PUT /api/intent-review/{id}/label`: 리뷰 항목 라벨링
|
||||
- `GET /api/intent-review/stats`: 리뷰 큐 통계
|
||||
- [ ] 재시드 배치 스크립트가 준비되어 있는가 (다음 단계)
|
||||
- [ ] 운영에서 리뷰 큐를 모니터링할 대시보드/쿼리가 있는가
|
||||
|
||||
|
||||
@ -0,0 +1,237 @@
|
||||
# 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/`로 분리
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user