This commit is contained in:
happybell80 2025-12-09 17:35:13 +09:00
commit 305e1afbac
2 changed files with 65 additions and 82 deletions

View File

@ -1,82 +0,0 @@
# rb8001 캘린더 조회/생성 의도 경계 세부 설계
**날짜**: 2025-12-09
**목표**: calendar_query vs calendar_event 경계 케이스를 UX/TDD 기준으로 명확히 정의하고, 3단계 의도 아키텍처와 일관되게 구현한다.
---
## 1. 현재 상태 정리
- FastPath 정규식이 `calendar_query``calendar_event`보다 우선 매칭하여, 조회·생성이 섞인 문장도 대부분 `calendar_query`로 고정됨.
- 의도 패턴: `rb8001/app/services/brain/decision_engine.py:120-180`
- 3단계 의도 파이프라인(Goal → ActionPlan → SkillSequence)은 도입되었지만, 캘린더 조회/생성 경계에는 아직 적극적으로 사용되지 않음.
- 메타데이터 파이프라인: `rb8001/app/services/brain/decision_engine.py:540-629`
- 기존 캘린더 테스트는 조회 vs 생성 분리, all-day 파싱, 승인 플로우에 초점.
- 조회 vs 생성 TDD: `rb8001/tests/test_calendar_query_vs_create.py:1-220`
- 로그 기반 시나리오: `rb8001/tests/test_intent_scenarios_from_logs.py:1-220`
- 겹침 케이스 실험 스크립트: `rb8001/tests/test_calendar_intent_overlap_cases.py:1-80`
---
## 2. UX/의도 기준
- **조회 중심 문장**: "등록돼 있는지 확인", "일정 전체 확인", "스케줄 보여줘" → 기본 intent는 `calendar_query`.
- **생성 중심 문장**: "일정 등록해줘/잡아줘/넣어줘" + 날짜·시간·타이틀 정보가 충분 → 기본 intent는 `calendar_event`.
- **조회+생성 혼합 문장**: "있나 확인하고, 없으면 잡아줘", "필요하면 등록해줘" → 단일 intent로 강제하지 않고, ① 조회 → ② 조건부 생성 멀티 액션으로 다룬다.
- UX 원칙: 애매하면 조용히 잘못 실행하지 말고, 한 번 더 물어보는 clarify를 사용한다.
---
## 3. 설계 방향
### 3.1 FastPath 의도 판정 조정
- 조회/생성 정규식이 **동시에 매칭**되는 경우를 감지한다.
- 이 경우 FastPath에서 바로 `calendar_query` 또는 `calendar_event`로 확정하지 않고, 상위 카테고리(예: `SCHEDULE_MANAGEMENT`) + 낮은 confidence로 내려 보낸다.
- `"없으면 잡아줘"`, `"필요하면 등록해줘"` 등 조건부 생성 패턴은 별도 플래그로 표시하여 후속 단계에서 ActionPlan에 반영한다.
### 3.2 3단계 파이프라인 활용
- IntentAnalyzer/ActionPlanner에서 조회+생성 혼합 메시지를 **Goal: SCHEDULE_MANAGEMENT**로 해석한다.
- ActionPlanner는 플래그/슬롯을 기준으로 다음 두 가지 플랜을 만든다.
- Plan A: `[QUERY_CALENDAR]` (조회만)
- Plan B: `[QUERY_CALENDAR, CONDITIONAL_CREATE_EVENT]` (없으면 등록)
- SkillSelector는 Plan B에 대해 `CALENDAR.get_events` 후, 결과가 비어 있을 때만 `CALENDAR.create_event`를 실행하는 시퀀스를 생성한다.
### 3.3 Clarify·승인 흐름
- FastPath/IntentAnalyzer에서 의도 불확실(conf 낮거나 혼합)하면, LLM 기반 clarify 메시지를 생성한다.
- 예: "먼저 내일 일정이 등록돼 있는지 확인하고, 없으면 일정까지 등록해드릴까요?"
- 사용자가 "응/ㅇㅇ/그래" 등으로 승인하면 `calendar_approval`이 원래 Plan A/B를 기억한 상태에서 적절한 액션을 선택하도록 한다.
---
## 4. 작업 항목 (하위 설계)
1. **의도 패턴/슬롯 확장**
- 조회/생성 동시 매칭, `"없으면 잡아줘"`, `"필요하면 등록해줘"` 등을 감지하는 보조 패턴/슬롯 추가.
- 위치: `rb8001/app/services/brain/decision_engine.py``app/services/brain/intent` 하위 모듈.
2. **FastPath 의사결정 로직 개선**
- `analyze_intent()`에서 캘린더 혼합 문장에 대해 단일 intent 강제 대신 상위 카테고리 + 플래그 반환.
3. **ActionPlanner 확장**
- `ActionPlanner.plan()`에서 SCHEDULE_MANAGEMENT + 플래그를 입력받아 `[QUERY]` vs `[QUERY→CONDITIONAL_CREATE]`를 구분.
- 위치: `rb8001/app/services/brain/intent/action_planner.py`.
4. **SkillSelector 멀티 액션 시퀀스**
- `SkillSelector.select()`에서 CONDITIONAL_CREATE_EVENT를 지원하고, get_events 결과에 따라 create_event 실행 여부를 결정.
- 위치: `rb8001/app/services/brain/intent/skill_selector.py`.
5. **calendar_approval 행동 분기**
- `DecisionEngine.decide_skill_sequence()` 또는 intent 파이프라인에서 `calendar_approval`이 직전 Plan A/B를 기억하도록 메타데이터 연결.
6. **의도 경계 TDD 케이스 추가**
- 위 10개 문장을 포함해 `"등록돼 있는지 확인"`, `"없으면 잡아줘"` 패턴을 `test_calendar_query_vs_create.py`, `test_intent_scenarios_from_logs.py`에 기대 intent/액션으로 고정.
7. **리뷰 큐 연동 규칙**
- calendar_query vs calendar_event margin이 작거나, 사용자 피드백(wrong/down)이 붙은 캘린더 케이스를 IntentReviewQueue에 자동 적재.
- 위치: `rb8001/app/services/brain/intent_review.py`, `app/state/conversation_repository.py`.
---
## 5. 검증·운영
- `pytest rb8001/tests/test_calendar_query_vs_create.py rb8001/tests/test_intent_scenarios_from_logs.py`로 회귀 여부를 상시 확인한다.
- 관리자용 Intent 리뷰 화면에서 캘린더 관련 샘플을 필터링하여, 조회↔생성 경계 오류를 집중 라벨링한다.
- 일정 기간 운영 후, 리뷰 큐/로그 데이터를 기반으로 FastPath 정규식·threshold를 재튜닝하고 이 계획 문서를 `troubleshooting/` 문서로 승격한다.

View File

@ -0,0 +1,65 @@
# rb8001 캘린더 조회/생성 의도 경계 FastPath 수정
**날짜**: 2025-12-09
**작성자**: Claude
**관련 파일**: `rb8001/app/services/brain/decision_engine.py:380-470`
---
## 문제 상황
- 사용자 발화가 조회·생성을 함께 암시하는 경우에도 FastPath 정규식이 한쪽 intent로 강하게 고정되면서 UX가 어색했다.
- 예: `"로빙아 내 구글 캘린더에 내일 12시 교육 일정 추가해줘."``calendar_query`로 분류되거나,
`"이번 주 일정 정리해서 보여주고, 안 잡힌 시간에 회의 하나 넣어줘."`가 단일 intent로만 처리됨.
- 상위 설계(3단계 의도 아키텍처, 하이브리드 의도 분석, HITL 리뷰 큐)는 존재하지만,
캘린더 FastPath(DecisionEngine) 레벨에서 조회/생성/삭제 경계가 충분히 정제되어 있지 않았다.
---
## 해결 방안
- FastPath에서 캘린더 관련 intent(CALENDAR_DELETE, CALENDAR_QUERY, CALENDAR_EVENT)를 별도 분기 처리하도록 수정.
- 삭제 패턴(CALENDAR_DELETE)은 항상 최우선으로 처리.
- 조회/생성 패턴이 둘 다 매칭되는 혼합 문장은 추가 규칙으로 분기.
- 조회/생성 혼합 문장에 대한 UX 기준을 명시:
- `"없으면"`, `"필요하면"`이 포함된 경우: **먼저 조회(intent=calendar_query)** 후 필요 시 생성하는 흐름으로 간주.
- `"등록해줘"`, `"추가해줘"`, `"넣어줘"`, `"잡아줘"` 등 명령형 생성 표현만 있는 경우: **생성(intent=calendar_event)** 우선.
- 그 밖의 혼합 문장은 기본적으로 조회(intent=calendar_query)로 처리.
- 캘린더 외 intent는 기존 intent_patterns 순서를 그대로 유지하되,
캘린더 세 가지 intent는 공통 분기 후 다른 intent와 독립적으로 처리.
### 수정 위치
- 의도 판정 로직 확장: `rb8001/app/services/brain/decision_engine.py:399-470`
- 기존 `priority_intents = [CALENDAR_DELETE, CALENDAR_QUERY]` 루프 제거.
- `calendar_delete_match`, `calendar_query_match`, `calendar_event_match`를 별도로 계산하고,
혼합 케이스에서 조건부/생성 키워드를 기준으로 분기.
- 이후 일반 intent 패턴 루프에서는 캘린더 intent 세 종류를 건너뛰도록 변경.
---
## 구현 결과
- UX 시나리오 20개를 기준으로 한 TDD 테스트 추가 (로컬 전용):
- 파일: `rb8001/tests/test_calendar_intent_overlap_tdd.py` (gitignore로 인해 리포지토리에는 포함하지 않음)
- 검증 범위:
- 순수 조회 문장: `calendar_query`
- 명시적 생성 문장: `calendar_event`
- `"없으면 잡아줘"`, `"필요하면 등록해줘"` 형태 혼합 문장: 기본 `calendar_query` (조회 우선)
- 삭제/정리 문장: `calendar_delete`
- 위 20개 케이스는 모두 DecisionEngine FastPath에서 기대 intent로 분류됨을 확인.
- `rb8001` 리포지토리에 변경 사항 커밋/푸시 후, Gitea Actions 자동 배포를 통해 51124 서버의 rb8001 컨테이너가 재시작되었음을 `docker ps | grep rb8001`로 검증.
---
## 교훈
- 상위 설계(3단계 의도 파이프라인, 하이브리드 의도 분석, HITL 리뷰 큐)만으로는
FastPath 정규식이 만드는 UX 문제를 막기 어렵다. 캘린더처럼 사용자 기대가 명확한 도메인에 대해서는
FastPath 레벨에서도 조회/생성/삭제 경계를 명시적으로 설계해야 한다.
- 계획 문서에는 ActionPlanner/SkillSelector·clarify·HITL까지 포함된 전체 작업이 적혀 있었지만,
실제 1차 배포에서는 **FastPath 캘린더 분기**만 안전하게 적용하는 식으로 단계를 나누는 것이 리스크 관리에 도움이 된다.
- TDD 방식으로 “문장 → 기대 intent”를 먼저 고정해 두면,
이후 ActionPlanner/SkillSelector·clarify·HITL를 추가로 도입하더라도
기존 캘린더 UX를 깨지 않고 개선 여부를 안정적으로 검증할 수 있다.