- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동 - book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서) - 빈 폴더 제거 (journey/assets/*)
302 lines
9.6 KiB
Markdown
302 lines
9.6 KiB
Markdown
# 로빙 기억력 문제 해결 및 Nginx 포트 추가
|
|
|
|
**날짜**: 2025-07-21
|
|
**작업자**: happybell80 & Claude
|
|
|
|
## 오후 10시 30분
|
|
|
|
### 10408 포트 Nginx SSL 설정 추가
|
|
|
|
**요청**: 희재님 테스트용 10408 포트에 SSL 설정 필요
|
|
|
|
**해결**:
|
|
1. nginx-deploy/server-nginx-default에 설정 추가
|
|
```nginx
|
|
# RB10408 API endpoints (희재님 테스트용)
|
|
location /rb10408/ {
|
|
proxy_pass http://localhost:10408/;
|
|
proxy_set_header Host $host;
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|
}
|
|
```
|
|
|
|
2. Git push로 자동 배포 트리거
|
|
- Gitea Actions가 자동으로 nginx 설정 적용
|
|
- https://ro-being.com/rb10408/ 접속 가능
|
|
|
|
## 오후 11시 00분
|
|
|
|
### 이메일 실제 전송 기능 구현
|
|
|
|
**문제**:
|
|
- "이메일 써줘"라고 하면 내용만 생성하고 실제 전송 안 함
|
|
- 사용자가 "어디로 보냈어?"라고 물어도 대답 못함
|
|
|
|
**원인 분석**:
|
|
- robeing_brain.py에서 이메일 내용만 생성하고 send_email 호출 안 함
|
|
- 의도 분석이 "써줘"를 email intent로 인식 못함
|
|
|
|
**해결**:
|
|
1. intent_patterns에 "이메일.*써" 패턴 추가
|
|
2. _handle_email_request 함수 개선
|
|
- "이메일 써줘. 내용은..." 패턴 인식
|
|
- Gemini로 실제 이메일 내용 생성
|
|
- 생성된 내용으로 실제 전송
|
|
- 이메일 주소 자동 추출 (없으면 기본값)
|
|
|
|
**결과**:
|
|
```python
|
|
# 이전: 내용만 보여주고 끝
|
|
# 이후: 실제로 이메일 전송 후 결과 표시
|
|
"✅ 이메일을 happybell80@naver.com로 전송했습니다!"
|
|
```
|
|
|
|
## 오후 11시 30분
|
|
|
|
### 로빙 기억력 0초 문제 분석
|
|
|
|
**현상**:
|
|
- 로빙이 바로 직전 대화도 기억 못함
|
|
- 매번 새로운 대화처럼 응답
|
|
- 자기가 가진 스킬도 인식 못함
|
|
|
|
**DB 사용 현황 분석**:
|
|
1. **PostgreSQL**: 사용자 정보, 세션 관리
|
|
2. **Neo4j**: 지식 그래프 (거의 미사용)
|
|
3. **ChromaDB**:
|
|
- 모든 대화 저장 중
|
|
- 하지만 검색 기능 미구현
|
|
- 사용자 메시지만 저장, 응답은 저장 안 함
|
|
|
|
**근본 원인**:
|
|
- 단기 대화 컨텍스트 저장소 없음
|
|
- 각 메시지를 독립적으로 처리
|
|
- 설계는 거창하지만 구현이 미완성
|
|
|
|
**개선 방향 결정**:
|
|
1. 가장 심플한 해결책: 메모리 딕셔너리 사용
|
|
2. robeing_brain.py에만 코드 추가
|
|
3. Redis 같은 외부 의존성 추가 안 함
|
|
|
|
**장단점 분석**:
|
|
- 장점: 즉시 작동, 코드 간단, 외부 의존성 없음
|
|
- 단점: 서버 재시작 시 초기화, 멀티프로세스 미지원
|
|
- 결론: MVP 프로토타입에 적합
|
|
|
|
## 오후 11시 50분
|
|
|
|
### 대화 컨텍스트 기능 구현 (진행 중)
|
|
|
|
**구현 내용**:
|
|
1. RobeingBrain 클래스에 conversation_history 딕셔너리 추가
|
|
2. 각 대화 후 히스토리 저장 (최근 5개만)
|
|
3. _handle_general_conversation에서 이전 대화 포함하여 Gemini 호출
|
|
4. ChromaDB 저장 시 응답도 함께 저장하도록 수정 예정
|
|
|
|
**코드 변경사항**:
|
|
```python
|
|
# 1. 대화 히스토리 저장소 추가
|
|
self.conversation_history: Dict[str, List[Dict[str, str]]] = {}
|
|
|
|
# 2. 대화 저장 (process_request 끝부분)
|
|
self.conversation_history[user_id].append({
|
|
"user": text,
|
|
"assistant": response
|
|
})
|
|
|
|
# 3. 컨텍스트 포함 프롬프트
|
|
if history:
|
|
context_text = "이전 대화:\n" + ...
|
|
full_prompt = f"당신은 대화의 흐름을 기억하는 로빙입니다..."
|
|
```
|
|
|
|
**다음 작업**:
|
|
- ChromaDB에 응답도 저장하도록 수정
|
|
- 테스트 후 배포
|
|
|
|
---
|
|
|
|
## 오후 11시 54분
|
|
|
|
### 스킬 시스템 HTTP 분리 및 이메일 스킬 마이크로서비스 전환
|
|
|
|
**문제 상황**:
|
|
- rb10508_test의 email_skill이 직접 import하며 인증 오류 발생
|
|
- 함수형 프로그래밍 시도가 비동기 문제로 복잡해짐
|
|
- 로빙과 스킬의 강한 결합
|
|
|
|
**아키텍처 문제**:
|
|
- 로빙 철학: 스킬은 로빙 컨테이너 외부에 독립적으로 존재해야 함
|
|
- 현재: skill_email을 직접 import하여 사용 (아키텍처 위반)
|
|
- 목표: HTTP API로 완전 분리
|
|
|
|
**해결 과정**:
|
|
|
|
1. **skill_email 서비스화**:
|
|
- main.py로 FastAPI 서버 구현 (포트 8501)
|
|
- `/send`, `/messages`, `/health` 엔드포인트 제공
|
|
- 독립 실행: `python main.py`
|
|
|
|
2. **rb10508_test 수정**:
|
|
- email_skill.py를 HTTP 클라이언트로 단순화 (111줄 → 57줄)
|
|
- sys.path 조작 제거, 직접 import 제거
|
|
- httpx로 API 호출만 수행
|
|
|
|
3. **Nginx 프록시 설정**:
|
|
```nginx
|
|
location /skill-email/ {
|
|
proxy_pass http://localhost:8501/;
|
|
}
|
|
```
|
|
|
|
4. **Docker 설정 정리**:
|
|
- volume mount 제거 (불필요)
|
|
- 환경변수로 SKILL_EMAIL_URL 전달
|
|
|
|
**주요 교훈**:
|
|
- "빠른 개발"이라며 아키텍처 원칙 무시하면 더 복잡해짐
|
|
- 마이크로서비스는 처음부터 HTTP로 분리하는 것이 정답
|
|
- 로빙 철학: 스킬은 외부 서비스, 로빙은 클라이언트
|
|
|
|
**결과**:
|
|
- 완전한 HTTP 분리 달성
|
|
- 인증 문제 해결 (각 서비스가 독립적으로 관리)
|
|
- 확장성 확보 (다른 로빙도 같은 스킬 사용 가능)
|
|
|
|
---
|
|
|
|
---
|
|
|
|
## 새벽 01시 30분
|
|
|
|
### Slack 이벤트 처리 무한루프 및 중복 처리 문제 해결
|
|
|
|
**문제 발견**:
|
|
1. **무한루프**: 로빙이 자기 메시지를 다시 처리하여 계속 응답
|
|
2. **중복 처리**: 같은 멘션 메시지가 app_mention/message 두 번 처리됨
|
|
3. **비동기 순서 문제**: "답변 생성중" 메시지가 실제 답변보다 8초 늦게 도착
|
|
|
|
**해결 과정**:
|
|
|
|
1. **SlackService 봇 ID 동적 획득** (25-31줄):
|
|
```python
|
|
try:
|
|
bot_info = self.client.auth_test()
|
|
self.bot_user_id = bot_info.get('user_id')
|
|
logger.info(f"Bot user ID: {self.bot_user_id}")
|
|
except Exception as e:
|
|
self.bot_user_id = None
|
|
```
|
|
|
|
2. **EventCache 서비스 신규 생성** (`event_cache.py`):
|
|
- 10분 TTL로 event_id 기반 중복 체크
|
|
- 메모리 기반 캐시로 Redis 없이 구현
|
|
- 주기적 만료 항목 정리 기능
|
|
|
|
3. **3중 봇 메시지 필터링** (slack.py 76-80줄):
|
|
```python
|
|
if (bot_id or
|
|
(slack_service.bot_user_id and event_user == slack_service.bot_user_id) or
|
|
subtype == "bot_message"):
|
|
return JSONResponse({"status": "bot_message_ignored"})
|
|
```
|
|
|
|
4. **비동기 응답 순서 보장** (124-132줄):
|
|
- 즉시 "🤔 답변 생성중..." 전송
|
|
- 0.1초 대기로 Slack API 순서 보장
|
|
- 백그라운드 처리 완료 후 "✅ 실제답변" 전송
|
|
|
|
5. **message 이벤트 처리 추가** (94줄):
|
|
- 기존 app_mention만 처리 → app_mention + message 처리
|
|
- 멘션 없는 직접 대화도 처리 가능
|
|
|
|
**추가 발견된 문제**:
|
|
|
|
### 이메일 주소 포함 메시지 필터링 문제
|
|
|
|
**증상**:
|
|
- `goeun2dc@gmail.com으로 전송해줘` → 정상 처리
|
|
- 이후 이메일 내용 전송 → 완전 무반응
|
|
- "?", "로빙?" 등 후속 메시지 → 모두 무반응
|
|
|
|
**원인 분석**:
|
|
- Slack이 이메일을 `<mailto:...>` 형식으로 변환
|
|
- 기존 텍스트 기반 중복 체크가 유사한 패턴을 중복으로 판단
|
|
- event_id 체크 전에 이미 차단됨
|
|
|
|
**해결**:
|
|
- 기존 `processed_events` 텍스트 기반 중복 체크 완전 제거
|
|
- event_id 기반 중복 체크만 유지
|
|
- 이메일 포함 메시지도 정상 처리됨
|
|
|
|
### 봇 필터링 오작동 문제 (진행 중)
|
|
|
|
**증상**:
|
|
- 16:19 첫 메시지 정상 처리
|
|
- 16:20 이후 모든 메시지가 `bot_message_ignored`로 차단
|
|
|
|
**추가한 디버깅**:
|
|
```python
|
|
logger.debug(f"Bot filter check - user: {event_user}, bot_user_id: {slack_service.bot_user_id}, bot_id: {bot_id}, subtype: {subtype}")
|
|
```
|
|
|
|
**대기 중**: 서버 로그에서 실제 필터링 값들 확인 필요
|
|
|
|
---
|
|
|
|
## 새벽 02시 10분
|
|
|
|
### nginx 499 상태 코드 발견 - Slack 3초 타임아웃 근본 원인
|
|
|
|
**🚨 중요한 발견**:
|
|
```
|
|
44.197.111.14 - - [22/Jul/2025:02:12:33 +0900] "POST /rb10508/api/slack/events HTTP/1.1" 499 0
|
|
```
|
|
- **499 = 클라이언트가 연결을 끊음**
|
|
|
|
**종합 분석**:
|
|
|
|
1. **일부 요청이 서버에 도달하지 않음**
|
|
- "로빙 뭐하니?" (Event ID: Ev096TRNGZ2Q) - 원본 없이 retry만 존재
|
|
- nginx까지는 도달했지만 FastAPI 로그에 없음
|
|
|
|
2. **nginx 499 오류 패턴**
|
|
- Slack이 응답을 기다리다 3초 후 포기하고 연결 끊음
|
|
- 우리 앱은 요청을 처리하지만 Slack은 이미 떠남
|
|
- 결과: Slack은 응답 못 받았다고 판단하여 retry
|
|
|
|
3. **타이밍 문제**
|
|
- FastAPI가 200 OK를 반환하는데 시간이 걸림
|
|
- 앱 초기화, 라우팅, 로깅 등으로 지연 발생
|
|
- Slack은 3초 내 응답을 못받으면 연결 끊고 retry
|
|
|
|
**해결책 (내일 진행)**:
|
|
1. **즉시 200 OK 반환하는 미들웨어 추가**
|
|
- 요청 받자마자 200 OK 먼저 반환
|
|
- 실제 처리는 그 다음
|
|
|
|
2. **더 간단한 엔드포인트 구조**
|
|
- 불필요한 로깅, 체크 최소화
|
|
- 가장 빠른 경로로 200 OK 반환
|
|
|
|
3. **비동기 처리 아키텍처 재검토**
|
|
- FastAPI BackgroundTasks 대신 Celery/RQ 검토
|
|
- 완전한 비동기 분리
|
|
|
|
**현재까지 적용된 해결책**:
|
|
- ✅ event_id 기반 중복 체크
|
|
- ✅ 3초 ACK 패턴 (코드상 구현됨)
|
|
- ❌ 하지만 실제로는 여전히 499 발생 (FastAPI 자체 지연)
|
|
|
|
---
|
|
|
|
**교훈**:
|
|
1. "큰 그림"보다 "작동하는 코드"가 우선
|
|
2. 복잡한 아키텍처보다 심플한 해결책이 때로는 최선
|
|
3. 프로토타입 단계에서는 과도한 설계 지양
|
|
4. 아키텍처 원칙을 지키는 것이 결국 더 빠른 개발
|
|
5. 이벤트 처리에서는 공식 event_id 사용이 텍스트 기반보다 정확
|
|
6. 디버깅 로그는 문제 발생 시점에 미리 추가해야 효과적
|
|
7. **nginx 로그 확인이 앱 로그만큼 중요 - 499는 클라이언트 포기 신호** |