DOCS/troubleshooting/251002_conversation_log_redesign.md
happybell80 b18b7f1143 refactor: 트러블슈팅 파일명 형식 통일
- 8자리 날짜(20251013) → 6자리 날짜(251013)
- 16개 파일 rename 완료
- 형식: yymmdd_주제.md로 통일

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-13 21:09:56 +09:00

229 lines
6.9 KiB
Markdown

# Conversation Log 재설계: Event Stream 아키텍처
## 작성일
2025-10-02
## 문제 상황
### 기존 conversation_log 테이블 한계
```sql
conversation_log
- user_id (FK)
- message
- response
```
**가정**: 항상 1:1 대화 (user → robeing)
### 실제 요구사항
1. **그룹 대화**: user1, user2, user3, robeing
2. **로빙 브리핑**: robeing → 팀 전체
3. **로빙 협업**: robeing1 ↔ robeing2
4. **멀티턴 쓰레드**: user ↔ robeing (여러 왕복)
### 근본 문제
- **참여자 표현 불가**: 1:1만 가정
- **방향성 모호**: message/response 구분 애매
- **컨텍스트 부재**: 어떤 채널/쓰레드인지 불명확
- **로빙 철학 위배**: 로빙도 대화 주체인데 user 테이블에 없음
## 철학적 배경
### 로빙 철학과 DB 구조 충돌
- **user 테이블**: 인증/식별 (OAuth, email, login)
- **robeing 테이블**: 성장 주체 (level, experience, stats)
- **문제**: 로빙 봇(Slack Bot ID)이 user 테이블에 없어서 봇 메시지 저장 불가
### "대화는 이벤트의 흐름"
- **Graph DB**: 공간적 관계망 (A-B-C 연결 구조)
- **Event Stream**: 시간적 흐름 (A→B→C 순서)
- 로빙의 베이지안 성장 = 시간에 따른 업데이트 → **시간축이 핵심**
## 해결 방안: Event 테이블 (6하원칙)
### 설계 원칙
**장소(where_id) + 시간(timestamp) = 이벤트 연결**
- where_id: Slack, 이메일, 웹, 음성통화 등 모든 채널
- timestamp: 시간 순서 보장
- 6하원칙: 모든 이벤트를 표준화
### Event 테이블 구조
```sql
event
-- 기본 6하원칙
- id (PK, UUID)
- who_id (UUID) -- 행위 주체
- who_type (VARCHAR) -- human/robeing/system
- what_type (VARCHAR) -- message/file_transfer/task_assign/analysis_request/reaction
- what_content (JSONB) -- 실제 내용/데이터
- when_at (TIMESTAMPTZ) -- 시간
- where_id (VARCHAR) -- 장소 (channel/dm/email/system)
- where_type (VARCHAR) -- slack/email/web/voice/internal
- why (VARCHAR) -- intent/purpose
- how (VARCHAR) -- method/tool
-- 확장 필드 (복잡한 관계 처리)
- to_who (JSONB) -- [{id, type}] 수신자 목록
- using (JSONB) -- [{id, type, resource}] 사용된 리소스/데이터
- affected (JSONB) -- [{id, type, change}] 영향받은 대상
- parent_event_id (UUID) -- 이전 이벤트 (인과관계)
- result (JSONB) -- {status, outcome, data} 결과
- metadata (JSONB) -- 기타 확장 데이터
-- 인덱스
- (where_id, when_at) -- 시계열 조회
- (who_id, when_at) -- 주체별 이력
- parent_event_id -- 이벤트 체인
```
### JSONB 활용 예시
#### 1. 파일 전송
```json
{
"who_id": "user-uuid",
"what_type": "file_transfer",
"what_content": {"filename": "report.pdf", "size": 1024},
"to_who": [{"id": "user2-uuid", "type": "human"}],
"where_id": "slack-dm-123"
}
```
#### 2. 로빙 협업
```json
{
"who_id": "robeing1-uuid",
"what_type": "analysis_request",
"to_who": [{"id": "robeing2-uuid", "type": "robeing"}],
"using": [{"id": "robeing3-uuid", "type": "robeing", "resource": "market_data"}],
"result": {"status": "completed", "data": "..."}
}
```
#### 3. 그룹 대화
```json
{
"who_id": "user1-uuid",
"what_type": "message",
"what_content": {"text": "팀 회의 시작합니다"},
"to_who": [
{"id": "user2-uuid", "type": "human"},
{"id": "user3-uuid", "type": "human"},
{"id": "robeing-uuid", "type": "robeing"}
],
"where_id": "slack-channel-team"
}
```
## 이벤트 연결 메커니즘
### 간단 버전
1. **같은 장소**: where_id로 묶음
2. **같은 쓰레드**: parent_event_id 체인
3. **시간 순서**: when_at으로 정렬
### 쿼리 예시
```sql
-- 특정 채널의 모든 이벤트 (시간순)
SELECT * FROM event
WHERE where_id = 'slack-channel-123'
ORDER BY when_at;
-- 특정 이벤트 체인 (쓰레드)
WITH RECURSIVE thread AS (
SELECT * FROM event WHERE id = 'root-event-id'
UNION ALL
SELECT e.* FROM event e
JOIN thread t ON e.parent_event_id = t.id
)
SELECT * FROM thread ORDER BY when_at;
```
## Graph DB와의 관계
### Graph DB가 필요한 순간
1. **복잡한 관계 쿼리 반복**: 3-hop 이상 탐색
2. **실시간 추천**: 협업 필터링, 소셜 추천
3. **영향력 분석**: 정보 전파 경로, 네트워크 중심성
4. **다중 로빙 협업**: 최적 조합 찾기
### 현재 판단
**Event Stream만으로 충분**
- JSONB로 관계 저장 (충분히 유연)
- 시계열 분석이 핵심 (TimescaleDB 최적화)
- 나중에 필요하면 Graph DB 추가 (Read Model 패턴)
### Graph vs Event Stream
- **Graph**: "누구와 연결되었나" (공간적 관계망)
- **Event**: "언제 무엇을 했나" (시간적 흐름)
- 로빙의 핵심(성장/기억/감정) = 시간축 → **Graph는 optional**
## 마이그레이션 전략
### 1단계: Event 테이블 생성 (robeing_metrics DB)
```sql
CREATE TABLE event (
-- 6하원칙 컬럼들
...
) PARTITION BY RANGE (when_at);
-- TimescaleDB 하이퍼테이블 전환
SELECT create_hypertable('event', 'when_at');
```
### 2단계: 기존 conversation_log 데이터 변환
```sql
INSERT INTO event (who_id, what_type, what_content, when_at, where_id, ...)
SELECT
user_id as who_id,
'message' as what_type,
jsonb_build_object('text', message, 'response', response) as what_content,
timestamp as when_at,
channel_id as where_id,
...
FROM conversation_log;
```
### 3단계: 애플리케이션 코드 전환
- conversation_log 조회 → event 조회
- 새 대화 저장 → event INSERT
## 기대 효과
### 1. 확장성
- 모든 종류의 이벤트 저장 가능 (대화, 파일, 작업, 알림 등)
- 새로운 이벤트 타입 추가 시 스키마 변경 불필요 (JSONB)
### 2. 분석 역량
- 시계열 분석 최적화 (TimescaleDB)
- 이벤트 체인 추적 (베이지안 업데이트)
- 사용자/로빙별 이력 조회
### 3. 철학적 일관성
- 로빙도 이벤트 주체로 동등하게 취급
- user = "관계의 상대방" 명확화
- 성장과 기억의 원천 = 이벤트 흐름
## 교훈
### 기존 설계의 문제
- **conversation_log**: 챗봇 QA 로그 수준
- **1:1 가정**: 다중 참여자 협업 불가
- **user/robeing 분리**: 로빙을 도구로 취급
### 새로운 설계의 핵심
- **Event Stream**: 모든 행위를 시간순 이벤트로
- **6하원칙**: 표준화된 구조
- **JSONB**: 복잡한 관계도 유연하게
- **시간축 중심**: 베이지안 성장과 일치
## 참고사항
### 관련 문서
- `/home/admin/DOCS/300_architecture/database/tables.md`: DB 스키마
- `/home/admin/DOCS/100_philosophy/125_베이즈_성장과_관계의_철학.md`: 로빙 철학
### DB 위치
- **main_db**: user, robeing 테이블 (기존)
- **robeing_metrics**: event 테이블 (새로 추가)
- TimescaleDB 하이퍼테이블로 시계열 최적화