docs: describe human-in-the-loop intent review and feedback flow
This commit is contained in:
parent
cc7baf86ae
commit
a618c13c15
150
300_architecture/390_human_in_the_loop_intent_learning.md
Normal file
150
300_architecture/390_human_in_the_loop_intent_learning.md
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
# Human-in-the-loop 의도 학습/리뷰 큐 아키텍처
|
||||||
|
|
||||||
|
**작성일**: 2025-11-16
|
||||||
|
**수정일**: 2025-11-16 (rb8001 intent 리뷰 큐/피드백 1차 구현 정리)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. 목표
|
||||||
|
|
||||||
|
1. rb8001에서 **모든 의도 분석 결과를 구조화된 로그로 남기고**, 나중에 재학습/분석에 활용할 수 있게 한다.
|
||||||
|
2. 모델이 헷갈리는 케이스·에러·사용자 부정 피드백만 골라 **리뷰 큐(intent_review_queue)** 로 보내서 human-in-the-loop 라벨링을 가능하게 한다.
|
||||||
|
3. 프론트/슬랙에서 최소 클릭(좋아요/싫어요)으로 피드백을 수집하고, 이를 **의도 로그/리뷰 큐와 자연스럽게 연결**한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. 데이터 흐름 개요
|
||||||
|
|
||||||
|
### 2.1 대화 저장 (ConversationLog)
|
||||||
|
|
||||||
|
- 책임 파일:
|
||||||
|
- `app/state/database.py:44-58: ConversationLog`
|
||||||
|
- `app/state/conversation_repository.py:16-159: save_conversation`
|
||||||
|
|
||||||
|
1. `router.route_message()`가 실행계획을 만들고 스킬을 실행한다.
|
||||||
|
2. LLM 응답(또는 스킬 응답)이 성공하면 `_save_conversation()` → `save_conversation()` 호출.
|
||||||
|
3. `ConversationLog`에 다음 필드 저장:
|
||||||
|
- `user_id` (UUID, FK to user)
|
||||||
|
- `channel_id` (frontend/slack 등)
|
||||||
|
- `message` (사용자 발화)
|
||||||
|
- `response` (로빙 응답)
|
||||||
|
- `intent` (DecisionEngine이 판단한 의도 이름)
|
||||||
|
- `confidence` (의도 신뢰도)
|
||||||
|
- `timestamp`
|
||||||
|
|
||||||
|
> **원칙**: 의도/신뢰도는 항상 ConversationLog에 남긴다. 리뷰 큐/재학습은 이 로그를 기반으로 한다.
|
||||||
|
|
||||||
|
### 2.2 Intent 리뷰 큐 (IntentReviewQueue)
|
||||||
|
|
||||||
|
- 책임 파일:
|
||||||
|
- `app/state/database.py:60-88: IntentReviewQueue`
|
||||||
|
- `app/brain/intent_review.py:16-82: should_enqueue_for_review`
|
||||||
|
- `app/state/conversation_repository.py:153-188: 리뷰 큐 저장 로직`
|
||||||
|
|
||||||
|
1. `save_conversation()`는 PostgreSQL 저장이 성공하고 `conversation_id`를 얻은 뒤, 의도가 있으면 `should_enqueue_for_review(...)`를 호출한다.
|
||||||
|
2. `should_enqueue_for_review` 규칙:
|
||||||
|
- `error=True` → 무조건 리뷰 큐 진입
|
||||||
|
- `user_feedback`가 부정(wrong/이상해/다시 등) → 무조건 리뷰 큐 진입
|
||||||
|
- 그 외에는 **top-alternatives와의 마진이 작은 케이스**만 리뷰 대상
|
||||||
|
- `margin = predicted_conf - best_alt_score < 0.1`
|
||||||
|
- `predicted_conf >= 0.3` 인 경우에만 고려
|
||||||
|
3. 리뷰 큐에 들어가는 필드:
|
||||||
|
- `conversation_log_id` (nullable, 히스토리와 연결 가능할 때만)
|
||||||
|
- `user_id`
|
||||||
|
- `message`
|
||||||
|
- `predicted_intent`, `predicted_confidence`
|
||||||
|
- `alternatives` (JSON, intent/score 목록)
|
||||||
|
- `user_feedback` (up/down/wrong 등)
|
||||||
|
- `error` (bool)
|
||||||
|
- `status` (`pending`/`confirmed`/`corrected`)
|
||||||
|
- `true_intent` (라벨링 완료 후 사람이 채운 값)
|
||||||
|
|
||||||
|
> **원칙**:
|
||||||
|
> - 리뷰 큐는 **“라벨링 대상 샘플”만 모아 두는 얇은 테이블**이다.
|
||||||
|
> - 실제 원문/컨텍스트는 항상 ConversationLog/ChromaDB에서 조회한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. 프론트/슬랙 피드백 흐름
|
||||||
|
|
||||||
|
### 3.1 프론트엔드 피드백 UI
|
||||||
|
|
||||||
|
- 책임 파일:
|
||||||
|
- `frontend-customer/src/components/chat-interface.tsx:646-680, 488-507, 510-751`
|
||||||
|
- `frontend-customer/src/services/robeing-api.ts:4-73, 100-154`
|
||||||
|
|
||||||
|
1. 각 로빙 응답 말풍선 아래 타임스탬프 옆에 **좋아요/싫어요 버튼** 추가:
|
||||||
|
- 클릭 시 `Message.metadata.feedback = 'up' | 'down'`로 낙관적 UI 업데이트.
|
||||||
|
2. 동시에 `sendChatFeedback(message.id, 'up' | 'down')` 호출:
|
||||||
|
- `POST { message_id, feedback }` → `gateway/api/feedback/chat`
|
||||||
|
- 게이트웨이는 rb8001의 `/api/feedback/chat`으로 프록시.
|
||||||
|
|
||||||
|
### 3.2 rb8001 피드백 API → 리뷰 큐
|
||||||
|
|
||||||
|
- 책임 파일:
|
||||||
|
- `rb8001/main.py:66-72, 189-149, 205-236, 522-531, 627-701, 366-404, 532-624, 636-701, 704-708` (중 `/api/feedback/chat`)
|
||||||
|
- `app/router/feedback_handler.py:16-118`
|
||||||
|
|
||||||
|
1. 엔드포인트: `/api/feedback/chat`
|
||||||
|
- Request Body: `{"message_id": "...", "feedback": "up" | "down"}`
|
||||||
|
- `get_current_user`로 JWT에서 **UUID** 추출.
|
||||||
|
2. `handle_chat_feedback(user_id, message_id, feedback)` 호출:
|
||||||
|
- `message_id`에서 `conversation_log_id`를 파싱 (`"123_robeing" → 123`)
|
||||||
|
- `conversation_log_id`로 `ConversationLog`를 찾아서 `message`, `intent`, `confidence`를 가져옴(있으면).
|
||||||
|
- 해당 `(conversation_log_id, user_id)` 조합에 대해 리뷰 큐 행을 생성/업데이트:
|
||||||
|
- `feedback='up'` → `status='confirmed'`
|
||||||
|
- `feedback='down'` → `status='corrected'`
|
||||||
|
3. ConversationLog가 없는 경우(예: 임시 ID)에는 `conversation_log_id=None`으로 리뷰 큐에 최소 정보만 남긴다.
|
||||||
|
|
||||||
|
> **원칙**: 피드백 API는 실패하더라도 사용자 경험을 깨지 않기 위해 항상 200을 반환하며, 내부 에러는 로그로만 남긴다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. 의도/리뷰 TDD 상태
|
||||||
|
|
||||||
|
### 4.1 Intent 리뷰 규칙 테스트
|
||||||
|
|
||||||
|
- 책임 파일:
|
||||||
|
- `rb8001/tests/test_intent_review_queue.py`
|
||||||
|
|
||||||
|
검증 내용:
|
||||||
|
- 캘린더 쿼리 vs 이벤트처럼 **마진이 작은 케이스**는 리뷰 큐로 가야 한다.
|
||||||
|
- high-confidence 명확 케이스는 리뷰 큐에 안 들어간다.
|
||||||
|
- 사용자 부정 피드백(wrong 등)이나 내부 에러가 있으면, confidence와 무관하게 리뷰 큐로 들어가야 한다.
|
||||||
|
|
||||||
|
### 4.2 종합 의도/캘린더 테스트
|
||||||
|
|
||||||
|
- 책임 파일:
|
||||||
|
- `rb8001/tests/test_intent_entity_skill_comprehensive.py`
|
||||||
|
|
||||||
|
검증 내용:
|
||||||
|
- 이메일/뉴스/문서/웹검색/액션/인사/캘린더(등록/조회/승인/삭제)까지 전체 intent 분류.
|
||||||
|
- 캘린더 조회/등록 문장들은 `calendar_event` vs `calendar_query`로 명시적으로 분리.
|
||||||
|
- 캘린더 후속 질문 `"어디서?"`에 대해 최근 `calendar_query` 응답에서 장소를 추출해 답변.
|
||||||
|
|
||||||
|
> **교훈**: 의도/캘린더 관련 코드는 “문장 → 기대 intent”를 먼저 테스트에 못 박고, DecisionEngine/threshold를 그에 맞춰 조정하는 TDD 패턴을 유지한다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. 향후 확장 포인트
|
||||||
|
|
||||||
|
1. **리뷰 큐 라벨링/조회 API**
|
||||||
|
- `status='pending'` 행만 필터링하는 관리자용 엔드포인트 추가.
|
||||||
|
- 사람이 `true_intent`를 입력하고 `status='confirmed'/'corrected'`로 변경하는 라벨링 플로우 구축.
|
||||||
|
2. **재학습/재시드 배치 연동**
|
||||||
|
- `IntentReviewQueue`에서 검수 완료된 샘플만 모아서 Ko-SRoBERTa prototype/Naive Bayes 시드 스크립트 입력으로 사용.
|
||||||
|
- “신규 검수 샘플 N개 + 일정 주기” 조건에서만 재시드/배포하도록 가드 추가.
|
||||||
|
3. **스킬 실행 로그와의 통합**
|
||||||
|
- `log_decision`/intent_runtime 로그에 **실행된 스킬 목록**을 포함하고, 리뷰 큐에서 이 정보를 함께 조회할 수 있게 하면 “어떤 스킬 조합이 실패/성공했는지”를 분석하기 쉬워진다.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. 체크리스트
|
||||||
|
|
||||||
|
- [x] ConversationLog에 `intent`/`confidence`를 항상 저장하는가
|
||||||
|
- [x] 리뷰 큐 규칙이 테스트(`test_intent_review_queue.py`)로 고정되어 있는가
|
||||||
|
- [x] 프론트/슬랙에서 피드백을 1~2클릭으로 보낼 수 있는가
|
||||||
|
- [x] 피드백 API가 실패해도 사용자 경험이 깨지지 않는가
|
||||||
|
- [ ] 라벨링/재시드용 뷰·배치 스크립트가 준비되어 있는가
|
||||||
|
- [ ] 운영에서 리뷰 큐를 모니터링할 대시보드/쿼리가 있는가
|
||||||
|
|
||||||
Loading…
x
Reference in New Issue
Block a user