- Fix: remove 'emotion-agnostic for position' (v1 limitation) - Add: v2 5-level addressing (복합 감정 기반) - Add: top_emotions + entropy + cumulative emotion logic - Prevent: addressing overuse (first sentence only)
158 lines
5.4 KiB
Markdown
158 lines
5.4 KiB
Markdown
# 감정 기반 호칭 시스템 구현
|
|
|
|
**작성일**: 2025-12-04
|
|
**수정일**: 2025-12-04 (복합 감정 기반 개선안 추가)
|
|
**작성자**: happybell80
|
|
**상태**: v1 구현 완료, v2 계획
|
|
|
|
---
|
|
|
|
## 배경 및 목표
|
|
|
|
- 현재: 모든 사용자를 "사용자님" 또는 `user.name`으로 획일적 호칭
|
|
- 문제: 감정 상태/상황 무시, `metadata` (nickname, position) 미활용
|
|
- 목표: 감정 기반 호칭 동적 변경으로 공감 능력 강화, 한국 직장 문화 존중 표현
|
|
|
|
## 조사 결과 (웹 검색)
|
|
|
|
**한국어 호칭 원칙**:
|
|
- 친밀한 관계: 별명/애칭 사용
|
|
- 공식적/부정적 상황: 정식 이름 사용
|
|
- 출처: 한국어 호칭 연구, UX 라이팅 전략
|
|
|
|
**AI 감정 반응**:
|
|
- 챗GPT emotional rebound 효과: 부정 → 위로, 긍정 → 친근
|
|
- 출처: 프랑크 바르돌 연구 (m.etnews.com)
|
|
|
|
**직장 문화**: 직책 호칭 최우선 (대표님, 이사님)
|
|
|
|
## 설계 결정
|
|
|
|
### 방식: 휴리스틱 규칙 (Python)
|
|
|
|
이유: 일관성, 속도, 예측 가능성, 제어 용이
|
|
LLM 역할: 결정된 호칭 자연스럽게 사용만
|
|
|
|
### 호칭 결정 규칙 (v1 - 구현 완료)
|
|
|
|
| 조건 | 호칭 |
|
|
|------|------|
|
|
| `metadata->>'position'` 있음 | "대표님", "이사님" |
|
|
| 긍정 감정 + position 없음 | `metadata->>'nickname'` → "joann님" |
|
|
| 부정 감정 + position 없음 | `user.name` → "이고은님" |
|
|
| nickname 없음 | `user.name` 성 제외 → "고은님" |
|
|
|
|
**긍정 감정**: happiness, surprise, neutral
|
|
**부정 감정**: fear, anger, sadness, disgust
|
|
**감정 소스**: emotion_readings 최근 10분 평균 (현재 메시지 가중치 2배)
|
|
|
|
**v1 문제점**: 직책 있으면 감정 무관 → 상황별 미묘한 차이 표현 불가
|
|
|
|
## 구현 (계층 분리 원칙 준수)
|
|
|
|
### 계층 구조
|
|
```
|
|
router.py (감정 분석 후)
|
|
↓
|
|
services/addressing_service.py (신규, 호칭 결정 비즈니스 로직)
|
|
↓
|
|
state/database.py (DB CRUD만)
|
|
```
|
|
|
|
### 1. services/addressing_service.py (신규)
|
|
**기능**: 호칭 결정 비즈니스 로직
|
|
- `get_preferred_name(user_id: str, current_emotion: str) -> str`
|
|
- state 호출: user 정보, 최근 감정 조회
|
|
- 호칭 규칙 적용 (직책 → 긍정감정=nickname → 부정감정=정식이름)
|
|
- 약 100줄 예상
|
|
|
|
### 2. state/database.py
|
|
**기존 함수 확인 후**:
|
|
- 없으면 추가: `get_user_basic_info(user_id)` - name, metadata 반환
|
|
- 없으면 추가: `get_recent_emotions(user_id, minutes=10)` - 감정 평균
|
|
- DB CRUD만, 비즈니스 로직 금지
|
|
|
|
### 3. router.py:264-283
|
|
**수정**: 감정 분석 후 addressing_service 호출
|
|
```python
|
|
from app.services.addressing_service import get_preferred_name
|
|
preferred_name = await get_preferred_name(user_id, user_emotion)
|
|
context['preferred_name'] = preferred_name
|
|
```
|
|
|
|
### 4. llm_service.py:131-132
|
|
**수정**: system_instruction에 호칭 지시
|
|
```python
|
|
preferred_name = enhanced_context.get('preferred_name', '사용자')
|
|
system_instruction += f"사용자를 '{preferred_name}'으로 호칭하세요. "
|
|
```
|
|
|
|
### 5. tables.md
|
|
**추가**: user.metadata 컬럼 문서화
|
|
```
|
|
| metadata | JSONB | YES | nickname, position, short_name, preferences |
|
|
```
|
|
|
|
## 테스트 시나리오
|
|
|
|
1. **긍정+직책**: "고마워요" (happiness) + position="대표" → "대표님"
|
|
2. **긍정+no직책**: "좋은 아침" (happiness) + nickname="joann" → "joann님"
|
|
3. **부정+no직책**: "힘들어요" (sadness) + name="이고은" → "이고은님"
|
|
4. **중립**: "날씨는?" (neutral) + name="김종태" → "종태님"
|
|
|
|
## 검증
|
|
|
|
**로그**:
|
|
```bash
|
|
docker logs rb8001 --tail 100 | grep "Preferred name"
|
|
```
|
|
|
|
**DB**:
|
|
```sql
|
|
-- 최근 10분 감정 평균
|
|
SELECT user_id, AVG((probs->>'happiness')::float) FROM emotion_readings
|
|
WHERE created_at > NOW() - INTERVAL '10 minutes' GROUP BY user_id;
|
|
```
|
|
|
|
**API**:
|
|
```bash
|
|
curl -X POST http://192.168.219.52:8001/api/message \
|
|
-H "Authorization: Bearer $JWT" -d '{"text": "고마워요!"}'
|
|
```
|
|
|
|
## 관련 문서
|
|
|
|
- [[tables.md]](../book/300_architecture/database/tables.md)
|
|
- [[230_감정윤리_필터]](../book/200_core_design/230_감정윤리_필터_LLM후처리와_정체성.md)
|
|
- [[311_FastAPI_구조_원칙]](../book/300_architecture/311_FastAPI_구조_원칙.md)
|
|
- [[251204_addressing_test_report]](251204_addressing_test_report.md)
|
|
|
|
---
|
|
|
|
## 개선 계획 (v2) - 복합 감정 기반
|
|
|
|
### 배경
|
|
v1에서 직책 있으면 감정 무관 → 상황별 미묘한 차이 표현 불가
|
|
|
|
### 복합 감정 5단계 호칭 (직책 + 감정 조합)
|
|
|
|
| 감정 상태 | 호칭 | 예시 |
|
|
|----------|------|------|
|
|
| 매우 부정 (fear+sadness, entropy 낮음) | **김종태 이사님** | "힘들고 불안해요" |
|
|
| 부정 (anger/disgust 단독) | **이사님** | "화나요" |
|
|
| 중립 (neutral 또는 entropy 높음) | **이사님** | "좋긴 한데..." |
|
|
| 긍정 (happiness 단독 0.5-0.9) | **종태 이사님** | "완전 좋아요!" |
|
|
| 매우 긍정 (happiness 0.9+, 누적 긍정) | **종태님** | "최고예요!" (10회차) |
|
|
|
|
### 구현 변경
|
|
- `addressing_service.py`: top_emotions + entropy + 누적 감정 활용 (약 30-40줄 수정)
|
|
- `llm_service.py`: system_instruction에 "호칭은 첫 문장 1회만" 추가 (호칭 남발 방지)
|
|
|
|
### 기대 효과
|
|
1. 감정 미묘함 반영 (복합 감정 패턴)
|
|
2. 호칭 변동 완화 (entropy로 확신도 반영)
|
|
3. 상황별 거리감 조절 (스트레스/갈등/협업/잡담)
|
|
4. 관계 진화 표현 (시간 경과 시 친밀화)
|
|
5. 오판 보완 (top-p 70% 안정성)
|
|
|