# Coldmail IR 분석 실패 해결 - 중복 파일 처리 개선 **날짜**: 2025-10-16 **작성자**: Claude (51124 서버 전담) **커밋**: skill-rag-file cea055c **관련 문서**: 251015_claude_coldmail_ir_analysis_failure.md --- ## 문제 요약 Coldmail 브리핑 워크플로우에서 동일한 PDF 파일이 재업로드될 때 `400 - File already exists for this team` 에러 발생하여 전체 IR 분석 실패. ### 연쇄 효과 ``` PDF 업로드 실패 (400 에러) ↓ document_id = None ↓ RAG 검색 0건 ↓ IR 분석 "N/A" 반환 ↓ Slack Lists API 에러 ``` --- ## 원인 분석 **위치**: `skill-rag-file/app/api/upload.py:67-71` **기존 코드**: ```python if result.scalar(): raise HTTPException( status_code=400, detail="File already exists for this team" ) ``` **문제점**: - 중복 파일 발견 시 HTTPException 발생 - document_id 반환 없음 - 워크플로우 중단 - **멱등성(idempotency) 미보장**: 동일 요청을 여러 번 실행할 때 결과가 달라짐 --- ## 해결 방법 ### 코드 수정 **위치**: `skill-rag-file/app/api/upload.py:60-79` ```python # Check if file already exists for this team from sqlalchemy import select stmt = select(TeamDocument).where( TeamDocument.team_id == team_id, TeamDocument.file_hash == file_hash ) result = await db.execute(stmt) existing_doc = result.scalar_one_or_none() if existing_doc: # 중복 파일 발견 - 기존 document_id 반환 (멱등성 보장) logger.info(f"File already exists (hash: {file_hash}), returning existing document_id: {existing_doc.id}") return FileUploadResponse( document_id=existing_doc.id, filename=existing_doc.filename, file_hash=existing_doc.file_hash, storage_path=existing_doc.storage_path, chunk_count=existing_doc.chunk_count or 0, message=f"File already exists, returned existing document_id: {existing_doc.id}" ) ``` ### 핵심 변경사항 1. **select(TeamDocument.id) → select(TeamDocument)**: 전체 레코드 조회 2. **HTTPException 제거**: 에러 대신 기존 데이터 반환 3. **FileUploadResponse 반환**: 기존 document_id와 메타데이터 제공 4. **INFO 로그 추가**: 중복 감지 로그 기록 --- ## 효과 ### 1. 멱등성 보장 **Before**: ``` 1st upload: 성공 → document_id: abc-123 2nd upload: 실패 → 400 에러 ``` **After**: ``` 1st upload: 성공 → document_id: abc-123 2nd upload: 성공 → document_id: abc-123 (동일) 3rd upload: 성공 → document_id: abc-123 (동일) ``` ### 2. Coldmail 워크플로우 안정성 ``` PDF 업로드 (멱등성 보장) ↓ document_id 항상 획득 ✅ ↓ RAG 검색 정상 작동 ✅ ↓ IR 분석 정확한 데이터 ✅ ↓ Slack Lists API 성공 ✅ ``` ### 3. 재시도 안전성 네트워크 오류, 타임아웃 등으로 재시도 시에도 안전하게 작동: - 1차 시도: 파일 저장, 벡터 생성 (느림) - 2차 시도: 기존 document_id 즉시 반환 (빠름) --- ## 배포 ### 커밋 정보 ```bash cd /home/admin/ivada_project/skill-rag-file git add app/api/upload.py git commit -m "Fix duplicate file handling in upload API Change duplicate file behavior from HTTPException to idempotent response. When same file (hash) uploaded by team, return existing document_id instead of raising 400 error. This ensures coldmail workflow continues when PDFs are re-uploaded. Fixes: Coldmail IR analysis failure due to PDF upload rejection" git push origin main ``` **커밋 해시**: cea055c ### 배포 확인 ```bash docker compose down && docker compose up -d --build # 상태 확인 docker ps --filter name=skill-rag-file # NAMES STATUS # skill-rag-file Up 2 minutes (healthy) # 로그 확인 docker logs skill-rag-file --tail 20 # INFO: Application startup complete. # INFO: Uvicorn running on http://0.0.0.0:8508 ``` --- ## 검증 계획 ### 즉시 검증 (수동) 1. **동일 파일 2회 업로드 테스트**: ```bash # 1차 업로드: 성공, document_id 획득 # 2차 업로드: 성공, 동일 document_id 획득 ``` 2. **로그 확인**: ```bash docker logs skill-rag-file | grep "File already exists" # 예상: INFO - File already exists (hash: ...), returning existing document_id: ... ``` ### 실전 검증 (내일 09:05) **Coldmail 브리핑 실행 시**: 1. PDF 업로드 성공 여부 2. IR 분석 "N/A" 아닌 실제 데이터 반환 여부 3. Slack Lists 전송 성공 여부 **로그 체크 포인트**: ```bash # rb8001 로그 (09:05 이후) docker logs rb8001 --since "2025-10-17T09:00:00" | grep -iE "upload|document_id" # skill-rag-file 로그 docker logs skill-rag-file --since "2025-10-17T09:00:00" | grep "File already exists" ``` --- ## 관련 이슈 해결 상태 ### 해결됨 ✅ 1. **중복 파일 처리 개선**: HTTPException → 기존 document_id 반환 2. **멱등성 보장**: 동일 요청 여러 번 실행 가능 3. **Coldmail 워크플로우 안정성**: PDF 업로드 실패로 인한 전체 실패 방지 ### 향후 개선 사항 1. **Slack Lists 동적 컬럼 매핑**: - 현재: 하드코딩된 컬럼 ID (Col09HQTDUM0T 등) - 개선: Lists 컬럼 조회 API + 컬럼명 기반 동적 매핑 2. **LangGraph 체크포인트 + 에러 복구**: - 현재: Stateless 실행 - 개선: PostgresSaver 체크포인트 + 실패 지점부터 재시도 - LLM 기반 에러 페이로드 수정 및 재전송 --- ## 교훈 ### 1. 멱등성은 필수 (Idempotency is Critical) **원칙**: 동일 요청을 여러 번 실행해도 결과가 같아야 함 **적용**: - POST 요청이라도 리소스 생성/조회에는 멱등성 필요 - 중복 체크 시 "에러"가 아닌 "기존 리소스 반환"이 정답 - 네트워크 불안정, 재시도 상황에서 안전성 보장 ### 2. Cascading Failure 방지 **문제**: 한 단계 실패 → 전체 워크플로우 실패 **해결**: - 각 단계의 실패 격리 - 재시도 가능한 설계 - Graceful degradation (부분 성공 허용) ### 3. 에러 메시지의 의미 **"File already exists"는 에러가 아니다**: - 상태 확인 메시지로 취급 - 이미 존재하는 리소스 정보 반환 - 클라이언트는 정상 처리 가능 ### 4. 프레임워크 활용의 실효성 **LangGraph 도입 vs 실제 활용**: - 체크포인트 없는 LangGraph = 코드 정리 효과만 - 핵심 기능(체크포인트, 재시도)까지 구현해야 프레임워크 가치 발휘 - 점진적 개선: 멱등성 → 체크포인트 → LLM 복구 --- ## 다음 단계 ### 즉시 수행 - [x] 코드 수정 및 배포 - [x] Docker 재시작 확인 - [x] 헬스체크 정상 확인 ### 내일 확인 (2025-10-17 09:05) - [ ] Coldmail 브리핑 실행 성공 여부 - [ ] PDF 업로드 로그에서 "File already exists" 메시지 확인 - [ ] IR 분석 결과에 실제 데이터 포함 확인 - [ ] Slack Lists API 전송 성공 확인 ### 향후 개선 - [ ] Slack Lists 동적 컬럼 매핑 구현 - [ ] LangGraph 체크포인트 활성화 - [ ] LLM 기반 에러 복구 로직 추가 --- ## 결론 ✅ **Coldmail IR 분석 실패 해결 완료** **2025-10-16, skill-rag-file 중복 파일 처리 개선 배포** ### 핵심 성과 1. **멱등성 보장**: 동일 파일 재업로드 시 기존 document_id 반환 2. **워크플로우 안정성**: PDF 업로드 실패로 인한 전체 실패 방지 3. **재시도 안전성**: 네트워크 오류, 타임아웃 상황에서도 안전 4. **즉시 배포**: skill-rag-file 컨테이너 재시작 완료 ### 검증 대기 내일 09:05 Coldmail 브리핑 실행 시 실전 검증 예정.