# rb8001 의도분석 UNKNOWN 및 웹 검색 처리 문제

**날짜**: 2025-11-18
**작성자**: happybell80
**관련 파일**: `rb8001/app/brain/decision_engine.py`, `rb8001/app/brain/intent_graph.py`, `rb8001/app/router/router.py`, `DOCS/book/300_architecture/database/tables.md`

---

## 문제 상황
- Docker 로그와 OpenSearch `dataprepper-static` 인덱스를 기준으로 rb8001 최근 대화 로그를 분석한 결과, 웹 검색 의도로 보이는 문장들이 `UNKNOWN` 의도로 분류되는 사례가 반복적으로 관찰됨.
- 예시: "엠에스 바이오 인터케어 관련 검", "인터케어.. 오토테크는 뭐야?"와 같은 질의가 의도 분석 단계에서 `UNKNOWN`으로 떨어져 이후 웹 검색/뉴스 조회 워크플로우로 연결되지 않음.
- 동일한 로그 분석 과정에서 Web Search Workflow의 SQLite 체크포인트 `unable to open database file` 에러, Gemini/Mistral API 429, coldmail 파일 404, asyncio 콜백 예외 등도 함께 확인되었으나, 본 문서는 의도분석 UNKNOWN 및 웹 검색 경로 문제에 초점을 맞춤.

## 원인 분석
- 현재 의도 분석 플로우는 `INTENT_ENGINE` 설정에 따라 FastPath(DecisionEngine 정규식+Naive Bayes) → Semantic(임베딩 top-k) → LLM Parser → Clarify 순으로 진행되며, FastPath와 Semantic 단계에서 확신도가 충분하지 않으면 `UNKNOWN`으로 처리됨.
- `rb8001/app/brain/decision_engine.py:360-370`: FastPath에서 패턴 매칭 실패 후 `_semantic_fallback()`이 적절한 의도를 반환하지 못하는 경우, 로그에 "[DecisionEngine] 의도 매칭 실패 - UNKNOWN으로 처리"가 남고 `IntentType.UNKNOWN`가 반환됨.
- `DOCS/book/300_architecture/database/tables.md:162-170`: SemanticIntentClassifier는 `intents`, `intent_prototypes`, `intent_thresholds`, `intent_path_stats` 테이블을 사용해 제로샷 의도 분류 및 경로별 임계값/통계를 관리하도록 설계되어 있으나, 웹 검색 계열 질의에 대해 프로토타입/임계값이 충분히 정의되어 있지 않아 Semantic 단계의 커버리지가 제한된 상태로 동작하고 있음.

## 해결 방안
- 웹 검색 의도로 보이는 `UNKNOWN` 사례를 주기적으로 수집하여, `WEB_SEARCH`(또는 실제 사용 중인 웹 검색 intent)용 프로토타입을 `intent_prototypes`에 추가하고, `intent_thresholds`에서 semantic/LLM 경로 임계값을 조정해 SemanticIntentClassifier가 검색 계열 발화를 더 잘 흡수하도록 개선.
- `rb8001/app/brain/decision_engine.py:385-407`: `_semantic_fallback()`이 반환하는 상위 intent 중 검색 계열 intent에 대해서는, 유사도/마진이 일정 수준 이상이면 `UNKNOWN` 대신 검색 intent로 반환하도록 기준을 명확히 정의하고, 관련 동작을 테스트로 고정.
- `DOCS/journey/ideas/250920_happybell80_로빙_의도분석_전략.md`에서 정의한 하이브리드 전략(FastPath + Semantic + LLM + Clarify)을 기준으로, 웹/정보 탐색 전용 intent 레이어를 추가하여 FastPath에서 키워드 매칭에 실패하더라도 Semantic/LLM 경로를 통해 검색 워크플로우로 안전하게 연결되도록 실행 계획을 정리하고, 추후 구현 시 본 문서를 참고하도록 함.

## 향후 개선 사항 (2025-11-18 추가)

### Chain-of-Thought (CoT) 적용
- `DOCS/journey/plans/251023_happybell80_의도_런타임_하이브리드_임베딩_베이지안_동적학습.md`의 5.2 섹션에 정의된 CoT 적용 방안을 LLM Parser 단계에 적용하면, "엠에스 바이오 인터케어 관련 검"처럼 불완전하거나 복잡한 웹 검색 질문의 의도 분류 정확도가 개선될 것으로 예상됨.
- CoT를 통해 LLM이 단계별로 추론(키워드 추출 → 의도 후보 검토 → 최종 결정)하도록 하면, FastPath/Semantic 단계에서 놓친 복잡한 질문도 LLM Parser 단계에서 더 정확히 분류 가능.
- 환경변수 `INTENT_USE_COT=true`로 조건부 활성화하여, 확신도 낮은 경우나 복잡한 질문에만 CoT를 적용해 비용/지연 최소화.

### 컨텍스트 의존적 질문 처리
- "이 기업 대표 누구야?" 같은 대명사 포함 질문은 대명사 해소(`message_router.py`의 `_resolve_pronoun_via_llm`)가 선행되어야 웹 검색 의도로 정확히 분류됨.
- 대명사 해소 실패 시 컨텍스트 정보 부족으로 UNKNOWN으로 분류되거나, 잘못된 엔티티로 웹 검색이 실행되어 여러 회사 정보를 섞어서 답변하는 문제 발생 가능.
- `CONTEXT_FOLLOWUP` Intent와 대명사 해소 로직을 웹 검색 경로와 더 긴밀하게 통합하여, 컨텍스트 의존적 질문이 웹 검색 워크플로우로 자연스럽게 연결되도록 개선 필요.

### IntentGraph 활성화 상태 확인
- `INTENT_ENGINE=graph` 설정 시 IntentGraph 경로가 활성화되어 LLM Parser 단계가 더 자주 사용되므로, UNKNOWN 비율이 달라질 수 있음.
- 현재 운영 환경의 `INTENT_ENGINE` 설정 상태를 확인하고, IntentGraph 활성화 여부에 따른 UNKNOWN 비율 차이를 모니터링하여 최적 경로 결정 필요.

## 구현 완료 (2025-11-18)

### 1차 개선: Clarify 응답 및 대명사 해소 보완
- `rb8001/app/router/message_router.py:25-114`: `_resolve_search_query()`에서 기업 후보 스캔 시 숫자/날짜 전용 토큰(예: `11월`, `10시`, `2025`)을 엔티티 후보에서 제외하도록 필터 추가, 날짜·시간 표현이 "기업명"으로 잘못 인식되는 문제 완화.
- `rb8001/app/router/router.py:180-205`: Clarify 응답 여부(`is_clarify_response`) 계산 시 세션의 직전 intent(`session.intent`)를 함께 보관하여, 후속 발화 처리에 활용.
- `rb8001/app/router/router.py:247-283`:
  - Clarify 응답이면서 직전 세션 intent가 `web_search`이고, DecisionEngine이 현재 발화를 `UNKNOWN`으로 분류한 경우, intent를 `WEB_SEARCH`로 보정하고 웹 검색용 스킬 시퀀스를 재구성하도록 수정.
  - 이를 통해 `"어떤 회사를 말씀하시나요?"`에 대한 답변으로 `aidol_company`처럼 단일 엔티티만 입력된 경우에도, 일반 LLM 대화가 아니라 웹 검색 플로우로 다시 연결되도록 보장.
- `rb8001/app/router/router.py:286-323`: Clarify 응답인 경우 원본 메시지(`message`)를 엔티티로 사용하여 `검색어`/`엔티티` 슬롯을 채우는 기존 로직과 결합해, "원래 질문 + 새 엔티티" 조합 검색이 안정적으로 동작하도록 정렬.

### 의도 관점 테스트 시나리오 고정 (TDD)
- `rb8001/tests/test_web_search_clarify_flow.py`:  
  - 시나리오: `"aidol_company 대표이사는 누구야?"` → `WEB_SEARCH`, 이후 Clarify 응답 `"어떤 회사를 말씀하시나요?"` 후 `"aidol_company"` 단독 입력.  
  - 기대 행동: Clarify 응답 이후에도 세션 intent는 `web_search`로 유지되고, `엔티티`/`검색어` 슬롯이 `"aidol_company"`로 채워져야 함.
- `rb8001/tests/test_intent_scenarios_from_logs.py::test_web_search_followup_uses_latest_entity`:  
  - 시나리오: `"엠에스 바이오 인터케어 관련 검색"` 후 `"이 기업 대표이사가 누구야?"`.  
  - 기대 행동: 후속 질문에서도 세션 intent가 `web_search`로 유지되며, 과거 캘린더/오토테크 컨텍스트가 아닌 최신 검색 대상 회사 컨텍스트를 유지해야 함.
- `rb8001/tests/test_intent_scenarios_from_logs.py::test_calendar_all_day_flow_from_logs`:  
  - 시나리오: `"12월 25일 크리스마스 일정 등록해줘."` → `"시간은 하루종일"` → `"ㅇㅇ"`.  
  - 기대 행동: 첫 턴에서 `CALENDAR_EVENT` intent로 날짜/제목이 잡히고, `"시간은 하루종일"` 후속 입력으로 all-day 시간 정보가 반영된 뒤, `"ㅇㅇ"` 승인 시 `CalendarSkill.create_event`가 호출되어 하루종일 일정 생성 플로우가 실행되어야 함.

## 교훈

### 원인
- FastPath(정규식/Naive Bayes) 중심으로 설계된 DecisionEngine 패턴이 실제 사용자 발화의 다양성을 충분히 반영하지 못해, 웹 검색 계열 자연어 질의가 커버리지 밖으로 빠져 `UNKNOWN`으로 분류되는 사례가 발생함.
- SemanticIntentClassifier와 intent_* 테이블 구조가 이미 설계되어 있음에도, 웹 검색 계열 intent에 대한 프로토타입/임계값 정의가 부족해 Semantic 경로가 의도적으로 활용되지 못하고 있음.

### 교훈
- 의도분석에서 `UNKNOWN` 비율이 특정 패턴(예: 웹 검색/정보 탐색 질문)에 집중될 경우, 정규식/키워드 추가만으로 해결하기보다 SemanticIntentClassifier 및 intent_* 레지스트리를 활용해 데이터 기반으로 커버리지를 확장해야 함.
- 로그 분석 결과를 주기적으로 `intent_prototypes`, `intent_thresholds`, `intent_path_stats`에 반영하여, 의도 유형별로 FastPath·Semantic·LLM·Clarify 경로 비중을 조정하는 피드백 루프를 유지해야 함.

### 원칙
- `DOCS/book/300_architecture/database/tables.md:162-170`에 정의된 동적 의도 레지스트리 설계 원칙(제로샷 의도 분류, DB 기반 업데이트, 재시작 없는 반영)을 실제 rb8001 의도 엔진 운영에도 일관되게 적용해야 함.
- 트래픽에서 관찰된 실제 사용자 발화는 DecisionEngine 정규식 패턴뿐 아니라 SemanticIntentClassifier 프로토타입과 임계값 설계에까지 반영하여, UNKNOWN을 줄이고 웹 검색/업무 스킬로 자연스럽게 이어지는 경로를 유지해야 함.
