diff --git a/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md b/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md index 5f224c0..c74a8f3 100644 --- a/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md +++ b/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md @@ -220,3 +220,34 @@ normal centroid: 80개 샘플 평균, 차원=384 - 단순 centroid 방식은 75% 정확도 한계 - 하이브리드 접근(임베딩 1차 → LLM 2차)이 더 효율적 - Phase 1 임베딩은 보조 역할, Phase 2 LLM이 최종 판단 + +--- + +## 실제 운영 사례 (2025-10-16) + +### 파인티처 투자 제안 메일 누락 + +**발생 시각**: 09:05:07.096 +**메일 정보**: +- 제목: '투자검토요청 관련하여 회사소개서 첨부드립니다_파인티처' +- 발신: petermin@fineteacher.com +- 수신: 2025-10-15 09:44 (24시간 검색 범위 내) + +**필터링 결과**: +``` +Stage 1 (Embedding): REJECT +coldmail: 0.2817, normal: 0.3435 (역전) +``` + +**문제점**: +- 명백한 투자 제안 메일이지만 임베딩 점수 역전으로 Stage 1 탈락 +- Stage 2 (LLM) 도달 실패로 최종 0건 검출 +- Slack 전송 없음 + +**원인**: +- Centroid 샘플이 실제 coldmail 패턴 반영 부족 +- "투자검토요청", "회사소개서" 같은 명시적 키워드도 낮은 점수 + +**영향**: +- 실제 IR 제안 누락으로 비즈니스 기회 손실 가능 +- 하이브리드 필터의 Stage 1 임계값 조정 필요성 확인 diff --git a/troubleshooting/251016_grpc_uvloop_blocking_error.md b/troubleshooting/251016_grpc_uvloop_blocking_error.md new file mode 100644 index 0000000..b04f236 --- /dev/null +++ b/troubleshooting/251016_grpc_uvloop_blocking_error.md @@ -0,0 +1,95 @@ +# gRPC + uvloop BlockingIOError 리소스 경합 + +**날짜**: 2025-10-16 +**작성자**: Claude +**관련 파일**: `rb8001/main.py`, `rb8001/app/llm/emotion_classifier.py` + +--- + +## 문제 상황 + +### 발생 시점 +- NaverWorks Daily Briefing 실행 시 (매일 09:00) +- 감정 분석 LLM 호출 중 발생 (09:00:03.240) + +### 에러 로그 +``` +{"time":"2025-10-16 09:00:03,240","level":"ERROR","module":"asyncio"} +Exception in callback functools.partial(>) +File "grpc/_cython/_cygrpc/aio/completion_queue.pyx.pxi", line 147, in grpc._cython.cygrpc.PollerCompletionQueue._handle_events +BlockingIOError: [Errno 11] Resource temporarily unavailable +``` + +### 영향 +- 작업 실패 없음 (NaverWorks Briefing 정상 완료) +- 로그 노이즈 발생으로 실제 오류 추적 방해 + +--- + +## 원인 분석 + +### 기술적 원인 +- **uvloop**: rb8001 main.py에서 사용 중인 고성능 event loop +- **gRPC**: emotion_classifier.py에서 Vertex AI (Gemini) 호출 시 사용 +- **리소스 경합**: uvloop의 epoll과 gRPC의 polling 메커니즘 충돌 + +### 발생 조건 +1. uvloop 활성화 상태에서 gRPC 비동기 호출 +2. 동시 다발적 LLM 요청 (감정 분석 + 요약 생성) +3. gRPC PollerCompletionQueue의 이벤트 처리 중 일시적 리소스 부족 + +--- + +## 해결 방안 + +### 1. gRPC 이벤트 루프 로깅 억제 (권장) + +**위치**: rb8001/main.py:1-10 + +**현재**: +```python +import uvloop +uvloop.install() +``` + +**변경**: +```python +import uvloop +import logging +uvloop.install() +logging.getLogger("grpc").setLevel(logging.CRITICAL) +``` + +### 2. asyncio 기본 루프 사용 (성능 하락) + +**위치**: rb8001/main.py:1-10 + +**변경**: uvloop.install() 제거, asyncio 기본 루프 사용 + +### 3. gRPC 채널 재사용 설정 + +**위치**: rb8001/app/llm/emotion_classifier.py + +**확인 필요**: gRPC 채널 풀링 설정 확인 + +--- + +## 구현 완료 + +미구현 (로그 노이즈만 발생, 기능 영향 없음) + +--- + +## 교훈 + +### uvloop + gRPC 조합 주의 +- uvloop은 asyncio보다 빠르지만 gRPC와 호환성 이슈 존재 +- 교훈: 성능 라이브러리 도입 시 의존성 충돌 사전 검증 + +### 로그 레벨 관리 +- ERROR 레벨 로그가 실제 오류가 아닐 수 있음 +- 교훈: 외부 라이브러리 로그는 필요 시 레벨 조정 + +### 일시적 오류 vs 치명적 오류 +- BlockingIOError [Errno 11]은 일시적 리소스 부족으로 재시도 가능 +- 교훈: 오류 코드와 영향 범위 구분 필요 diff --git a/troubleshooting/251016_naverworks_briefing_system_uuid_error.md b/troubleshooting/251016_naverworks_briefing_system_uuid_error.md new file mode 100644 index 0000000..b422aa4 --- /dev/null +++ b/troubleshooting/251016_naverworks_briefing_system_uuid_error.md @@ -0,0 +1,114 @@ +# NaverWorks Briefing 'system' 사용자 UUID 오류 + +**날짜**: 2025-10-16 +**작성자**: Claude +**관련 파일**: `rb8001/app/skills/naverworks_briefing.py`, `rb8001/app/state/database.py` + +--- + +## 문제 상황 + +### 발생 시점 +- NaverWorks Daily Briefing 실행 시 (매일 09:00) +- 감정 분석 결과 DB 저장 시도 중 (09:00:03.277) + +### 에러 로그 +``` +{"time":"2025-10-16 09:00:03,277","level":"ERROR","module":"app.state.database"} +Failed to save emotion reading: invalid input for query argument $1: 'system' +(invalid UUID 'system': length must be between 32..36 characters, got 6) + +{"time":"2025-10-16 09:00:03,277","level":"WARNING","module":"app.llm.emotion_llm"} +Failed to save emotion reading for user system +``` + +### 영향 +- emotion_reading 테이블 저장 실패 +- NaverWorks Briefing 자체는 정상 완료 + +--- + +## 원인 분석 + +### 1. 'system' 문자열 전달 +**위치**: naverworks_briefing.py:177 (추정) + +감정 분석 호출 시 user_id로 'system' 문자열 전달: +```python +emotions = await emotion_classifier.classify_emotion(summary_text, user_id="system") +``` + +### 2. DB 저장 시 UUID 검증 +**위치**: app/state/database.py + +emotion_reading 테이블의 user_id 컬럼은 UUID 타입: +```sql +user_id UUID NOT NULL REFERENCES users(id) +``` + +### 3. UUID 변환 실패 +asyncpg는 'system' 문자열을 UUID로 변환 불가 (길이 6자 < 32자) + +--- + +## 해결 방안 + +### 1. user_id를 nullable로 변경 (DB) + +**51123 서버에서 실행**: +```sql +ALTER TABLE emotion_reading ALTER COLUMN user_id DROP NOT NULL; +``` + +### 2. 시스템 사용자 처리 로직 추가 + +**위치**: app/state/database.py:save_emotion_reading + +**변경**: +```python +async def save_emotion_reading(user_id: str, emotions: List[str], ...): + if user_id == "system": + user_id = None # nullable로 저장 + await conn.execute("INSERT INTO emotion_reading ...") +``` + +### 3. 전용 시스템 UUID 생성 (권장) + +**51123 서버에서 실행**: +```sql +-- 전용 시스템 사용자 생성 +INSERT INTO users (id, username, email) VALUES +('00000000-0000-0000-0000-000000000000', 'system', 'system@robeing.internal'); +``` + +**위치**: naverworks_briefing.py + +**변경**: SYSTEM_USER_UUID 환경변수 사용 + +--- + +## 구현 완료 + +미구현 (감정 저장 실패만, 브리핑 기능 정상) + +--- + +## 교훈 + +### UUID 타입 검증 누락 +- 함수 시그니처에서 user_id: str 허용하지만 실제는 UUID 필요 +- 교훈: 타입 힌트와 실제 검증 불일치 방지 + +### 시스템 사용자 처리 미정의 +- 사용자가 아닌 시스템 작업의 user_id 처리 정책 부재 +- 교훈: 시스템 사용자는 전용 UUID 또는 nullable 정책 수립 + +### DB 제약조건과 코드 불일치 +- DB는 NOT NULL이지만 코드는 'system' 문자열 전달 +- 교훈: DB 스키마와 코드 로직 동기화 검증 필요 + +--- + +## 참고 문서 + +- 250826_id_체계_정리_및_conversation_logs_문제_해결.md: UUID vs Slack ID 문제