226 lines
9.5 KiB
Markdown
226 lines
9.5 KiB
Markdown
# 감정 시스템 구현 문서 (rb8001)
|
|
|
|
**작성일**: 2025-10-02
|
|
**작성자**: Claude & happybell80
|
|
**목적**: rb8001의 균등분포 감정 분석을 실제 모델 추론 + 시계열 저장/집계로 전환
|
|
|
|
## 사용자 관점 시나리오
|
|
|
|
### 시나리오 1: 감정 기반 대화 조절
|
|
- **상황**: 사용자가 "프로젝트 실패해서 너무 짜증나" 입력
|
|
- **동작**: 로빙이 분노/좌절 감지 → 공감적이고 차분한 톤으로 응답 조절
|
|
- **효과**: 평소보다 조심스럽게 대안 제시, 비판적 피드백 자제
|
|
- **구현 요구사항**: 감정 분석 API + LLM 프롬프트 동적 조절
|
|
|
|
### 시나리오 2: 장기 감정 패턴 리포트
|
|
- **상황**: 매주 금요일 자동 리포트
|
|
- **동작**: "이번 주 당신의 감정 변화" 시각화 제공
|
|
- **예시**: "월요일 불안 70% → 수요일 기쁨 60% → 금요일 만족 80%"
|
|
- **효과**: 번아웃 예방, 정신건강 관리 지원
|
|
- **구현 요구사항**: TimescaleDB 시계열 저장 + time_bucket 집계 쿼리
|
|
|
|
### 시나리오 3: 팀 감정 온도계
|
|
- **상황**: Slack 채널 전체 대화 모니터링
|
|
- **동작**: 실시간 "팀 분위기 지표" 계산 및 제공
|
|
- **효과**: 부정 감정 임계치 초과 시 매니저 알림, 회의 전 팀 감정 브리핑
|
|
- **구현 요구사항**: 채널별 감정 집계 + 임계치 알림 시스템
|
|
|
|
### 시나리오별 요구사항
|
|
- **시나리오 1**: 문장 단건 추론 API, LLM 톤 조절 규칙 (rb8001/app/llm/llm_service.py:119)
|
|
- **시나리오 2**: 사용자별 시계열 저장, time_bucket 집계, 요약 API
|
|
- **시나리오 3**: 채널/워크스페이스 집계, 임계치 알림, 관리자 요약 API
|
|
|
|
## 현재 상황
|
|
|
|
### 코드 위치 (정확)
|
|
- **균등 분포 반환**: rb8001/app/core/emotion/base.py:46, 51
|
|
- **감정 분석 호출**: rb8001/app/llm/emotion_llm.py:25, 56
|
|
- **LLM 서비스 호출**: rb8001/app/llm/llm_service.py:119
|
|
- **감정 API**: 없음 (rb8001/main.py 확인 완료)
|
|
- **emotion_readings 테이블**: 없음
|
|
|
|
### 서버 확인 결과 (2025-10-02)
|
|
|
|
#### 51124 서버 확인 사항
|
|
```bash
|
|
# 확인 필요
|
|
docker exec rb8001 ls -la /code/app/core/emotion/ # emotion 파일 목록
|
|
docker exec rb8001 cat /code/app/core/emotion/emotion_llm.py # 구현 코드
|
|
docker exec rb8001 ls -la /code/onnx_models/ # ONNX 모델 존재 여부
|
|
docker logs rb8001 --tail 1000 | grep -i emotion # 감정 로그
|
|
curl http://localhost:8001/v1/emotion/infer -X POST -d '{"text":"테스트"}' # API 테스트
|
|
|
|
# 전체 서비스 확인
|
|
for container in rb8001 skill-embedding robeing_monitor skill-rag-file; do
|
|
echo "=== $container ==="
|
|
docker exec $container find /code -name "*emotion*" 2>/dev/null
|
|
done
|
|
```
|
|
|
|
#### 51123 서버 확인 결과
|
|
- **PostgreSQL**: emotion_readings 테이블 없음
|
|
- **모델 파일**: /opt/models에 감정 모델 없음 (sentence-transformer만 존재)
|
|
- **API 엔드포인트**: /v1/emotion/* 엔드포인트 없음 ("Endpoint not found")
|
|
- **Gateway 서비스**: robeing-gateway 실행 중 (포트 8100)
|
|
- **결론**: 51123에 감정 시스템 관련 인프라 전혀 구현되지 않음
|
|
|
|
### 기존 코드 파일
|
|
- rb8001/app/core/emotion/base.py (EmotionState, 엔트로피, ThompsonSampler)
|
|
- rb8001/app/core/emotion/bayesian.py (베이지안 학습)
|
|
- rb8001/app/core/emotion/storage.py (사용자별 파라미터)
|
|
- rb8001/app/core/emotion/monitoring.py (감정 모니터링)
|
|
- rb8001/app/llm/emotion_llm.py (analyze_user_emotion, generate_response_with_emotion)
|
|
|
|
### 미구현 항목 (2025-10-02 업데이트)
|
|
- ✅ rb8001/app/core/emotion/emotion_classifier.py (skill-embedding 호출로 구현)
|
|
- ✅ ONNX 모델은 skill-embedding이 처리 (rb8001 마운트 불필요)
|
|
- ✅ emotion_readings 테이블 (51123 서버에서 생성 완료)
|
|
- ✅ /v1/emotion/* API 엔드포인트 (구현 완료)
|
|
|
|
### ONNX 모델 현황
|
|
- **모델 위치** (51124 서버): /home/admin/ivada_project/onnx_models/aihub-7emotions/
|
|
- **모델 파일**: model.onnx (442MB), BERT 기반 7클래스 감정 분류
|
|
- **레이블**: fear, surprise, anger, sadness, neutral, happiness, disgust
|
|
- **토크나이저**: vocab.txt, tokenizer.json 포함
|
|
- **학습 코드** (51124): /home/admin/ivada_project/training_emotion/train_korean_emotion.py
|
|
- **문제**: rb8001 컨테이너에 볼륨 마운트 안 됨
|
|
|
|
## 데이터 모델 설계
|
|
|
|
### emotion_readings 테이블
|
|
필수 컬럼:
|
|
- user_id (UUID) - 사용자 감정
|
|
- company_id (UUID) - 회사별 집계용
|
|
- robeing_id (VARCHAR) - 로빙의 감정 상태 (예: 'rb8001')
|
|
- emotion_type (VARCHAR) - 'user' | 'robeing'
|
|
- created_at (TIMESTAMPTZ)
|
|
- probs (JSONB)
|
|
- entropy (FLOAT)
|
|
- top_label (TEXT)
|
|
- top_p (FLOAT)
|
|
- model_version (TEXT)
|
|
- meta (JSONB)
|
|
- text_hash (VARCHAR) - 원문 미저장, 해시만
|
|
|
|
저장 위치:
|
|
- robeing_metrics DB (별도 DB 권장)
|
|
- TimescaleDB 하이퍼테이블 적용
|
|
- 51123 서버에서 CREATE TABLE 및 create_hypertable() 직접 실행
|
|
- 인덱스: (user_id, created_at), (company_id, created_at), (robeing_id, created_at)
|
|
|
|
### TimescaleDB 참조
|
|
- 설치/활성화: DOCS/troubleshooting/250714_system_metrics_implementation.md
|
|
- time_bucket 쿼리: frontend-base/backend/metrics_database.py:103
|
|
- asyncpg interval 이슈: DOCS/troubleshooting/250715_metrics_graph_timebucket_error.md
|
|
|
|
## API 스펙
|
|
|
|
### POST /v1/emotion/infer
|
|
- 입력: text, user_id
|
|
- 출력: {probs, entropy, top_label, top_p, model_version}
|
|
- 저장: emotion_readings 삽입
|
|
|
|
### GET /v1/emotion/timeseries
|
|
- 쿼리: user_id, start, end, bucket
|
|
- 출력: bucket별 집계 결과
|
|
|
|
### GET /v1/emotion/team-insight
|
|
- 쿼리: channel_id, workspace_id, 기간
|
|
- 출력: 팀 감정 지표
|
|
|
|
## 통합 포인트
|
|
|
|
### 감정 분석 삽입
|
|
- rb8001/app/llm/emotion_llm.py:25 - emotion_classifier.py 호출로 대체
|
|
- rb8001/app/core/emotion/base.py:46, 51 - 실제 분석 로직으로 교체
|
|
|
|
### LLM 톤 조절
|
|
- rb8001/app/llm/llm_service.py:133 - 주석 블록 재활성화
|
|
- 감정 기반 프롬프트 조절 로직 추가
|
|
|
|
### 저장 계층
|
|
- DB 접근: rb8001/app/state/database.py 활용 또는 신규 emotion DB 클라이언트 파일 생성
|
|
- robeing_metrics DB 연결 (51123 서버에서 CREATE TABLE 직접 실행)
|
|
- 비동기 저장 큐 고려
|
|
|
|
### 권한
|
|
- user_id는 JWT sub(UUID)
|
|
- robeing-gateway/app/main.py:23 검증 로직 존재
|
|
|
|
## 구현 참조 패턴
|
|
|
|
### 키워드 매칭 로직
|
|
- 정규식 기반 의도 분류: rb8001/app/brain/decision_engine.py:69, 116
|
|
- 키워드·정규식 혼합 파싱: rb8001/app/skills/email_integration.py:214, 226-239, 245-258
|
|
|
|
### DB 연결 패턴
|
|
- asyncpg 풀: frontend-base/backend/metrics_database.py:12, 23
|
|
- asyncpg 단건: robeing-monitor/app/api/monitor.py:139, 208
|
|
- asyncpg 단건: skill-email/services/naverworks_provider.py:38
|
|
- asyncpg 단건: rb8001/app/services/coldmail_filter.py:159, 183, 225
|
|
- psycopg2 동기: rb8001/app/skills/news_posting_skill.py:35-37
|
|
- SQLAlchemy Async: robeing-gateway/app/database.py:22-36
|
|
|
|
### API 라우트 패턴
|
|
- rb8001 메인 라우트: rb8001/main.py:344 (@app.post("/api/slack/events"))
|
|
- rb8001 스케줄 테스트: rb8001/main.py:356 (@app.post("/api/schedule/test-news"))
|
|
- 인증 의존성 주입: rb8001/app/auth.py:61 (get_current_user), 66-75 (JWT decode)
|
|
- 게이트웨이 검증: robeing-gateway/app/main.py:41 (get_verified_user), 228-232 (Authorization 포워딩)
|
|
|
|
### 시계열 집계 패턴
|
|
- TimescaleDB 설치: DOCS/troubleshooting/250714_system_metrics_implementation.md:18-29
|
|
- time_bucket 쿼리: frontend-base/backend/metrics_database.py:103
|
|
- asyncpg interval 이슈: DOCS/troubleshooting/250715_metrics_graph_timebucket_error.md:28, 57, 85, 112-113
|
|
|
|
### 데이터 모델 참조
|
|
- emotion_readings 스키마: DOCS/ideas/emotion_graph_implementation.md:215, 232
|
|
- emotion 스키마: DOCS/ideas/250916_로빙_감정_분석_시스템_구현_계획.md:11
|
|
- rb_news 테이블 예시: rb8001/scripts/create_rb_news_table.sql:8, 56, 77
|
|
|
|
### 팀/주간 집계 메타
|
|
- 채널 식별: rb8001/app/skills/news_posting_skill.py:214, 467
|
|
- JSONB 메타 패턴: DOCS/ideas/emotion_graph_implementation.md:235, 259
|
|
|
|
## 작업 순서
|
|
|
|
1. skill-embedding 서비스에 감정 분석 추가:
|
|
- emotion_service.py: ONNX 모델 로드 및 추론
|
|
- routers/emotion.py: API 엔드포인트 (/emotion)
|
|
- main.py: 라우터 include
|
|
2. rb8001에서 skill-embedding 호출:
|
|
- emotion_classifier.py: HTTP 클라이언트로 skill-embedding 호출
|
|
- 환경변수: SKILL_EMBEDDING_URL=http://localhost:8515
|
|
3. EmotionAwareLLM 연결: rb8001/app/llm/emotion_llm.py:25
|
|
4. LLMService 톤 조절: rb8001/app/llm/llm_service.py:133
|
|
5. DB 스키마 준비: 51123 서버에서 robeing_metrics DB에 emotion_readings 테이블 생성
|
|
6. DB 클라이언트: rb8001/app/state/database.py 활용 또는 신규 파일 생성
|
|
7. API 라우터 생성: rb8001/app/router/emotion_endpoint.py 생성 후 main.py에 include
|
|
8. 집계 쿼리 구현: time_bucket 기반
|
|
|
|
## 모니터링/성능
|
|
|
|
### 추론 지연
|
|
- rb10508_micro/app/core/emotion/monitoring.py 패턴 참고
|
|
|
|
### 저장 압력
|
|
- TTL/압축은 TimescaleDB 정책으로 설정
|
|
|
|
## 개발 원칙
|
|
- 한 파일 최대 500줄 제한
|
|
- 기능별 파일 분리 (라우터, 서비스, 모델)
|
|
- main.py는 라우터 include만, 직접 엔드포인트 정의 금지
|
|
|
|
## 리스크/롤백
|
|
|
|
### 모델 미가용
|
|
- 균등분포 폴백 허용
|
|
|
|
### DB 장애
|
|
- 저장 실패해도 응답 생성 지속 (비동기 큐)
|
|
|
|
## 테스트 체크리스트
|
|
|
|
- 단위: 레이블 확률 합=1, 엔트로피 범위, dominant 일관성
|
|
- 통합: POST infer → DB insert 생성, GET timeseries → time_bucket 응답
|
|
- 회귀: 균등분포 제거 후 LLM 응답 톤 변화 확인
|