# rb8001-skill-rag-file Slack 파일 처리 연동 구조

## 작성일: 2025-09-16
## 작성자: Claude (51123 서버 관리자)

## 1. 현재 상황

### 확인된 상태
- **team_document 테이블**: 생성 완료 (51123 PostgreSQL)
  - processing_status, chunk_count, mime_type, file_size 컬럼 포함
  - idx_team_doc_hash 유니크 인덱스 생성됨
- **파일 저장 디렉터리**: /mnt/hdd/data/documents/ 생성 완료
- **slack_workspace**: 2개 팀 등록, bot_token 보유
  - Robeing (T0925SXPS4D)
  - Company-X Team (T09C98KB933)
- **Slack 스코프**: files:read 포함 (설치 URL에서 확인)

### 구현 완료 (2025-09-16)
- **rb8001**: 파일 처리 로직 추가됨
  - slack_handler.py:286-288: 파일 있으면 process_slack_files_async() 호출
  - slack_handler.py:50-97: Slack API로 파일 다운로드 후 skill-rag-file 전송
  - 임시 team_id 사용 중 (TODO: Slack team_id → DB UUID 매핑)

### skill-rag-file 구현 상태 (51124)
- **구현 완료**: app/main.py, api/{upload.py,search.py}, services/* (텍스트 추출/청킹/임베딩/ChromaDB)
- **DB 모델**: 실제 테이블과 동기화 완료 (file_size, mime_type, chunk_count, processing_status 추가)
- **기본 경로**: DOCUMENT_BASE_PATH=/mnt/51123data/documents (app/core/config.py)

## 2. 실제 메시지 플로우 (확인됨)

### 현재 Slack 메시지 처리 경로
```
Slack Event API
    ↓
nginx (51123)
    ↓
robeing-gateway (51123)
    - team_id에서 bot_token 조회 (slack_workspace 테이블)
    - Slack user ID → UUID 변환
    - 헤더 추가: X-Slack-Bot-Token, X-User-Id
    ↓
rb8001:8001 (51124)
    /api/slack/events
    - slack_endpoint.py → slack_handler.py
    - handle_slack_events()에서 파일 추출
    - asyncio.create_task(process_slack_message_async())로 비동기 처리
    ↓
brain_service/llm_service (파일 처리 없이 텍스트만 처리)

## 3. rb8001 파일 처리 현황 (51124)

### 구현 완료 코드
- **파일 추출**: rb8001/app/router/slack_handler.py:244
- **파일 처리 호출**: 286-288행에서 process_slack_files_async() 비동기 호출
- **파일 다운로드 및 전송**: 50-97행 process_slack_files_async() 함수
  - Slack API로 파일 다운로드 (bot_token 사용)
  - skill-rag-file:8508/api/upload로 전송
  - 임시 team UUID 사용 중

## 4. skill-rag-file 현황 (51124)

### API 엔드포인트 (구현됨)
- POST /api/upload: 파일 업로드
- POST /api/search: 검색 (GET 아님)
- GET /api/search/collections: 컬렉션 목록
- GET /healthz: 헬스체크

### 네트워크 연결 상태
- rb8001 → skill-rag-file:8508: 연결 가능 (확인됨)
- 컨테이너 네트워크: host 모드 사용

## 5. 데이터베이스 상태 (51123 PostgreSQL)

### team_document 테이블 상태
- 테이블과 모델 동기화 완료
- file_size, mime_type, chunk_count, processing_status 컬럼 포함
- 유니크 인덱스: idx_team_doc_hash (file_hash)

## 6. 확인 완료 사항
- ✅ team_document 테이블 생성됨 (코드 모델 동기화 완료)
- ✅ /mnt/hdd/data/documents/ 디렉터리 생성됨
- ✅ Slack bot_token 2개 팀 보유 (Robeing, Company-X Team)
- ✅ files:read 스코프 포함
- ✅ rb8001 → skill-rag-file:8508 네트워크 연결 가능
- ✅ rb8001 파일 처리 로직 구현됨
- ✅ skill-rag-file DB 모델 동기화 완료

## 7. 해결 완료 사항 (2025-09-16)

### PostgreSQL 권한 문제 해결
- **문제**: skill-rag-file (51124)이 team_document 테이블 접근 시 permission denied
- **원인**: team_document 테이블이 51123 PostgreSQL에 있고 권한 없음
- **해결**:
  1. skill-rag-file .env에 51123 PostgreSQL 연결 정보 추가
     - `DATABASE_URL=postgresql+asyncpg://robeings:password@192.168.219.45:5432/main_db`
  2. PostgreSQL 권한 부여: `GRANT ALL PRIVILEGES ON TABLE team_document TO robeings;`
  3. 테이블 소유자 변경: `ALTER TABLE team_document OWNER TO robeings;`
  4. 모든 주요 테이블 소유자 robeings로 통일 (company, team, user, slack_workspace, team_document)

## 8. 완료된 작업 (2025-09-16)

### Slack team_id → DB team UUID 매핑 구현
- rb8001: slack_workspace 테이블에서 slack_team_id로 team_id(UUID) 조회
- SessionLocal과 sqlalchemy.text 사용하여 DB 직접 조회

### UUID 매핑 로직 수정
- process_slack_message_async에서 UUID 우선 사용

### skill-rag-file SQLAlchemy 모델 수정
- team_id 컬럼에서 ForeignKey 제거 (DB 레벨 외래키는 유지)

### 파일 처리 테스트 결과
- 파일 저장: /mnt/51123data/documents/38bdc27d-cb01-4960-867e-41733d2f3529/2025-09/소버린 AI.md
- DB 저장: team_document 테이블 ID: 733c7c2a-09ad-47a2-b005-abb30bc427d0
- ChromaDB: rb8001_38bdc27d-cb01-4960-867e-41733d2f3529_documents 컬렉션 생성
- 1개 청크 처리
- API 응답: 200 OK

### 비동기 병렬 처리 구현 (2025-09-16)
- 문제: 큰 파일 처리 중 작은 파일 대기
- 해결: asyncio.Semaphore(3)로 최대 3개 동시 처리
- 결과: 전체 처리 시간 단축

## 9. 해결된 문제 (2025-09-17)

### 파일 처리 후 로빙 활용 문제 (해결됨)
- **현상**: 파일이 skill-rag-file로 저장되지만 로빙이 파일 내용 모름
- **원인**: process_slack_files_async와 process_slack_message_async가 독립 실행
- **해결**:
  - process_message_with_files_async() 함수 추가하여 파일 처리 완료 대기
  - skill-rag-file 검색 API 호출하여 파일 내용을 LLM context에 포함
  - 검색 API 파라미터 수정 (team_id 필수, document_ids 제거)

### ChromaDB 볼륨 마운트 (해결됨)
- **rb8001**: ./chroma_db → /code/chroma_db (상대 경로)
- **skill-rag-file**: ./chroma_db → /app/chroma_db (상대 경로)
- **해결**: 두 서비스 모두 상대 경로로 통일, SSHFS 충돌 방지

## 10. 해결된 문제 (2025-09-17)

### 파일 처리 즉시 응답 방식 개선
- **문제**: 파일 업로드와 질의가 별도 이벤트로 처리되면 파일 내용 인식 못함
- **원인**: context에 file_ids가 없으면 검색 경로 미실행
- **초기 해결 시도 문제**:
  - WebClient.api_call() 잘못 사용 (URL 직접 전달 불가)
  - requests 패키지 없음 (requirements.txt에 미포함)
  - 비동기 함수에서 동기 블로킹 코드 사용
- **최종 해결**:
  - httpx.AsyncClient 사용 (이미 설치됨)
  - 파일 텍스트를 먼저 메모리에 추출 (httpx로 비동기 다운로드)
  - context["file_contents"]로 즉시 LLM에 전달
  - skill-rag-file 업로드는 asyncio.create_task()로 백그라운드 처리
- **결과**:
  - ChromaDB 에러와 무관하게 즉시 파일 내용 활용 가능
  - 파일 다운로드 및 처리 성공
  - 로빙이 파일 내용 읽고 정확히 답변

### 파일 의도 분석 오류 (2025-09-17 01:42)
- **증상**: 파일 업로드시 "이메일 서비스 연결 실패" 오류
- **원인**:
  - router.py의 이메일 키워드 체크가 파일 내용까지 검사
  - DecisionEngine이 파일 내용의 "이메일" 단어로 EMAIL_SEND 분류
  - skill-email 서비스 호출 → 422 Unprocessable Entity
- **해결 시도**:
  - router.py의 이메일 키워드 체크 제거
  - DecisionEngine에 파일 감지 로직 추가 ("[파일:" in message)
  - DOCUMENT_ANALYSIS 우선순위 상향
- **남은 문제**: 패턴 매칭이 파일 내용까지 검사하는 구조적 문제


### ChromaDB 검색 구조 (API 통신)
- **현재 구조**:
  - rb8001: 자체 ChromaDB에서만 검색
  - skill-rag-file: 자체 ChromaDB에서만 검색
  - 서로 직접 폴더 참조 없음
- **문서 검색 플로우**:
  1. rb8001 → skill-rag-file API 호출 (http://localhost:8508/api/search)
  2. skill-rag-file이 자체 ChromaDB 검색 후 결과 반환
  3. rb8001이 API 응답 받아서 LLM context에 포함
- **결론**: API 통신으로 분리된 구조, 직접 폴더 참조 불필요