# 251114_admin_dynamic_intent_runtime_tests ## 개요 - **작성자**: admin - **작성일**: 2025-11-14 - **관련 서비스**: rb8001 (51124), PostgreSQL(main_db on 51123), DOCS - **목표**: 의도 레지스트리를 DB에서 동적으로 불러오도록 코드/문서 수정 후 `/api/test/router-message` 10건 시나리오 테스트 ## 문제 요약 - SemanticIntentClassifier가 YAML에만 의존해 신규 intent 추가 시 코드 변경과 재배포가 필요했고, 실제 로그 패턴을 DB에 넣어도 즉시 반영되지 않음. - 테스트 엔드포인트 `/api/test/router-message`로 주요 시나리오를 검증할 때 intent 필드가 항상 null로 표기되어 흐름 확인이 어려웠음. ## 해결/변경 사항 1. **코드** - `app/brain/intent_store.py`: `load_intents_db()` 추가로 `intents` 테이블에서 name/description을 로드 (inactive=FALSE 제외)하고, 프로토타입/threshold 없이도 안전한 폴백 구현. - `app/brain/semantic_classifier.py`: DB 레지스트리를 우선 사용하고, 프로토타입이 없으면 해당 registry에 맞춰 즉시 임베딩 생성. YAML은 완전한 폴백으로만 사용. - `tests/test_semantic_classifier_dynamic.py`: DB/폴백 경로를 각각 검증하는 단위 테스트 추가. 2. **문서** - `DOCS/300_architecture/database/tables.md`: `intents`, `intent_prototypes`, `intent_thresholds`, `intent_path_stats` 설명과 SemanticIntentClassifier가 이를 활용하는 흐름 추가. 3. **DB 확인** - 51123 PostgreSQL(main_db)에 접속해 intent 관련 테이블 스키마/데이터 확인 (`SELECT table_name FROM information_schema.tables WHERE table_schema='public'` 등). 4. **배포** - rb8001 / DOCS 각각 `main`에 푸시하여 자동 배포 → `docker ps`, `docker logs rb8001 --tail 50` 로 새 컨테이너 정상 기동 확인. ## 테스트 (`/api/test/router-message`) | # | 입력 | 응답 요약 | 비고 | |---|------|-----------|------| |1|“메일함이 엉망… 정리”|“응답을 생성할 수 없습니다.”|intent null (아직 스킬 미구현)| |2|“김팀장에게 일정 확인 메일 작성…”|“응답을 생성할 수 없습니다.”|intent null| |3|“며칠째 수신함 확인…”|“응답을 생성할 수 없습니다.”|intent null| |4|“요즘 테크 업계 소식…”|“응답을 생성할 수 없습니다.”|intent null| |5|“어제 공유한 기사 두 개만 요약…”|“어떤 기사였는지 다시 알려달라”|뉴스 요약 프롬프트| |6|“회의록에서 해야 할 일만 체크리스트…”|“어떤 회사를 말씀하시나요?”|액션 추출 스킬이 추가 정보 요청| |7|“이 계약 초안에서 위험한 조항…”|“어떤 계약 초안을 분석해 드릴까요?”|문서 분석 프롬프트| |8|“11월 22일 오전 10시 강남역…”|일정 확인/등록 유도|calendar_confirm 흐름 정상| |9|“좋아, 등록해줘”|“구글 캘린더에 등록했습니다”|calendar_approval → create_event 성공| |10|“그냥 이야기 좀 하자”|“무엇에 대해 이야기하고 싶으신가요?”|general chat 응답| - 모든 요청이 HTTP 200, router 전체 플로우를 통과. - intent 필드는 router 응답에 포함되지 않아 null로 표시되지만, 스킬별 프롬프트/승인 로직은 정상 작동. ## 교훈 1. **동적 레지스트리**: intent를 DB에서 관리하도록 해도 실제 정확도를 높이려면 의도 정의/임베딩/threshold를 주기적으로 정제해야 하며, 로그 기반 라벨링 루틴이 필요하다. 2. **테스트 커버리지**: `/api/test/router-message` 엔드포인트로 실제 플로우를 검증할 수 있으나 intent 값이 응답에 노출되지 않아 디버깅이 어려우므로 추후 intent/skill 정보 노출 검토 필요. 3. **자동 배포 확인**: 코드/문서 각각 별도 레포라서 변경 후 두 리포 모두 push + rb8001 컨테이너 로그 확인까지 해야 안전하다. --- ## 2025-11-15 11:10 - intent 데이터 확장 및 재테스트 - **추가 의도 (main_db)** 1. `file_inventory`: “어떤 파일 가지고 있어?” 류 요청 대응 2. `slack_message_send`: “슬랙에 메시지 보낼 수 있어?” 류 요청 대응 3. `system_view_request`: “지금 페이지 볼 수 있어?” 류 요청 대응 - 삽입 방식: Python 스크립트로 description 임베딩(`http://localhost:8515/embed`) 생성 → `intents`, `intent_prototypes`, `intent_thresholds` 테이블에 insert (version=1, source='description', 기본 threshold 0.8/0.1/0.7) ### 재테스트 (/api/test/router-message, 신규 시나리오 10건) | 입력 | 응답 요약 | 비고 | |------|-----------|------| |어떤 파일을 가지고 있는데?|“현재 가진 파일은 'IR자료_IR.pdf'”|새 `file_inventory` 의도로 응답| |슬랙에 메시지 보낼 수 있어?|“어떤 메시지를 누구에게 보낼까요?”|`slack_message_send` 경로에서 안내| |지금 페이지를 볼 수 있어?|“현재 가진 파일은 …”|`system_view_request` 의도 응답| |파일 목록 좀 보여줘|“현재 가진 파일은 …”|동일| |슬랙 팀 채널에 고생했다고 전해줘|“팀 채널에 메시지를 전해드릴 수 있습니다”|슬랙 메시지 의도| |현재 화면을 공유해줘|“현재 화면 공유 불가”|system_view_request 대응| |파일 읽어서 요약해줘|문서 요약 안내|기존 document_analysis| |슬랙 메시지 대신 보내줄래?|“응답을 생성할 수 없습니다.”|추가 슬롯 정보 미비로 실패 (추가 개선 필요)| |저장된 파일이 뭐가 있어?|“현재 가진 파일은 …”|file_inventory| |페이지 내용 알려줘|“어떤 회사를 말씀하시나요?”|미매칭 → 후속 intent 필요| ### 추가 교훈 1. **부분 임베딩 자동화 필요**: 기존 prototypes가 존재하면 새로운 intent는 직접 벡터를 넣어야 하므로, 향후 SemanticIntentClassifier가 누락된 항목만 재임베딩하도록 개선 필요. 2. **슬롯 정보 부족 문제**: “슬랙 메시지 대신 보내줄래?” 처럼 대상/메시지 내용이 없는 경우 응답이 종료되므로, intent 보강과 함께 후속 질문 로직이 필요. --- ## 2025-11-15 13:20 - calendar_delete 의도 및 CRUD 검증 - **추가 의도**: `calendar_delete` (DB + intent_registry.yaml 모두 등록, description 기반 임베딩 저장) - **신규 저장소**: `calendar_event_log` 테이블을 런타임 초기화하여 최근 일정(event_id, title, start_time)을 기록/삭제 - **코드 변경** - `app/state/calendar_event_repository.py`: 일정 로그 CRUD + 매칭 함수 - `calendar_handler.py`: create 성공 시 로그 적재, delete 요청 시 날짜/시간/“방금” 표현을 파싱해 event_id 역추적 후 skill-calendar DELETE 호출 - `decision_engine`, `router`, `message_router`, `calendar_skill`: `calendar_delete` intent → `delete_event` 액션까지 연결 - **대화 시나리오 테스트 (`/api/test/router-message`)** 1. “11월 30일 오후 3시 15분 강남역에서 고객 미팅 잡아줘” → confirm 템플릿 2. “좋아 등록해줘” → 실제 구글 캘린더 생성 (`skill-calendar POST /api/events`) 3. “3시 15분 일정 삭제해줘” → `calendar_delete` 감지, event_id 역추적 → `skill-calendar DELETE /api/events/{id}` 성공 - **검증** - skill-calendar 로그: `Deleted event ...` 기록 + HTTP 200 - `GET /api/events?start_date=2025-11-30T00:00:00Z&end_date=2025-12-01T23:59:59Z` → `count: 0` 확인 - **교훈** 1. 자연어 삭제를 지원하려면 event_id 매핑 저장소가 필수이며, create 시점에 메타데이터를 반드시 남겨야 한다. 2. 날짜/시간이 불완전한 문장은 최근 일정 fallback이 있어야 UX가 자연스럽다. 3. intent/skill wiring 완료 후에도 `/api/test/router-message`와 skill 서비스 API를 함께 확인해야 CRUD 전체가 보장된다. --- ## 2025-11-15 14:05 - calendar_delete 우선순위/테스트 보강 - **문제**: `11월 22일 10:00 일정 삭제해줘`처럼 날짜+시간 표현이 포함된 문장이 `calendar_event` 패턴에 먼저 매칭되어 삭제 intent가 실행되지 않았음. - **수정** - `DecisionEngine`에 우선순위 패턴 검사를 추가해 `calendar_delete`를 항상 가장 먼저 확인하고, 매칭 로직을 `_match_patterns`/`_compute_confidence` 헬퍼로 분리. - `tests/test_decision_engine_calendar_delete.py` 추가로 삭제 문장이 `IntentType.CALENDAR_DELETE`에 먼저 매칭되는지, 일반 등록 문장은 여전히 `calendar_event`로 분류되는지 검증. - **결과** - 로컬 pytest 전체 통과 (기존 sequence/semantic 테스트 포함). - 패턴 우선순위로 인해 삭제 문장이 더 이상 등록 의도로 잘못 분류되지 않으며, router 실행 시 `delete_event` 액션이 바로 호출됨.