DOCS/troubleshooting/20251002_emotion_system_implementation.md
happybell80 dc0d76b2f5 감정 시스템 구현 완료 문서 업데이트
- 최종 구현 완료 섹션 추가 (핵심 기능, 검증 결과, 아키텍처)
- 수정한 파일 목록 및 DB 현황 기록
- 작업 순서 체크리스트 완료 표시
- 테스트 결과 및 교훈 추가
- 마이크로서비스 아키텍처, asyncio, JSONB 교훈 정리

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-02 12:30:06 +09:00

11 KiB

감정 시스템 구현 문서 (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

최종 구현 완료 (2025-10-02)

핵심 기능

  • 균등분포 제거: 실제 ONNX 모델 사용 (aihub-7emotions-v1)
  • skill-embedding 연동: HTTP API로 감정 분석 서비스 호출
  • DB 저장: emotion_readings 테이블에 실시간 데이터 저장 (3건 확인)
  • API 정상 작동: /v1/emotion/infer 엔드포인트 200 OK

감정 분석 검증 결과

  • 화난 텍스트: sadness 90% (우울/좌절 감지)
  • 기쁜 텍스트: happiness 99% (기쁨 감지)
  • 분노 텍스트: anger 68.8% (분노 감지)

아키텍처

  • 경량화: rb8001은 HTTP 클라이언트만, ONNX는 skill-embedding에서 처리
  • 비동기: asyncio 기반, FastAPI event loop 충돌 해결
  • 폴백: skill-embedding 장애 시 균등분포 반환

수정한 파일

  • rb8001/app/router/emotion_endpoint.py: EmotionClassifier 초기화, predict_async 사용
  • rb8001/app/core/emotion/base.py: async 함수로 변경, EmotionClassifier 연동
  • rb8001/app/core/emotion/emotion_classifier.py: skill-embedding HTTP 호출
  • rb8001/app/state/database.py: JSONB 저장 시 json.dumps() 사용
  • rb8001/tests/test_emotion_system.py: async 테스트 수정
  • rb8001/main.py: emotion_router include (line 48-50)

DB 현황

emotion_readings 테이블: 3건

  • 12:27:10 | happiness (90.6%)
  • 12:22:56 | happiness (99.2%)
  • 12:21:57 | anger (68.8%)

구현 완료 항목

  • rb8001/app/core/emotion/emotion_classifier.py (skill-embedding 호출)
  • ONNX 모델은 skill-embedding이 처리
  • emotion_readings 테이블 (51123 서버에서 생성 완료)
  • /v1/emotion/* API 엔드포인트 (구현 완료)
  • asyncio event loop 충돌 수정
  • JSONB 타입 캐스팅

미구현 항목 (다음 단계)

  • LLM 톤 조절: emotion_llm.py 연동 (rb8001/app/llm/llm_service.py:133)
  • 시계열 집계: TimescaleDB time_bucket 쿼리
  • 팀 감정 지표: 채널별/회사별 집계

활용 가능 시나리오

  • 시나리오 1 (즉시 가능): 감정 기반 대화 톤 자동 조절
  • 시나리오 2-3 (개발 필요): 주간 리포트, 팀 감정 모니터링

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
  • 처리 방식: skill-embedding 서비스가 ONNX 모델 처리, rb8001은 HTTP API 호출만

데이터 모델 설계

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. DB 스키마 준비: 51123 서버에서 robeing_metrics DB에 emotion_readings 테이블 생성
  4. DB 클라이언트: rb8001/app/state/database.py 활용
  5. API 라우터 생성: rb8001/app/router/emotion_endpoint.py 생성 후 main.py에 include
  6. asyncio event loop 충돌 수정: 모든 감정 함수를 async로 변경
  7. JSONB 타입 캐스팅: json.dumps()로 변환 후 저장
  8. EmotionAwareLLM 연결: rb8001/app/llm/emotion_llm.py:25 (미구현)
  9. LLMService 톤 조절: rb8001/app/llm/llm_service.py:133 (미구현)
  10. 집계 쿼리 구현: time_bucket 기반 (미구현)

모니터링/성능

추론 지연

  • rb10508_micro/app/core/emotion/monitoring.py 패턴 참고

저장 압력

  • TTL/압축은 TimescaleDB 정책으로 설정

개발 원칙

  • 한 파일 최대 500줄 제한
  • 기능별 파일 분리 (라우터, 서비스, 모델)
  • main.py는 라우터 include만, 직접 엔드포인트 정의 금지

리스크/롤백

모델 미가용

  • 균등분포 폴백 허용

DB 장애

  • 저장 실패해도 응답 생성 지속 (비동기 큐)

테스트 결과

성공한 테스트

  • API 정상 작동: /v1/emotion/infer 200 OK
  • DB 저장: emotion_readings 테이블에 3건 저장 확인
  • 감정 분석 정확도:
    • 화난 텍스트 → sadness 90%
    • 기쁜 텍스트 → happiness 99%
    • 분노 텍스트 → anger 68.8%
  • 균등분포 제거: 실제 모델 추론 값 사용

수정한 문제

  • asyncio event loop 충돌 (new_event_loop 제거)
  • JSONB 저장 오류 (json.dumps 추가)
  • 테스트 코드 비동기 함수 호출

교훈

기술적 교훈

  1. 마이크로서비스 아키텍처: rb8001 경량화를 위해 ONNX 모델을 skill-embedding으로 분리
  2. asyncio 주의: FastAPI는 이미 event loop 내에서 실행 중, new_event_loop() 사용 금지
  3. JSONB 타입: asyncpg는 dict를 자동 변환하지 않음, json.dumps() 필요
  4. 폴백 전략: 외부 서비스 장애 대비 균등분포 폴백 구현

설계 교훈

  1. 문서와 실제 코드 불일치: 문서를 무조건 신뢰하지 말고 실제 코드 확인 필수
  2. 추측 금지: "아마", "것 같다" 대신 직접 확인
  3. 한 파일 500줄 제한: 기능별 파일 분리로 유지보수성 향상
  4. 테스트 중요성: 실제 API는 작동해도 테스트 코드가 틀릴 수 있음