From 4dc8bf3e4ca2eca735a60f2680c85def8eed2d68 Mon Sep 17 00:00:00 2001 From: Claude-51124 Date: Tue, 14 Oct 2025 15:18:56 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20troubleshooting=20=EB=AC=B8=EC=84=9C=20?= =?UTF-8?q?=ED=86=B5=ED=95=A9=20(plans=20=EC=82=AD=EC=A0=9C,=20102?= =?UTF-8?q?=EC=A4=84)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - plans/251014_claude_coldmail_hybrid_implementation.md 삭제 - troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md 통합 - 문제 분석 + 해결 방안 + 구현 계획 + 교훈 (102줄) - 코드 블록 최소화, 파일명:줄번호로 참조 - 구현 완료 섹션 추가 (커밋 7c5b033) 문서 작성 원칙 준수 (100줄 이하, 주제별 분리 대신 축약) Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- ...4_claude_coldmail_hybrid_implementation.md | 138 ----------------- ...aude_coldmail_filter_tokenization_issue.md | 145 +++++++----------- 2 files changed, 59 insertions(+), 224 deletions(-) delete mode 100644 plans/251014_claude_coldmail_hybrid_implementation.md diff --git a/plans/251014_claude_coldmail_hybrid_implementation.md b/plans/251014_claude_coldmail_hybrid_implementation.md deleted file mode 100644 index a1ecb96..0000000 --- a/plans/251014_claude_coldmail_hybrid_implementation.md +++ /dev/null @@ -1,138 +0,0 @@ -# Coldmail 하이브리드 필터 구현 계획 - -**날짜**: 2025-10-14 -**작성자**: Claude (51124 서버 전담) -**관련**: `251014_claude_coldmail_filter_tokenization_issue.md` - ---- - -## 구현 개요 - -**목표**: KoBERT + Gemini + Naive Bayes 3단계 하이브리드 coldmail 필터 -**예상 시간**: 12시간 (1.5일) - ---- - -## Phase 1: KoBERT 임베딩 필터 (4시간) - -### 신규 파일 -- `rb8001/app/services/coldmail_embedding_filter.py` - -### 주요 함수 -- `create_embedding()`: skill-embedding (8515)로 임베딩 생성 -- `calculate_similarity()`: cosine similarity 계산 -- `is_coldmail_by_embedding()`: threshold 0.6 기반 판단 - -### DB 테이블 -```sql -CREATE TABLE coldmail_embedding_clusters ( - id SERIAL PRIMARY KEY, - embedding VECTOR(768), - label VARCHAR(10), - example_subject TEXT, - created_at TIMESTAMP DEFAULT NOW() -); -``` - -**필요 확장**: pgvector extension (`CREATE EXTENSION vector;`) - ---- - -## Phase 2: Gemini LLM 분류기 (2시간) - -### 신규 파일 -- `rb8001/app/services/coldmail_llm_classifier.py` - -### 주요 함수 -- `classify_by_llm()`: Gemini zero-shot 분류 + 이유 설명 - -### Prompt 예시 -``` -다음 이메일이 투자/제안/협업 관련 coldmail인지 판단하시오. -제목: {subject} -발신자: {sender_email} - -응답: {"is_coldmail": true/false, "reason": "..."} -``` - -### 주의사항 -- JSON 마크다운 블록(```json) 제거 필요 -- gemini-2.5-flash-lite 사용 (비용 최소화) - ---- - -## Phase 3: 하이브리드 통합 (3시간) - -### 신규 파일 -- `rb8001/app/services/coldmail_hybrid_filter.py` - -### 주요 함수 -- `hybrid_coldmail_filter()`: 3단계 순차 실행 -- `update_from_feedback()`: Slack 버튼 피드백 처리 - -### 반환 구조 -```python -{ - "stage": "embedding" | "llm" | "hybrid", - "embedding_similarity": 0.75, - "llm_reason": "투자 유치 관련 IR 자료", - "naive_bayes_score": 0.85 -} -``` - ---- - -## Phase 4: coldmail_briefing 통합 (1시간) - -### 수정 파일 -- coldmail_briefing.py:121-136 - -### 변경 내용 -- 기존 `is_coldmail()` → `hybrid_coldmail_filter()` 교체 -- 로그에 분류 상세 정보 추가 -- `email["coldmail_details"]` 저장 - ---- - -## Phase 5: 테스트 및 검증 (2시간) - -### 테스트 케이스 - -**Success (coldmail)**: -1. "251013_올굿즈컴퍼니_회사소개서.pdf" from gomtose@naver.com -2. "2025 빅웨이브 하반기 IR 초대" from biigwave@ccei.kr -3. "투자제안서 검토 요청" from silkro2009@silkro.org - -**Failure (normal)**: -1. "[KBAN] 피싱 메일 주의" from jointips@kban.or.kr -2. "[SSG.COM] 주문 내역" from ssgadmin@ssg.com -3. "회의 일정 안내" from team@company.com - -### 검증 항목 -- [ ] 임베딩 클러스터 로딩 성공 -- [ ] LLM JSON 파싱 정상 -- [ ] Slack 피드백 → DB 반영 확인 -- [ ] 응답 시간 500ms 이내 -- [ ] API 호출 로그 확인 (10% 이하) - ---- - -## 구현 일정 - -| Phase | 작업 | 시간 | 우선순위 | -|-------|------|------|---------| -| 1 | KoBERT 임베딩 필터 | 4h | 높음 | -| 2 | Gemini LLM 분류기 | 2h | 중간 | -| 3 | 하이브리드 통합 | 3h | 높음 | -| 4 | coldmail_briefing 통합 | 1h | 높음 | -| 5 | 테스트 및 검증 | 2h | 높음 | - -**총 12시간** (1.5일) - ---- - -## 참고 - -- 2024년 연구: BERT F1 0.99 (Journal of Big Data) -- KoBERT: github.com/SKTBrain/KoBERT -- skill-embedding: localhost:8515 diff --git a/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md b/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md index 2f28dde..7fa039a 100644 --- a/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md +++ b/troubleshooting/251014_claude_coldmail_filter_tokenization_issue.md @@ -1,113 +1,88 @@ -# Coldmail 필터 토큰화 문제 분석 +# Coldmail 필터 토큰화 문제 및 하이브리드 구현 **날짜**: 2025-10-14 **작성자**: Claude (51124 서버 전담) -**관련 파일**: `rb8001/app/services/coldmail_filter.py`, `rb8001/app/scheduler/jobs/coldmail_briefing.py` +**관련 파일**: `rb8001/app/services/coldmail_filter.py`, `rb8001/tests/test_coldmail_filter.py` --- ## 문제 상황 -### 발단 9시 5분 Coldmail Daily Briefing 실행 시 IR deck 이메일이 Slack에 전송되지 않음. -### 조사 결과 -- 올굿즈컴퍼니 IR deck (`251013_올굿즈컴퍼니_회사소개서.pdf`): coldmail 확률 30.35% -- 빅웨이브 IR 행사 안내: coldmail 확률 7.65% -- 투자제안서 검토 요청: coldmail 확률 28.38% -- **모두 threshold 70% 미달로 필터링 실패** +**조사 결과**: +- 올굿즈컴퍼니 IR deck: coldmail 확률 30.35% (threshold 70% 미달) +- 빅웨이브 IR 행사: 7.65% +- 투자제안서 검토: 28.38% ---- +**근본 원인**: coldmail_filter.py:44-52 정규식 토큰화 실패 +- "회사소개서" → 하나의 토큰 (형태소 분리 안 됨) +- "ir에" → 하나의 토큰 (조사 분리 안 됨) +- DB의 "ir" (cold 90.9%)와 매칭 실패 -## 근본 원인: 토큰화 알고리즘 한계 - -### 현재 구현 -coldmail_filter.py:44-52 - 정규식 `re.split(r'[\s\W]+')` 사용 - -### 문제점 - -**1. 복합명사 분해 실패** -- `"회사소개서"` → 하나의 토큰 (형태소 분리 안 됨) -- DB에는 "회사", "소개서"로 학습되어 있으나 매칭 실패 - -**2. 조사 분리 실패** -- `"ir에"` → 하나의 토큰 -- DB의 "ir" (coldmail 90.9%)와 매칭 실패 - -**3. 테스트 결과** -``` -제목: "251013_올굿즈컴퍼니_회사소개서.pdf" -토큰: ['251013_올굿즈컴퍼니_회사소개서', 'pdf'] - -제목: "2025 빅웨이브 하반기 IR에 여러분을 초대합니다" -토큰: ['2025', '빅웨이브', 'biig', 'wave', '하반기', 'ir에', ...] -``` - -### 학습 데이터는 정상 -``` -총 단어 수: 56 -총 coldmail: 347, normal: 210 -주요 키워드: ir(90.9%), 투자(90.9%), 투자유치(100%), 제안(80%) -``` - -**테스트 스크립트**: `rb8001/tests/test_coldmail_filter.py` +**DB 학습 데이터는 정상** (56단어, cold 347, normal 210) --- ## 해결 방안: 3단계 하이브리드 -### 설계 철학 -- **속도**: KoBERT 임베딩으로 1차 필터링 (빠름, 90% 처리) -- **정확도**: Gemini LLM으로 경계선 케이스 판단 (높음, 10% 처리) -- **학습**: Naive Bayes로 실시간 피드백 반영 (지속 개선) +**설계**: +1. KoBERT 임베딩 (고속, 90% 필터링) - skill-embedding (8515) 활용 +2. Gemini LLM (정밀, 10% 처리) - zero-shot classification +3. Naive Bayes (실시간 학습) - Slack 피드백 반영 -### 아키텍처 -``` -이메일 수신 - ↓ -[1단계] KoBERT 임베딩 (고속) - - skill-embedding (8515) 활용 - - coldmail/normal 클러스터와 cosine similarity - - threshold 0.6 이상만 통과 - ↓ -[2단계] Gemini LLM (정밀) - - 1단계 통과한 10%만 처리 - - Zero-shot classification + 이유 설명 - ↓ -[3단계] Naive Bayes (학습) - - Slack 버튼 피드백 → DB 즉시 반영 - - 1단계 threshold 동적 조정 - ↓ -Slack Lists 등록 + 피드백 버튼 -``` - -**상세 구현**: `251014_claude_coldmail_hybrid_implementation.md` 참고 +**예상 효과**: +- 재현율: 30% → 95% +- 응답 속도: 500ms (1차 100ms + 2차 400ms) +- API 비용: 10% (1차 필터 효과) --- -## 기술 선택 근거 +## 구현 계획 (12시간) -### KoBERT (2024년 F1 0.99) -- 의미 기반 매칭 (동의어, 유사 표현 자동 처리) -- skill-embedding (8515) 기존 인프라 활용 -- 형태소 분석기 불필요 +### Phase 1: KoBERT 임베딩 필터 (4h) +**신규**: coldmail_embedding_filter.py +- create_embedding(): skill-embedding (8515)로 임베딩 생성 +- calculate_similarity(): cosine similarity +- is_coldmail_by_embedding(): threshold 0.6 -### Gemini LLM -- 즉시 사용 가능 (학습 데이터 불필요) -- 설명 가능성 (판단 이유 제공) -- 1차 필터 후 10%만 호출 → 비용 감당 가능 +**DB**: coldmail_embedding_clusters 테이블 (pgvector extension 필요) -### Naive Bayes 유지 -- 실시간 학습 가능 (Slack 피드백 → DB 즉시 반영) -- 빠른 추론 (임베딩/LLM 장애 시 백업) +### Phase 2: Gemini LLM 분류기 (2h) +**신규**: coldmail_llm_classifier.py +- classify_by_llm(): Gemini zero-shot + 이유 설명 +- JSON 마크다운 블록 전처리 + +### Phase 3: 하이브리드 통합 (3h) +**신규**: coldmail_hybrid_filter.py +- hybrid_coldmail_filter(): 3단계 순차 실행 +- update_from_feedback(): Slack 버튼 → 3모델 업데이트 + +### Phase 4: coldmail_briefing 통합 (1h) +**수정**: coldmail_briefing.py:121-136 +- is_coldmail() → hybrid_coldmail_filter() 교체 +- 분류 상세 정보 로그 추가 + +### Phase 5: 테스트 및 검증 (2h) +**테스트 케이스** (6개): +- Success: 올굿즈컴퍼니, 빅웨이브, 투자제안서 +- Failure: KBAN, SSG.COM, 회의 안내 + +**검증**: +- [ ] 임베딩 클러스터 로딩 +- [ ] LLM JSON 파싱 +- [ ] Slack 피드백 → DB 반영 +- [ ] 응답 시간 500ms 이내 +- [ ] API 호출 10% 이하 --- -## 예상 효과 +## 구현 완료 -- **재현율**: 30% → 95% (IR deck 놓치지 않음) -- **응답 속도**: 평균 500ms (1차 100ms + 2차 400ms) -- **API 비용**: 전체의 10%만 호출 (1차 필터 효과) +### 테스트 스크립트 (커밋 7c5b033) +**파일**: rb8001/tests/test_coldmail_filter.py +- test_hybrid_coldmail_filter():92-134 추가 +- --test hybrid 옵션 추가 --- @@ -115,14 +90,12 @@ Slack Lists 등록 + 피드백 버튼 ### 단순 정규식 토큰화의 한계 - 한국어는 교착어 특성상 조사/어미 분리 필수 -- 복합명사 분해 없이는 키워드 매칭 실패 - 교훈: 의미 기반 접근(임베딩)이 토큰 기반보다 강건함 ### 단일 모델의 위험성 -- Naive Bayes만으로는 형태소 문제 해결 불가 -- LLM만으로는 학습 불가능 + 비용 과다 +- Naive Bayes: 형태소 문제 해결 불가 +- LLM: 학습 불가능 + 비용 과다 - 교훈: 하이브리드 접근으로 각 모델의 장점 활용 ### 피드백 루프의 중요성 -- 모델 정확도는 사용자 피드백으로 지속 개선 - 교훈: 실시간 학습 가능한 구조가 장기적으로 유리