mirror of
https://github.com/hwanny1128/HGZero.git
synced 2025-12-06 11:26:25 +00:00
292 lines
7.6 KiB
Markdown
292 lines
7.6 KiB
Markdown
# 연관 회의록 조회 API 구현 문서
|
|
|
|
## 📋 개요
|
|
|
|
**준호** (Backend Developer)
|
|
|
|
회의록 ID를 기준으로 유사한 연관 회의록을 조회하는 API를 구현했습니다.
|
|
|
|
## 🎯 구현 방식: Option A (DB 조회 후 벡터 검색)
|
|
|
|
### 선택 이유
|
|
- ✅ **정확도 우선**: full_content 기반 검색으로 높은 정확도 보장
|
|
- ✅ **확장성**: 향후 full_content 기반 추가 기능 확장 용이
|
|
- ✅ **성능 최적화**: Redis 캐싱으로 DB 조회 오버헤드 최소화
|
|
|
|
### 처리 흐름
|
|
|
|
```
|
|
1. 요청 수신
|
|
↓
|
|
2. Redis 캐시 조회 (연관 회의록 결과)
|
|
├─ HIT → 즉시 반환
|
|
└─ MISS → 3단계로 진행
|
|
↓
|
|
3. Redis 캐시 조회 (회의록 full_content)
|
|
├─ HIT → 5단계로 진행
|
|
└─ MISS → 4단계로 진행
|
|
↓
|
|
4. DB 조회 (rag_minutes 테이블)
|
|
├─ full_content 획득
|
|
└─ Redis에 캐싱 (TTL: 30분)
|
|
↓
|
|
5. 벡터 임베딩 생성 (Azure OpenAI)
|
|
↓
|
|
6. 벡터 유사도 검색 (자기 자신 제외)
|
|
↓
|
|
7. 결과 Redis 캐싱 (TTL: 1시간)
|
|
↓
|
|
8. 응답 반환
|
|
```
|
|
|
|
## 🔧 구현 상세
|
|
|
|
### 1. 데이터 모델 추가
|
|
|
|
**파일**: `rag/src/models/minutes.py`
|
|
|
|
```python
|
|
class RelatedMinutesRequest(BaseModel):
|
|
"""연관 회의록 조회 요청"""
|
|
minute_id: str # 기준 회의록 ID
|
|
meeting_title: str # 회의 제목 (현재 미사용)
|
|
summary: str # 회의록 요약 (현재 미사용)
|
|
top_k: int = 5 # 반환할 최대 결과 수
|
|
similarity_threshold: float = 0.7 # 최소 유사도 임계값
|
|
|
|
class RelatedMinutesResponse(BaseModel):
|
|
"""연관 회의록 조회 응답"""
|
|
minutes: RagMinutes # 회의록 정보
|
|
similarity_score: float # 유사도 점수 (0.0 ~ 1.0)
|
|
```
|
|
|
|
### 2. DB 쿼리 함수 개선
|
|
|
|
**파일**: `rag/src/db/rag_minutes_db.py`
|
|
|
|
**수정 내용**: `search_by_vector()` 함수에 `exclude_minutes_id` 파라미터 추가
|
|
|
|
```python
|
|
def search_by_vector(
|
|
self,
|
|
query_embedding: List[float],
|
|
top_k: int = 5,
|
|
similarity_threshold: float = 0.7,
|
|
exclude_minutes_id: Optional[str] = None # 자기 자신 제외
|
|
) -> List[Dict[str, Any]]:
|
|
"""벡터 유사도 검색"""
|
|
# WHERE 절에 minutes_id != %s 조건 추가
|
|
# 자기 자신을 결과에서 제외
|
|
```
|
|
|
|
### 3. Redis 캐싱 유틸리티 구현
|
|
|
|
**파일**: `rag/src/utils/redis_cache.py`
|
|
|
|
**주요 기능**:
|
|
- `get(key)`: 캐시 조회
|
|
- `set(key, value, ttl)`: 캐시 저장
|
|
- `delete(key)`: 캐시 삭제
|
|
- `delete_pattern(pattern)`: 패턴 매칭 일괄 삭제
|
|
|
|
**특징**:
|
|
- Redis 연결 실패 시 자동으로 캐싱 비활성화 (서비스 장애 방지)
|
|
- JSON 직렬화/역직렬화 자동 처리
|
|
|
|
### 4. 설정 추가
|
|
|
|
**파일**: `rag/config.yaml`
|
|
|
|
```yaml
|
|
rag_minutes:
|
|
search:
|
|
top_k: 5
|
|
similarity_threshold: 0.7
|
|
cache:
|
|
ttl: 1800 # 회의록 조회 결과: 30분
|
|
prefix: "minutes:"
|
|
related_ttl: 3600 # 연관 회의록 검색 결과: 1시간
|
|
```
|
|
|
|
### 5. API 엔드포인트 구현
|
|
|
|
**파일**: `rag/src/api/main.py`
|
|
|
|
**엔드포인트**: `POST /api/minutes/related`
|
|
|
|
**Request Body**:
|
|
```json
|
|
{
|
|
"minute_id": "MIN-2025-001",
|
|
"meeting_title": "2025 Q1 마케팅 전략 회의",
|
|
"summary": "2025년 1분기 마케팅 전략 수립을 위한 회의",
|
|
"top_k": 5,
|
|
"similarity_threshold": 0.7
|
|
}
|
|
```
|
|
|
|
**Response**:
|
|
```json
|
|
[
|
|
{
|
|
"minutes": {
|
|
"meeting_id": "MTG-2024-050",
|
|
"title": "2024 Q4 마케팅 성과 분석",
|
|
"minutes_id": "MIN-2024-050",
|
|
"full_content": "...",
|
|
...
|
|
},
|
|
"similarity_score": 0.85
|
|
},
|
|
...
|
|
]
|
|
```
|
|
|
|
## ⚡ 성능 최적화
|
|
|
|
### 2단계 캐싱 전략
|
|
|
|
1. **회의록 조회 결과 캐싱** (TTL: 30분)
|
|
- 키: `minutes:{minute_id}`
|
|
- DB 조회 횟수 감소
|
|
|
|
2. **연관 회의록 검색 결과 캐싱** (TTL: 1시간)
|
|
- 키: `minutes:related:{minute_id}:{top_k}:{threshold}`
|
|
- 벡터 검색 및 임베딩 생성 비용 절감
|
|
|
|
### 예상 성능 개선
|
|
|
|
| 케이스 | 처리 시간 | 비고 |
|
|
|--------|----------|------|
|
|
| 캐시 MISS (최초 요청) | ~2-3초 | DB 조회 + 임베딩 생성 + 벡터 검색 |
|
|
| 회의록 캐시 HIT | ~1-2초 | 임베딩 생성 + 벡터 검색 |
|
|
| 연관 회의록 캐시 HIT | ~50ms | 캐시에서 즉시 반환 |
|
|
|
|
## 🔍 사용 예시
|
|
|
|
### Python (requests)
|
|
|
|
```python
|
|
import requests
|
|
|
|
url = "http://localhost:8000/api/minutes/related"
|
|
data = {
|
|
"minute_id": "MIN-2025-001",
|
|
"meeting_title": "2025 Q1 마케팅 전략 회의",
|
|
"summary": "2025년 1분기 마케팅 전략 수립",
|
|
"top_k": 5,
|
|
"similarity_threshold": 0.7
|
|
}
|
|
|
|
response = requests.post(url, json=data)
|
|
related_minutes = response.json()
|
|
|
|
for item in related_minutes:
|
|
print(f"제목: {item['minutes']['title']}")
|
|
print(f"유사도: {item['similarity_score']:.2f}")
|
|
print("---")
|
|
```
|
|
|
|
### curl
|
|
|
|
```bash
|
|
curl -X POST "http://localhost:8000/api/minutes/related" \
|
|
-H "Content-Type: application/json" \
|
|
-d '{
|
|
"minute_id": "MIN-2025-001",
|
|
"meeting_title": "2025 Q1 마케팅 전략 회의",
|
|
"summary": "2025년 1분기 마케팅 전략 수립",
|
|
"top_k": 5,
|
|
"similarity_threshold": 0.7
|
|
}'
|
|
```
|
|
|
|
## 🧪 테스트 시나리오
|
|
|
|
### 1. 정상 케이스
|
|
- ✅ 존재하는 minute_id로 요청
|
|
- ✅ 유사한 회의록 5개 반환
|
|
- ✅ 자기 자신은 결과에서 제외
|
|
|
|
### 2. 캐싱 동작 확인
|
|
- ✅ 최초 요청: 캐시 MISS → DB 조회
|
|
- ✅ 2번째 요청: 캐시 HIT → 즉시 반환
|
|
- ✅ TTL 경과 후: 캐시 MISS → 재조회
|
|
|
|
### 3. 에러 케이스
|
|
- ❌ 존재하지 않는 minute_id → 404 오류
|
|
- ❌ 잘못된 요청 형식 → 422 오류
|
|
|
|
## 📊 데이터베이스 영향
|
|
|
|
### 쿼리 패턴
|
|
|
|
```sql
|
|
-- 1. 회의록 조회 (캐시 MISS 시)
|
|
SELECT * FROM rag_minutes WHERE minutes_id = 'MIN-2025-001';
|
|
|
|
-- 2. 벡터 유사도 검색 (캐시 MISS 시)
|
|
SELECT *,
|
|
1 - (embedding <=> '{vector}'::vector) as similarity_score
|
|
FROM rag_minutes
|
|
WHERE embedding IS NOT NULL
|
|
AND 1 - (embedding <=> '{vector}'::vector) >= 0.7
|
|
AND minutes_id != 'MIN-2025-001' -- 자기 자신 제외
|
|
ORDER BY embedding <=> '{vector}'::vector
|
|
LIMIT 5;
|
|
```
|
|
|
|
### 인덱스 활용
|
|
|
|
- `minutes_id`: Primary Key 인덱스 활용
|
|
- `embedding`: pgvector 인덱스 활용 (IVFFlat 또는 HNSW)
|
|
|
|
## 🔐 보안 고려사항
|
|
|
|
1. **SQL Injection 방지**: psycopg2 파라미터 바인딩 사용
|
|
2. **Rate Limiting**: 향후 추가 권장 (분당 요청 수 제한)
|
|
3. **인증/인가**: 향후 JWT 토큰 기반 인증 추가 권장
|
|
|
|
## 🚀 향후 개선 사항
|
|
|
|
### 1. Hybrid 검색 지원 (Option B 통합)
|
|
- summary를 활용한 빠른 1차 검색
|
|
- full_content로 정밀한 2차 검색
|
|
- 적응형 임계값 조정
|
|
|
|
### 2. 성능 모니터링
|
|
- 캐시 히트율 추적
|
|
- API 응답 시간 측정
|
|
- 벡터 검색 성능 분석
|
|
|
|
### 3. 검색 품질 개선
|
|
- 시간 가중치 적용 (최근 회의록 우선)
|
|
- 부서/팀별 필터링 옵션
|
|
- 주제별 카테고리 분류
|
|
|
|
## 📝 파일 변경 목록
|
|
|
|
| 파일 | 변경 유형 | 설명 |
|
|
|------|----------|------|
|
|
| `rag/src/models/minutes.py` | 수정 | Request/Response 모델 추가 |
|
|
| `rag/src/db/rag_minutes_db.py` | 수정 | exclude_minutes_id 파라미터 추가 |
|
|
| `rag/src/utils/redis_cache.py` | 신규 | Redis 캐싱 유틸리티 |
|
|
| `rag/src/api/main.py` | 수정 | API 엔드포인트 추가 |
|
|
| `rag/config.yaml` | 수정 | rag_minutes 설정 추가 |
|
|
|
|
## ✅ 구현 완료 체크리스트
|
|
|
|
- [x] Request/Response 모델 정의
|
|
- [x] DB 쿼리 함수 개선 (자기 자신 제외)
|
|
- [x] Redis 캐싱 유틸리티 구현
|
|
- [x] API 엔드포인트 구현
|
|
- [x] 2단계 캐싱 전략 적용
|
|
- [x] 설정 파일 업데이트
|
|
- [x] 문서 작성
|
|
|
|
---
|
|
|
|
**작성자**: 준호 (Backend Developer)
|
|
**작성일**: 2025-10-29
|
|
**버전**: 1.0.0
|