- gmail_tokens → gmail_token (33 files) - companies → company (17 files) - conversation_logs → conversation_log (27 files) - workspace_members → workspace_member (28 files) All table names now match the actual PostgreSQL schema
400 lines
10 KiB
Markdown
400 lines
10 KiB
Markdown
# rb8001과 skill-email Gmail 통합 완료 보고서
|
|
|
|
## 작업일: 2025-08-19
|
|
## 작업자: 희재
|
|
## 상태: 완료
|
|
|
|
---
|
|
|
|
## 1. 작업 개요
|
|
|
|
### 목적
|
|
rb8001이 Gmail 기능을 사용할 수 있도록 skill-email 서비스와 통합
|
|
|
|
### 배경
|
|
- skill-email은 DB 기반 토큰 관리로 전환 완료
|
|
- rb8001이 사용자의 Gmail 요청을 감지하고 처리할 수 있도록 통합 필요
|
|
- Gmail 아이템 장착 상태 확인 및 권한 관리 필요
|
|
|
|
---
|
|
|
|
## 2. 구현 내용
|
|
|
|
### 2.1 환경변수 설정
|
|
**파일**: `/home/heejae/rb8001/.env`
|
|
```bash
|
|
# Skill Services
|
|
SKILL_EMAIL_URL=http://localhost:8501
|
|
MONITOR_SERVICE_URL=http://localhost:9024
|
|
|
|
# PostgreSQL for Gmail tokens (SSH tunnel)
|
|
POSTGRES_CONNECTION_STRING=postgresql://robeings:robeings@localhost:5433/main_db
|
|
```
|
|
|
|
### 2.2 Gmail 통합 모듈 구현
|
|
**파일**: `/home/heejae/rb8001/app/skills/email_integration.py`
|
|
|
|
#### 주요 클래스: EmailIntegration
|
|
Gmail 스킬 통합을 담당하는 핵심 모듈
|
|
|
|
#### 핵심 메서드
|
|
|
|
1. **check_gmail_equipped(user_id)**
|
|
```python
|
|
async def check_gmail_equipped(self, user_id: str) -> bool:
|
|
# PostgreSQL에서 직접 장착 상태 확인
|
|
# 5분 캐싱으로 성능 최적화
|
|
cur.execute("""
|
|
SELECT COUNT(*) FROM gmail_token
|
|
WHERE user_id = %s AND is_equipped = true
|
|
""", (user_id,))
|
|
```
|
|
- gmail_token 테이블의 is_equipped 확인
|
|
- 캐시 TTL: 300초 (5분)
|
|
- DB 연결 실패 시 False 반환
|
|
|
|
2. **parse_email_intent(message)**
|
|
```python
|
|
def parse_email_intent(self, message: str) -> Optional[Dict[str, Any]]:
|
|
# 이메일 관련 키워드 감지
|
|
# 발송/조회/답장 의도 분류
|
|
# 수신자, 제목, 내용 추출
|
|
```
|
|
- 키워드: "이메일", "메일", "보내", "전송", "확인", "조회", "답장"
|
|
- 정규표현식으로 수신자 추출: `([가-힣]+님?)(?:한테|에게|께)`
|
|
- 이메일 주소 패턴: `[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}`
|
|
|
|
3. **process_email_request(message, user_id, channel)**
|
|
```python
|
|
async def process_email_request(
|
|
self,
|
|
message: str,
|
|
user_id: str,
|
|
channel: str = None
|
|
) -> Tuple[bool, str]:
|
|
# 1. Gmail 장착 확인
|
|
# 2. 이메일 의도 파싱
|
|
# 3. skill-email 서비스 호출
|
|
# 4. 응답 처리
|
|
```
|
|
|
|
**처리 플로우:**
|
|
- 미장착 시: "Gmail 패스포트를 먼저 장착해주세요! 🎫"
|
|
- 발송 요청: POST /process 엔드포인트 호출
|
|
- 조회 요청: GET /messages 엔드포인트 호출
|
|
- 토큰 만료: 재인증 안내 메시지
|
|
|
|
4. **handle_reauth(user_id)**
|
|
```python
|
|
async def handle_reauth(self, user_id: str) -> Tuple[bool, str]:
|
|
# robeing-monitor 서비스에 재인증 요청
|
|
# OAuth URL 생성 및 반환
|
|
```
|
|
|
|
### 2.3 라우터 통합
|
|
**파일**: `/home/heejae/rb8001/app/router/router.py`
|
|
|
|
#### 변경 내용
|
|
```python
|
|
# Import 추가
|
|
from app.skills.email_integration import email_integration
|
|
|
|
# route_message 메서드에 Gmail 처리 추가
|
|
async def route_message(self, message: str, user_id: str, channel: str, thread_ts: str = None):
|
|
# 0. 슬래시 명령어 처리
|
|
# ...
|
|
|
|
# 0.5. Gmail 요청 확인 및 처리 (새로 추가)
|
|
email_result = await email_integration.process_email_request(message, user_id, channel)
|
|
if email_result[0] or email_result[1] is not None:
|
|
return {
|
|
"success": email_result[0],
|
|
"message": email_result[1],
|
|
"content": email_result[1],
|
|
"service": "gmail",
|
|
"execution_plan": {"intent": "email", "skills": ["email"]}
|
|
}
|
|
|
|
# 1. 기존 Brain 기반 라우팅
|
|
# ...
|
|
```
|
|
|
|
**처리 우선순위:**
|
|
1. 슬래시 명령어 (/memory, /stats 등)
|
|
2. Gmail 요청 (이메일 발송/조회)
|
|
3. 일반 Brain 기반 라우팅
|
|
|
|
---
|
|
|
|
## 3. 테스트 구현
|
|
|
|
### 3.1 통합 테스트 스크립트
|
|
**파일**: `/home/heejae/rb8001/test_gmail_integration.py`
|
|
|
|
#### 테스트 케이스
|
|
1. **장착 상태 확인**
|
|
- U091UNVE41M (전희재): is_equipped=True ✅
|
|
- U0925SXQFDK (종태): is_equipped=False ✅
|
|
|
|
2. **의도 파싱 테스트**
|
|
- "종태님한테 회의 일정 메일 보내줘" → action: send, to: 종태님 ✅
|
|
- "최근 메일 확인해줘" → action: list, limit: 5 ✅
|
|
|
|
3. **프로세스 테스트**
|
|
- 미장착 사용자 → 장착 안내 메시지 ✅
|
|
- 장착된 사용자 → skill-email 호출 ✅
|
|
|
|
### 3.2 테스트 결과
|
|
```bash
|
|
=== rb8001 Gmail Integration Test ===
|
|
|
|
[Test: 장착 상태 확인]
|
|
User U091UNVE41M: Gmail equipped = True
|
|
|
|
[Test: 이메일 발송 의도 파싱]
|
|
Parsed intent: {'action': 'send', 'to': '종태님', 'subject': '회의 일정 안내', 'body': '...'}
|
|
|
|
[Test: 이메일 조회 의도 파싱]
|
|
Parsed intent: {'action': 'list', 'limit': 5}
|
|
|
|
[Test: 이메일 처리 (미장착)]
|
|
Success: False
|
|
Message: Gmail 패스포트를 먼저 장착해주세요! 🎫
|
|
|
|
[Test: 이메일 처리 (장착됨)]
|
|
Success: True
|
|
Message: 이메일을 보내기 위해 다음 정보가 필요합니다...
|
|
```
|
|
|
|
---
|
|
|
|
## 4. API 통신
|
|
|
|
### 4.1 skill-email 엔드포인트
|
|
- **POST /process**: 대화형 이메일 처리
|
|
- **GET /messages**: 이메일 목록 조회
|
|
- **GET /health**: 서비스 상태 확인
|
|
|
|
### 4.2 요청/응답 형식
|
|
|
|
#### 발송 요청
|
|
```json
|
|
{
|
|
"message": "종태님한테 회의 일정 메일 보내줘",
|
|
"user_id": "U091UNVE41M",
|
|
"channel": "C123456",
|
|
"robeing_id": "rb8001"
|
|
}
|
|
```
|
|
|
|
#### 응답
|
|
```json
|
|
{
|
|
"success": true,
|
|
"content": "이메일 처리 메시지",
|
|
"data": {
|
|
"type": "need_more_info|send|error",
|
|
"draft": {...},
|
|
"missing_fields": ["to", "subject"]
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. 보안 및 권한 관리
|
|
|
|
### 5.1 장착 확인
|
|
- is_equipped=true인 토큰만 사용 가능
|
|
- 사용자별 개별 확인 (Slack User ID 기반)
|
|
|
|
### 5.2 캐싱
|
|
- 장착 상태 5분 캐싱
|
|
- DB 부하 감소 및 응답 속도 개선
|
|
|
|
### 5.3 에러 처리
|
|
- DB 연결 실패 → 기본값 False (안전한 실패)
|
|
- 토큰 만료 → 재인증 안내
|
|
- 네트워크 타임아웃 → 30초 제한
|
|
|
|
---
|
|
|
|
## 6. 디렉토리 구조
|
|
|
|
```
|
|
/home/heejae/rb8001/
|
|
├── .env # 환경변수 (수정됨)
|
|
├── app/
|
|
│ ├── skills/ # 새로 생성
|
|
│ │ └── email_integration.py # Gmail 통합 모듈
|
|
│ ├── router/
|
|
│ │ └── router.py # 라우터 (수정됨)
|
|
│ └── ...
|
|
└── test_gmail_integration.py # 테스트 스크립트
|
|
```
|
|
|
|
---
|
|
|
|
## 7. 데이터 플로우
|
|
|
|
```
|
|
사용자 메시지 (Slack)
|
|
↓
|
|
rb8001 (router.py)
|
|
↓
|
|
Gmail 의도 감지
|
|
↓
|
|
장착 상태 확인 (PostgreSQL)
|
|
↓
|
|
skill-email 호출 (HTTP)
|
|
↓
|
|
Gmail API 실행
|
|
↓
|
|
응답 반환
|
|
↓
|
|
Slack 메시지 전송
|
|
```
|
|
|
|
---
|
|
|
|
## 8. 주요 성과
|
|
|
|
1. **통합 완료** ✅
|
|
- rb8001이 Gmail 요청 감지 및 처리 가능
|
|
- skill-email 서비스와 완전 연동
|
|
|
|
2. **권한 관리** ✅
|
|
- Gmail 아이템 장착 상태 확인
|
|
- 미장착 시 친절한 안내 메시지
|
|
|
|
3. **성능 최적화** ✅
|
|
- 5분 캐싱으로 DB 부하 감소
|
|
- 30초 타임아웃으로 무한 대기 방지
|
|
|
|
4. **유연한 구조** ✅
|
|
- 의도 파싱과 서비스 호출 분리
|
|
- 확장 가능한 아키텍처
|
|
|
|
---
|
|
|
|
## 9. 문제 해결
|
|
|
|
### 9.1 권한 오류
|
|
- **문제**: `/home/heejae/rb8001/app/skills/` 디렉토리 생성 실패
|
|
- **해결**: `sudo mkdir -p` 및 `chmod 777` 적용
|
|
|
|
### 9.2 의도 파싱
|
|
- **문제**: "test@example.com에게" 형식 파싱 실패
|
|
- **해결**: 이메일 정규표현식 패턴 추가
|
|
|
|
### 9.3 모듈 임포트
|
|
- **문제**: psycopg2 모듈 위치
|
|
- **해결**: 함수 내부에서 동적 임포트
|
|
|
|
---
|
|
|
|
## 10. 사용 시나리오
|
|
|
|
### 시나리오 1: 이메일 발송
|
|
```
|
|
사용자: @rb8001 종태님한테 회의 일정 메일 보내줘
|
|
rb8001: (장착 확인) → (의도 파싱) → (skill-email 호출)
|
|
skill-email: 이메일 주소가 필요합니다. 종태님의 이메일은?
|
|
사용자: goeun2dc@gmail.com
|
|
skill-email: (이메일 발송) → 성공 메시지
|
|
```
|
|
|
|
### 시나리오 2: 미장착 사용자
|
|
```
|
|
사용자: @rb8001 이메일 보내줘
|
|
rb8001: Gmail 패스포트를 먼저 장착해주세요! 🎫
|
|
`/inventory` 명령어로 인벤토리를 확인할 수 있습니다.
|
|
```
|
|
|
|
### 시나리오 3: 메일 조회
|
|
```
|
|
사용자: @rb8001 최근 메일 확인해줘
|
|
rb8001: 📧 최근 이메일:
|
|
1. *회의 일정 안내*
|
|
발신: 김종태
|
|
날짜: 2025-08-19
|
|
...
|
|
```
|
|
|
|
---
|
|
|
|
## 11. 향후 개선사항
|
|
|
|
### 즉시 필요
|
|
- [ ] Docker 컨테이너 재빌드 및 배포
|
|
- [ ] Slack 실제 환경 테스트
|
|
- [ ] 에러 로깅 강화
|
|
|
|
### 추후 개선
|
|
- [ ] 사용자 이름 → 이메일 주소 자동 매핑
|
|
- [ ] 답장 기능 구현
|
|
- [ ] 첨부파일 지원
|
|
- [ ] 이메일 검색 기능
|
|
- [ ] 대화 컨텍스트 유지 (연속 대화)
|
|
|
|
---
|
|
|
|
## 12. 의존성
|
|
|
|
### Python 패키지
|
|
- httpx: 비동기 HTTP 클라이언트
|
|
- psycopg2-binary: PostgreSQL 연결
|
|
- asyncio: 비동기 처리
|
|
|
|
### 외부 서비스
|
|
- skill-email (포트 8501)
|
|
- PostgreSQL (SSH 터널 5433 → 5432)
|
|
- robeing-monitor (포트 9024, 선택적)
|
|
|
|
---
|
|
|
|
## 13. 참고 명령어
|
|
|
|
```bash
|
|
# 통합 테스트 실행
|
|
cd /home/heejae/rb8001 && python3 test_gmail_integration.py
|
|
|
|
# skill-email 상태 확인
|
|
curl http://localhost:8501/health
|
|
|
|
# 이메일 발송 테스트
|
|
curl -X POST http://localhost:8501/process \
|
|
-H "Content-Type: application/json" \
|
|
-d '{"message": "...", "user_id": "U091UNVE41M", ...}'
|
|
|
|
# PostgreSQL 장착 상태 확인
|
|
python3 -c "
|
|
import psycopg2
|
|
conn = psycopg2.connect('postgresql://robeings:robeings@localhost:5433/main_db')
|
|
cur = conn.cursor()
|
|
cur.execute('SELECT user_id, is_equipped FROM gmail_token')
|
|
for row in cur.fetchall():
|
|
print(f'{row[0]}: equipped={row[1]}')
|
|
"
|
|
```
|
|
|
|
---
|
|
|
|
## 14. 검증 체크리스트
|
|
|
|
- [x] 환경변수 설정 완료
|
|
- [x] EmailIntegration 클래스 구현
|
|
- [x] 장착 상태 확인 동작
|
|
- [x] 의도 파싱 정확도
|
|
- [x] skill-email API 호출 성공
|
|
- [x] 라우터 통합 완료
|
|
- [x] 에러 처리 동작
|
|
- [x] 캐싱 메커니즘 동작
|
|
- [x] 테스트 스크립트 통과
|
|
- [ ] 실제 Gmail API 호출 (토큰 유효성)
|
|
- [ ] Slack 실환경 테스트
|
|
|
|
---
|
|
|
|
**작업 완료: 2025-08-19**
|
|
**다음 단계: Docker 배포 및 실환경 테스트** |