From 724ed609b29d504fa6c813980ea634bf50006056 Mon Sep 17 00:00:00 2001 From: Claude-51124 Date: Wed, 21 Jan 2026 09:27:02 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20=EC=BD=9C=EB=93=9C=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?interrupt=20=EC=83=81=ED=83=9C=20=EB=B3=B4=EC=A1=B4=20=EB=AC=B8?= =?UTF-8?q?=EC=A0=9C=20=ED=95=B4=EA=B2=B0=20=EB=AC=B8=EC=84=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - troubleshooting: 노드 분리 설계로 interrupt 상태 보존 문제 해결 - plans/archive: 완료된 계획 문서 이동 --- ...60121_coldmail_checkpoint_interrupt_fix.md | 74 +++++++++++++++++++ ...ldmail_interrupt_state_preservation_fix.md | 38 ++++++++++ 2 files changed, 112 insertions(+) create mode 100644 journey/plans/archive/260121_coldmail_checkpoint_interrupt_fix.md create mode 100644 journey/troubleshooting/260121_coldmail_interrupt_state_preservation_fix.md diff --git a/journey/plans/archive/260121_coldmail_checkpoint_interrupt_fix.md b/journey/plans/archive/260121_coldmail_checkpoint_interrupt_fix.md new file mode 100644 index 0000000..8115d65 --- /dev/null +++ b/journey/plans/archive/260121_coldmail_checkpoint_interrupt_fix.md @@ -0,0 +1,74 @@ +# 콜드메일 워크플로우 interrupt() 패턴 수정 계획 + +**작성일**: 2026-01-21 +**완료일**: 2026-01-21 +**관련 파일**: `rb8001/app/services/workflows/coldmail_workflow.py`, `rb8001/app/services/slack/coldmail_service.py` + +→ **구현 완료**: `troubleshooting/260121_coldmail_interrupt_state_preservation_fix.md` + +--- + +## 문제 상황 + +- `process_node`에서 `interrupt()` 호출 후에도 워크플로우가 `send_node`까지 진행 완료 +- `waiting_confirmation`이 비워진 상태로 checkpoint 저장 +- 사용자가 Slack 버튼 클릭 시 `Email not found in waiting_confirmation` 에러 발생 + +**근본 원인**: `interrupt()` 반환값을 사용하지 않고, 별도 라우팅으로 `confirm_node` 분기 시도 + +**히스토리**: +- 2025-12-23: `interrupt_after=["process"]` 사용 → process 노드 완료 후 중단 시도 +- 2026-01-18: LangGraph 1.0 업그레이드 시 `interrupt()` 동적 호출로 변경 +- **누락된 부분**: `decision = interrupt(payload)` 후 `decision`으로 분기하는 패턴 미적용 + +--- + +## 아키텍처 + +### 현재 (잘못된 패턴) + +``` +process_node → interrupt() 호출 → 워크플로우 계속 진행 → send_node → END + ↑ waiting_confirmation 비워짐 +``` + +### 수정 후 (올바른 패턴) + +``` +process_node → interrupt(payload) → 워크플로우 일시정지 + ↓ + 사용자 버튼 클릭 + ↓ + Command(resume={"confirmed": True, "email_id": ...}) + ↓ + interrupt() 반환값으로 결과 수신 → 후속 처리 +``` + +--- + +## 필요 작업 + +### Phase 1: process_node 수정 + +- `coldmail_workflow.py:106-157`: `interrupt()` 반환값 패턴 적용 + - `decision = interrupt({"emails": waiting_confirmation})` 형태로 변경 + - `decision`에서 `confirmed_email_id` 추출하여 처리 + - `interrupt()` 이후 코드가 재개 시점에 실행되도록 구조 변경 + +### Phase 2: coldmail_service.py 수정 + +- `coldmail_service.py:160`: `Command(resume=...)` 호출 시 사용자 입력 전달 + - `Command(resume={"confirmed": True, "email_id": email_id})` 형태로 변경 + +### Phase 3: confirm_node 제거 또는 통합 + +- `route_after_process` 분기 로직 제거 +- `confirm_node` 로직을 `process_node` 내부로 통합 (interrupt 반환값 처리) + +--- + +## 검증 방법 + +- 신뢰도 95% 미만 이메일 → Slack 버튼 전송 확인 +- 버튼 클릭 후 `Command(resume=...)` → 워크플로우 재개 확인 +- `waiting_confirmation` 상태 유지 및 정상 처리 확인 diff --git a/journey/troubleshooting/260121_coldmail_interrupt_state_preservation_fix.md b/journey/troubleshooting/260121_coldmail_interrupt_state_preservation_fix.md new file mode 100644 index 0000000..a21239c --- /dev/null +++ b/journey/troubleshooting/260121_coldmail_interrupt_state_preservation_fix.md @@ -0,0 +1,38 @@ +# 콜드메일 워크플로우 interrupt() 상태 보존 문제 해결 + +**날짜**: 2026-01-21 +**작성자**: happybell80 +**관련 파일**: `rb8001/app/services/workflows/coldmail_workflow.py`, `rb8001/app/services/slack/coldmail_service.py` + +--- + +## 문제 상황 + +- Slack 버튼 클릭 시 `Email not found in waiting_confirmation` 에러 발생 +- `interrupt()` 호출 후에도 워크플로우가 계속 진행되어 `waiting_confirmation`이 비워진 상태로 저장됨 + +## 원인 분석 + +- LangGraph 1.0의 `interrupt()`는 노드 실행을 즉시 중단시킴 +- 노드가 반환(return)되기 전에 `interrupt()`가 호출되면 해당 노드 내 상태 변경이 checkpoint에 저장되지 않음 +- 재개 시 노드가 처음부터 다시 실행됨 + +## 해결 방안 + +노드 분리 설계 적용: +- `coldmail_workflow.py:115-160`: `process_node` - 상태 업데이트 후 반환 (waiting_confirmation 저장) +- `coldmail_workflow.py:163-197`: `wait_confirmation_node` - interrupt() 호출하여 사용자 확인 대기 +- `coldmail_workflow.py:200-236`: `confirm_node` - 재개 후 confirmed_email_id 처리 + +워크플로우 플로우: `fetch → filter → process → wait_confirmation (interrupt) → confirm → send → END` + +## 테스트 결과 + +- `tests/test_coldmail_interrupt_return_pattern.py`: 4개 테스트 모두 통과 +- 기존 `tests/test_coldmail_workflow_interrupt.py`: 4개 테스트 모두 통과 + +## 교훈 + +1. **LangGraph interrupt 동작 이해**: `interrupt()` 호출 시 노드 반환 전이면 상태가 저장되지 않음 +2. **노드 분리 필수**: 상태 저장이 필요한 로직과 `interrupt()` 호출을 별도 노드로 분리 +3. **TDD 중요성**: 단위 테스트로 interrupt/resume 동작 검증 후 배포