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. 테스트 원칙
|
## 18. 테스트 원칙
|
||||||
|
|
||||||
- **실제 테스트 필수**: 코드 수정 후 추측하지 말고 실제로 테스트 (curl, Slack 직접 사용, DB 조회)
|
- **실제 테스트 필수**: 코드 수정 후 추측하지 말고 실제로 테스트 (curl, Slack 직접 사용, DB 조회)
|
||||||
|
- **컨테이너 테스트 파일 반영**: tests 디렉토리는 볼륨 마운트 아님 (빌드 시 COPY). 새 테스트 파일 추가 시 `docker cp [파일경로] [컨테이너명]:/code/tests/` 사용 또는 컨테이너 재빌드
|
||||||
|
|
||||||
## 19. 리팩토링 시 로직 상실 방지 원칙
|
## 19. 리팩토링 시 로직 상실 방지 원칙
|
||||||
|
|
||||||
|
|||||||
@ -131,6 +131,21 @@ pytest tests/test_*.py -v
|
|||||||
grep -l "import pytest\|@pytest" tests/test_*.py
|
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. 테스트 네이밍 및 구조
|
## 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