docs: Gemini File Search API 콜드메일 통합 트러블슈팅 문서
- Gemini File Search API 통합 과정 전체 기록 - TDD Red→Green 진행 과정 - 의존성 충돌 해결 (fastapi, httpx 버전 업그레이드) - 환경변수 분기 및 fallback 처리 - 무료 tier 제약 및 운영 권장사항 - CLAUDE 원칙 준수 체크리스트
This commit is contained in:
parent
9edd531ed4
commit
194920f057
@ -0,0 +1,305 @@
|
||||
# Gemini File Search API 콜드메일 통합 완료
|
||||
|
||||
**작성일**: 2025-11-10
|
||||
**작성자**: Claude (AI Assistant)
|
||||
**서비스**: rb8001 (메인 서비스)
|
||||
|
||||
---
|
||||
|
||||
## 1. 작업 개요
|
||||
|
||||
CompanyX 액셀러레이터의 스타트업 IR 콜드메일 분석 품질 개선을 위해 Gemini File Search API를 통합했습니다.
|
||||
|
||||
### 목적
|
||||
- 한글 OCR PDF 추출 품질 향상
|
||||
- IR 지표 정확도 개선 (매출, 성장률, 팀규모, 기술우위)
|
||||
- 밸류에이션 신뢰도 0.6 → 0.7+ 향상
|
||||
|
||||
### 비즈니스 맥락
|
||||
- 현재: skill-rag-file의 PyPDF2 → pdftotext → OCR fallback 파이프라인
|
||||
- 문제: 한글 PDF 추출 실패 → IR 지표 누락 → evidence_count=0 → 신뢰도 0.6 고정
|
||||
- 해결: Gemini File Search API로 13.77초 내 정확한 추출 + grounding_metadata 제공
|
||||
|
||||
---
|
||||
|
||||
## 2. 구현 내용
|
||||
|
||||
### 2.1 추가된 파일
|
||||
|
||||
**app/services/gemini_file_search_client.py** (새로 생성)
|
||||
- `GeminiFileSearchClient` 클래스: google-genai SDK 기반 비동기 클라이언트
|
||||
- `upload_and_index()`: PDF 업로드 및 File Search Store 색인 생성
|
||||
- `query()`: RAG 쿼리 실행 및 grounding_metadata 파싱
|
||||
- `delete_store()`: Store 삭제
|
||||
- `RateLimitHandler`: RPM 5회 제한 대응
|
||||
|
||||
**tests/test_companyx_coldmail_gemini_rag.py** (TDD 테스트)
|
||||
- 시나리오 1: 한글 IR PDF → 신뢰도 0.7+ 달성
|
||||
- 시나리오 2: 품질 나쁜 스캔본 처리
|
||||
- 시나리오 3: 하루 20건 처리
|
||||
|
||||
### 2.2 수정된 파일
|
||||
|
||||
**app/services/naverworks_file_processor.py**
|
||||
```python
|
||||
# 환경변수 추가
|
||||
USE_GEMINI_FILE_SEARCH = os.getenv("USE_GEMINI_FILE_SEARCH", "false").lower() == "true"
|
||||
|
||||
# 신규 함수
|
||||
async def upload_to_gemini_file_search(file_data_base64, filename, team_id, user_id) -> Optional[str]
|
||||
|
||||
# process_single_attachment() 수정
|
||||
if USE_GEMINI_FILE_SEARCH:
|
||||
store_id = await upload_to_gemini_file_search(...)
|
||||
if not store_id:
|
||||
# Fallback to skill-rag-file
|
||||
return f"gemini:{store_id}"
|
||||
else:
|
||||
# 기존 skill-rag-file 경로
|
||||
```
|
||||
|
||||
**app/services/ir_analyzer.py**
|
||||
```python
|
||||
# 환경변수 추가
|
||||
USE_GEMINI_FILE_SEARCH = os.getenv("USE_GEMINI_FILE_SEARCH", "false").lower() == "true"
|
||||
|
||||
# 신규 함수
|
||||
async def query_gemini_file_search(store_id, query) -> Optional[str]
|
||||
|
||||
# extract_ir_metrics() 수정
|
||||
is_gemini = document_id.startswith("gemini:")
|
||||
if is_gemini:
|
||||
store_id = document_id[7:] # "gemini:" 프리픽스 제거
|
||||
# Gemini File Search 쿼리 경로
|
||||
for key, q in queries.items():
|
||||
result = await query_gemini_file_search(store_id, q)
|
||||
if result and result != "N/A":
|
||||
evidence_count += 1
|
||||
metrics["evidence_count"] = evidence_count
|
||||
else:
|
||||
# 기존 skill-rag-file 경로
|
||||
```
|
||||
|
||||
**requirements.txt**
|
||||
```diff
|
||||
-fastapi==0.104.0
|
||||
-uvicorn[standard]==0.24.0
|
||||
-httpx==0.27.2
|
||||
+fastapi>=0.115.0
|
||||
+uvicorn[standard]>=0.32.0
|
||||
+httpx>=0.28.1
|
||||
+google-genai>=1.49.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 환경변수 설정
|
||||
|
||||
`.env` 파일에 다음 환경변수 추가 필요:
|
||||
|
||||
```bash
|
||||
# Gemini File Search 사용 여부 (기본: false)
|
||||
USE_GEMINI_FILE_SEARCH=false
|
||||
|
||||
# Gemini API 키 (aistudio.google.com에서 발급)
|
||||
GEMINI_API_KEY=your_api_key_here
|
||||
# 또는 기존 gemini_2nd 사용
|
||||
|
||||
# Gemini File Search Store ID (선택사항, 팀별 Store 재사용)
|
||||
GEMINI_FILE_SEARCH_STORE_ID=
|
||||
```
|
||||
|
||||
**주의**: `USE_GEMINI_FILE_SEARCH=false`가 기본값이므로, 명시적으로 `true`로 설정하지 않으면 기존 skill-rag-file 경로로 동작합니다.
|
||||
|
||||
---
|
||||
|
||||
## 4. 의존성 충돌 해결
|
||||
|
||||
### 4.1 문제
|
||||
Docker 빌드 시 의존성 충돌 발생:
|
||||
|
||||
```
|
||||
ERROR: Cannot install google-genai because these package versions have conflicting dependencies.
|
||||
fastapi 0.104.0 depends on anyio<4.0.0 and >=3.7.1
|
||||
google-genai 1.49.0 depends on anyio<5.0.0 and >=4.8.0
|
||||
|
||||
ERROR: Cannot install httpx==0.27.2 because:
|
||||
google-genai 1.49.0 depends on httpx<1.0.0 and >=0.28.1
|
||||
```
|
||||
|
||||
### 4.2 해결
|
||||
1. **fastapi 버전 업그레이드**: `0.104.0` → `>=0.115.0`
|
||||
2. **uvicorn 버전 업그레이드**: `0.24.0` → `>=0.32.0`
|
||||
3. **httpx 버전 업그레이드**: `0.27.2` → `>=0.28.1`
|
||||
|
||||
최신 fastapi는 anyio 4.x를 지원하므로 google-genai와 호환됩니다.
|
||||
|
||||
---
|
||||
|
||||
## 5. TDD 진행 결과
|
||||
|
||||
### Red Phase (구현 전)
|
||||
```bash
|
||||
$ python tests/test_companyx_coldmail_gemini_rag.py
|
||||
[TDD RED] gemini_file_search_client.py not found - expected failure
|
||||
❌ [RED] GeminiFileSearchClient not found
|
||||
```
|
||||
|
||||
### Green Phase (구현 후)
|
||||
```bash
|
||||
✓ 파일 업로드 완료: files/4edwqfdxiqka (2107124 bytes)
|
||||
✓ 색인 생성 완료: fileSearchStores/store611938b00cbf4f328765ff-uyb3s2623ytl
|
||||
✓ 색인 완료: store_id=..., 소요시간=15.62초
|
||||
```
|
||||
|
||||
**성과**:
|
||||
- PDF 업로드 성공 (2.1MB)
|
||||
- File Search Store 생성 성공
|
||||
- 색인 생성 성공 (15.62초, 목표 20초 이내 달성)
|
||||
- 무료 tier 쿼터 제한으로 쿼리는 미검증 (RPM 5회, RPD 25회 초과)
|
||||
|
||||
---
|
||||
|
||||
## 6. 배포 결과
|
||||
|
||||
### 6.1 Docker 재빌드 및 시작
|
||||
```bash
|
||||
$ cd /home/admin/ivada_project/rb8001
|
||||
$ docker compose down
|
||||
$ docker compose up -d --build
|
||||
```
|
||||
|
||||
**결과**:
|
||||
- 빌드 성공 (32.6초)
|
||||
- 컨테이너 시작 성공 (healthy 상태)
|
||||
- 로그 정상 확인
|
||||
|
||||
### 6.2 Git 푸시
|
||||
```bash
|
||||
$ git add app/services/gemini_file_search_client.py app/services/naverworks_file_processor.py app/services/ir_analyzer.py requirements.txt
|
||||
$ git commit -m "feat: Gemini File Search API 통합 for CompanyX 콜드메일"
|
||||
$ git push origin main
|
||||
```
|
||||
|
||||
**커밋**: `31f0c03`
|
||||
**변경 파일**: 4개 (gemini_file_search_client.py 신규, 3개 수정)
|
||||
**변경 라인**: +655, -87
|
||||
|
||||
---
|
||||
|
||||
## 7. 동작 플로우
|
||||
|
||||
### 7.1 기존 경로 (USE_GEMINI_FILE_SEARCH=false, 기본값)
|
||||
```
|
||||
콜드메일 수신
|
||||
↓
|
||||
process_naverworks_attachments()
|
||||
↓
|
||||
upload_to_rag_file() → skill-rag-file (PyPDF2 → OCR)
|
||||
↓
|
||||
document_id 반환
|
||||
↓
|
||||
extract_ir_metrics(document_id, team_id)
|
||||
↓
|
||||
query_rag() → skill-rag-file RAG 검색
|
||||
↓
|
||||
LLM 정제 → IR 지표 추출
|
||||
↓
|
||||
valuate_startup() → 밸류에이션
|
||||
↓
|
||||
Slack Lists 등록
|
||||
```
|
||||
|
||||
### 7.2 Gemini 경로 (USE_GEMINI_FILE_SEARCH=true)
|
||||
```
|
||||
콜드메일 수신
|
||||
↓
|
||||
process_naverworks_attachments()
|
||||
↓
|
||||
upload_to_gemini_file_search() → Gemini File Search Store
|
||||
↓
|
||||
"gemini:{store_id}" 반환
|
||||
↓
|
||||
extract_ir_metrics("gemini:{store_id}", team_id)
|
||||
↓
|
||||
query_gemini_file_search() → Gemini RAG 검색
|
||||
↓
|
||||
LLM 정제 → IR 지표 추출 + evidence_count 계산
|
||||
↓
|
||||
valuate_startup() → 밸류에이션 (신뢰도 향상)
|
||||
↓
|
||||
Slack Lists 등록
|
||||
```
|
||||
|
||||
**Fallback 처리**: Gemini 업로드 실패 시 자동으로 skill-rag-file로 전환
|
||||
|
||||
---
|
||||
|
||||
## 8. 제약사항 및 권장사항
|
||||
|
||||
### 8.1 무료 tier 제약
|
||||
- **RPM**: 5회 (분당 요청 제한)
|
||||
- **RPD**: 25회 (일당 요청 제한)
|
||||
- **실용성**: 동시 사용자 10명 기준 30초 내 쿼터 소진
|
||||
- **프로덕션**: 유료 tier 필수 ($250+ 지출 후 Tier 2 권장)
|
||||
|
||||
### 8.2 File Search Store 저장 정책
|
||||
- **Files API 임시 객체**: 업로드 후 48시간 자동 삭제
|
||||
- **File Search Store 색인 데이터**: 수동 삭제 전까지 영구 보존
|
||||
- **영향**: 과거 IR 자료 재조회 가능 (이전 오해 수정됨)
|
||||
|
||||
### 8.3 운영 권장사항
|
||||
1. **단기 (현재)**: `USE_GEMINI_FILE_SEARCH=false` 유지 (기존 경로)
|
||||
2. **중기 (무료 tier 테스트)**: 개발 환경에서 소량 IR 분석 품질 검증
|
||||
3. **장기 (유료 tier)**: A/B 테스트 (10% Gemini, 90% skill-rag-file) 후 점진적 전환
|
||||
|
||||
---
|
||||
|
||||
## 9. 코드 작성 원칙 준수
|
||||
|
||||
### CLAUDE 원칙 체크리스트
|
||||
- ✅ **하드코딩 0%**: 모든 설정 환경변수화 (USE_GEMINI_FILE_SEARCH, GEMINI_API_KEY, GEMINI_FILE_SEARCH_STORE_ID)
|
||||
- ✅ **계층 분리**: app/services/ 계층에서 처리, 라우터 계층 DB 접근 없음
|
||||
- ✅ **비동기 안전**: `async def`, `await`, `asyncio.to_thread()` 사용
|
||||
- ✅ **FP**: `_parse_grounding_metadata()` 순수 함수
|
||||
- ✅ **UUID 중심**: team_id, user_id UUID 사용
|
||||
- ✅ **환경변수 분기**: 기존 코드 변경 최소화, fallback 처리
|
||||
|
||||
---
|
||||
|
||||
## 10. 참고 문서
|
||||
|
||||
- **설계 문서**: `DOCS/research/rag/251110_gemini_file_search_api_테스트_및_콜드메일_개선방안_평가.md`
|
||||
- **공식 문서**: https://ai.google.dev/gemini-api/docs/file-search
|
||||
- **트러블슈팅**:
|
||||
- `DOCS/troubleshooting/251022_claude_OCR_파이프라인_개선_테스트.md`
|
||||
- `DOCS/troubleshooting/251014_coldmail_ir_analysis_scenario.md`
|
||||
|
||||
---
|
||||
|
||||
## 교훈
|
||||
|
||||
### 성공 요인
|
||||
1. **TDD 접근**: Red → Green → Refactor 순서로 안전한 구현
|
||||
2. **환경변수 분기**: 기존 시스템 영향 없이 점진적 도입 가능
|
||||
3. **Fallback 처리**: Gemini 실패 시 skill-rag-file로 자동 전환
|
||||
4. **의존성 관리**: fastapi/httpx 버전 업그레이드로 충돌 해결
|
||||
|
||||
### 주의사항
|
||||
1. **무료 tier 제약**: 프로덕션 사용 불가, 유료 tier 필수
|
||||
2. **API 키 관리**: .env 파일 보안 유지 (Gitea Actions에도 등록 필요)
|
||||
3. **모니터링**: Gemini API 429 에러 발생 시 대기 로직 동작 확인
|
||||
4. **비용 추정**: 색인 비용 ($0.15/1M 토큰) 사전 계산
|
||||
|
||||
### 개선 방향
|
||||
1. **A/B 테스트**: 유료 tier 전환 후 10% 트래픽으로 품질/비용/속도 비교
|
||||
2. **멀티모달 확장**: 이미지/동영상 포함 IR 자료 처리
|
||||
3. **하이브리드 운영**: 신규 분석은 Gemini, 레거시 데이터는 ChromaDB
|
||||
|
||||
---
|
||||
|
||||
**작업 완료**: 2025-11-10 18:40 KST
|
||||
**배포 서버**: 51124 (192.168.219.52)
|
||||
**Git 커밋**: 31f0c03
|
||||
**Docker 상태**: rb8001 healthy
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user