docs: 콜드메일 다중 확인 루프 트러블슈팅 및 테스트 원칙 보완
- 트러블슈팅 문서 추가: 260121_coldmail_multi_confirm_loop_fix.md - 계획 문서 archive 이동: 260121_coldmail_multi_confirm_and_summary_fix.md - 테스트 원칙 보완: 컨테이너 테스트 파일 반영 방법 (docker cp) - 백엔드 원칙 보완: 테스트 원칙에 컨테이너 반영 추가
This commit is contained in:
parent
d4be579021
commit
9c2df748b3
@ -363,6 +363,7 @@ utils
|
||||
## 18. 테스트 원칙
|
||||
|
||||
- **실제 테스트 필수**: 코드 수정 후 추측하지 말고 실제로 테스트 (curl, Slack 직접 사용, DB 조회)
|
||||
- **컨테이너 테스트 파일 반영**: tests 디렉토리는 볼륨 마운트 아님 (빌드 시 COPY). 새 테스트 파일 추가 시 `docker cp [파일경로] [컨테이너명]:/code/tests/` 사용 또는 컨테이너 재빌드
|
||||
|
||||
## 19. 리팩토링 시 로직 상실 방지 원칙
|
||||
|
||||
|
||||
@ -131,6 +131,21 @@ pytest tests/test_*.py -v
|
||||
grep -l "import pytest\|@pytest" tests/test_*.py
|
||||
```
|
||||
|
||||
### 컨테이너 테스트 파일 반영
|
||||
|
||||
**배경**: tests 디렉토리는 볼륨 마운트가 아님 (Dockerfile에서 `COPY ./tests /code/tests`로 빌드 시만 복사)
|
||||
|
||||
**새 테스트 파일 추가 시**:
|
||||
```bash
|
||||
# 방법 1: docker cp (빠름, 재빌드 불필요)
|
||||
docker cp tests/test_new_feature.py rb8001:/code/tests/
|
||||
|
||||
# 방법 2: 컨테이너 재빌드 (확실함)
|
||||
docker compose down && docker compose up -d --build
|
||||
```
|
||||
|
||||
**주의**: 호스트에서 테스트 파일 수정 후 컨테이너에 반영 안 되는 문제 주의
|
||||
|
||||
---
|
||||
|
||||
## 7. 테스트 네이밍 및 구조
|
||||
|
||||
@ -0,0 +1,95 @@
|
||||
# 콜드메일 다중 확인 루프 및 요약 메시지 개선
|
||||
|
||||
**작성일**: 2026-01-21
|
||||
**관련 파일**: `rb8001/app/services/workflows/coldmail_workflow.py`
|
||||
|
||||
---
|
||||
|
||||
## 문제 상황
|
||||
|
||||
1. **다중 이메일 확인 시 1개만 처리**: 오다가다, 에듀온 2개 모두 "맞음" 클릭 → 에듀온만 처리됨
|
||||
2. **요약 메시지 부적절**: `None억원 (신뢰도 0%)` 표시 → IR 분석 안 된 건 의미 없는 정보
|
||||
|
||||
---
|
||||
|
||||
## 아키텍처 (현재 vs 개선)
|
||||
|
||||
### 현재 (버그)
|
||||
|
||||
```
|
||||
wait_confirmation (interrupt)
|
||||
↓ 버튼 클릭 1회
|
||||
confirm → send → END ← 워크플로우 종료, 나머지 대기 이메일 유실
|
||||
```
|
||||
|
||||
- 현재 엣지: `confirm → send` (L352, 고정 엣지)
|
||||
|
||||
### 개선 (루프)
|
||||
|
||||
```
|
||||
wait_confirmation (interrupt)
|
||||
↓ 버튼 클릭
|
||||
confirm
|
||||
↓ 남은 대기 있으면
|
||||
↓ → wait_confirmation (다시 interrupt)
|
||||
↓ 없으면
|
||||
send → END
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 필요 작업
|
||||
|
||||
### Phase 1: 다중 확인 루프 구현 (TDD)
|
||||
|
||||
1. **Red**: `tests/test_coldmail_workflow_multi_confirm.py` 작성
|
||||
- 2개 이상 waiting_confirmation 시나리오
|
||||
- 첫 번째 confirm 후 다시 wait_confirmation으로 루프 확인
|
||||
2. **Green**: 코드 수정
|
||||
- `route_after_confirm()` 함수 추가 (L311 이후)
|
||||
- `waiting_confirmation` 남아있으면 `"wait_confirmation"` 반환
|
||||
- 없으면 `"send"` 반환
|
||||
- L352 `confirm → send` 고정 엣지를 조건부 엣지로 변경
|
||||
3. **Refactor**: 필요 시 정리
|
||||
|
||||
### Phase 2: 요약 메시지 개선 (TDD)
|
||||
|
||||
1. **Red**: 테스트 케이스 추가
|
||||
- `median`이 None인 경우 표시 문구 검증
|
||||
2. **Green**: `send_node` 수정 (L260-279)
|
||||
- `median`이 None이면 `"등록 완료 (IR 분석 대기)"` 표시
|
||||
3. **Refactor**: 필요 시 정리
|
||||
|
||||
---
|
||||
|
||||
## 예상 변경 범위
|
||||
|
||||
| 파일 | 변경 내용 |
|
||||
|------|----------|
|
||||
| coldmail_workflow.py L311~ | `route_after_confirm()` 함수 추가 |
|
||||
| coldmail_workflow.py L352 | `confirm → send` 조건부 엣지로 변경 |
|
||||
| coldmail_workflow.py L260-279 | `send_node` 요약 포맷 수정 (median None 처리) |
|
||||
| tests/test_coldmail_workflow_multi_confirm.py | 다중 확인 루프 테스트 (신규) |
|
||||
|
||||
---
|
||||
|
||||
## 검증 절차
|
||||
|
||||
### 단위 테스트
|
||||
```bash
|
||||
cd /home/admin/ivada_project/rb8001
|
||||
docker exec rb8001 pytest tests/test_coldmail_workflow_multi_confirm.py -v
|
||||
```
|
||||
|
||||
### E2E 테스트 (실제 Slack)
|
||||
1. 콜드메일 2건 이상 대기 상태 생성
|
||||
2. Slack에서 첫 번째 "맞음" 클릭 → 다시 interrupt 대기 확인
|
||||
3. 두 번째 "맞음" 클릭 → 요약 메시지 전송 확인
|
||||
|
||||
### 배포 후 확인
|
||||
```bash
|
||||
git push origin main
|
||||
# Gitea Actions 완료 대기
|
||||
docker ps | grep rb8001 # 재시작 확인
|
||||
docker logs rb8001 --tail 50 # 에러 확인
|
||||
```
|
||||
@ -0,0 +1,94 @@
|
||||
# 콜드메일 다중 확인 루프 및 요약 메시지 개선
|
||||
|
||||
**날짜**: 2026-01-21
|
||||
**작성자**: happybell80
|
||||
**관련 파일**: `rb8001/app/services/workflows/coldmail_workflow.py`
|
||||
|
||||
---
|
||||
|
||||
## 문제 상황
|
||||
|
||||
1. **다중 이메일 확인 시 1개만 처리**: 오다가다, 에듀온 2개 모두 "맞음" 클릭 → 에듀온만 처리됨
|
||||
2. **요약 메시지 부적절**: `None억원 (신뢰도 0%)` 표시 → IR 분석 안 된 건 의미 없는 정보
|
||||
|
||||
---
|
||||
|
||||
## 원인 분석
|
||||
|
||||
### 문제 1: 워크플로우 종료
|
||||
- `confirm → send` 고정 엣지로 인해 첫 확인 후 바로 요약 전송 → 종료
|
||||
- 나머지 `waiting_confirmation` 유실
|
||||
|
||||
### 문제 2: median None 처리 누락
|
||||
- `send_node`에서 `median`이 None인 경우 처리 로직 없음
|
||||
- `f"- {company}: {median}억원"` → `None억원` 표시
|
||||
|
||||
---
|
||||
|
||||
## 해결 방법
|
||||
|
||||
### Phase 1: 다중 확인 루프
|
||||
|
||||
```python
|
||||
# route_after_confirm() 함수 추가
|
||||
def route_after_confirm(state: ColdmailState) -> str:
|
||||
if state.get("waiting_confirmation"):
|
||||
return "wait_confirmation"
|
||||
else:
|
||||
return "send"
|
||||
|
||||
# confirm → send 고정 엣지를 조건부 엣지로 변경
|
||||
workflow.add_conditional_edges(
|
||||
"confirm",
|
||||
route_after_confirm,
|
||||
{
|
||||
"wait_confirmation": "wait_confirmation",
|
||||
"send": "send"
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
### Phase 2: 요약 메시지 개선
|
||||
|
||||
```python
|
||||
# median이 None이면 IR 분석 대기 표시
|
||||
if median is None:
|
||||
summary_lines.append(f"- {company}: 등록 완료 (IR 분석 대기)")
|
||||
elif is_hold:
|
||||
...
|
||||
else:
|
||||
summary_lines.append(f"- {company}: {median}억원 (신뢰도 {conf_pct}%)")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 변경 내용
|
||||
|
||||
| 파일 | 변경 |
|
||||
|------|------|
|
||||
| coldmail_workflow.py L311-325 | `route_after_confirm()` 함수 추가 |
|
||||
| coldmail_workflow.py L365-372 | `confirm → send` 조건부 엣지로 변경 |
|
||||
| coldmail_workflow.py L267-269 | `send_node` median None 처리 추가 |
|
||||
| tests/test_coldmail_workflow_multi_confirm.py | 5개 테스트 케이스 추가 |
|
||||
|
||||
---
|
||||
|
||||
## 검증
|
||||
|
||||
### 단위 테스트
|
||||
```bash
|
||||
docker exec rb8001 pytest tests/test_coldmail_workflow_multi_confirm.py -v
|
||||
# 5 passed
|
||||
```
|
||||
|
||||
### 배포
|
||||
- `git push origin main` → Gitea Actions 자동 배포
|
||||
- `docker ps | grep rb8001` → 재시작 확인 완료
|
||||
|
||||
---
|
||||
|
||||
## 교훈
|
||||
|
||||
1. **LangGraph 루프 패턴**: 조건부 엣지로 동일 노드 재진입 가능 (고정 엣지 대신 `route_after_*` 함수 사용)
|
||||
2. **None 값 처리 필수**: 외부 데이터(IR 분석 결과 등)는 None 가능성 항상 고려
|
||||
3. **테스트 컨테이너 반영**: tests 디렉토리는 볼륨 마운트 아님 → `docker cp` 또는 재빌드 필요
|
||||
Loading…
x
Reference in New Issue
Block a user