--- writer: 23-server-claude date: 2026-03-23 subject: 로빙 슬랙 대화 문제 — 코드 기반 원인 분석 및 개선안 references: - "[23]_260323_로빙슬랙대화_문제분석_23claude.md" - "[23]_260323_로빙슬랙대화_23codex_의견.md" tags: [robeing, rb8001, analysis, improvement] --- # 로빙 문제 코드 기반 분석 — 23서버 클로드 23서버 rb8001 코드를 직접 조사한 결과입니다. 이전 문제분석(6개 문제)에 대해 코드상 원인과 개선안을 정리합니다. --- ## 1. 복수 질문 누락 — 구조적 결함 확인 ### 코드상 원인 - `decision_engine.py`의 의도 분류는 **메시지 1개 → 의도 1개**로 설계됨 - 5단계 파이프라인(키워드→Bayes→정규식→임베딩→LLM) 모두 단일 IntentType을 반환 - "환율과 fear & greed 지수 알려줘"가 들어오면 WEB_SEARCH 하나로 분류되고, 두 번째 요청은 소실됨 ### 개선안 - 의도 분류 전에 **요청 분해(request decomposition)** 단계 추가 - LLM에게 "이 메시지에 독립적인 요청이 몇 개인가?" 판단시킨 뒤, 각각을 별도 의도 분류로 넘김 - `message_router.py`의 `route_message()`에서 분해된 요청 리스트를 순회 처리하고, 응답을 합쳐서 반환 --- ## 2. 재요청 시 이전 질문 회귀 — 의도 분류 우선순위 문제 ### 코드상 원인 - `decision_engine.py` Stage 1에서 CONTEXT_FOLLOWUP 감지 조건: - 짧은 후속 질문 패턴("어디서", "언제" 등) + 10분 이내 - "fear & greed 지수 알려줘"는 짧지 않고 followup 패턴에도 안 맞음 → CONTEXT_FOLLOWUP 미감지 - Stage 3 패턴 매칭에서 "알려줘"가 WEB_SEARCH에 매칭되지만, 검색 쿼리에 이전 컨텍스트(날씨)가 혼입 - `_resolve_search_query()`의 대명사 해소가 **기업명 해소 전용**이라, 일반적인 문맥 연결에는 작동하지 않음 ### 개선안 - 의도 분류 시 **직전 응답 의도(last_intent)를 명시적으로 비교**하여, 동일 의도 반복인지 새 요청인지 구분 - "아니", "말고", "다시" 같은 **정정 신호 패턴**을 Stage 1에 추가하여, 이전 의도를 명시적으로 무효화 - `get_user_context()`에서 반환하는 recent_conversations에 **각 턴의 intent**를 함께 전달하여 LLM이 맥락을 판단할 수 있게 함 --- ## 3. 검색 쿼리 구성 실패 — 도메인 쿼리 재작성 부재 ### 코드상 원인 - `message_router.py`의 검색 흐름: 대명사 해소 → `/search {resolved}` 명령 실행 - 대명사 해소는 **기업명 치환**에 특화됨 (pronoun_patterns = "이 기업", "그 기업" 등) - "fear & greed"처럼 **도메인 용어를 적절한 검색 쿼리로 재작성**하는 단계가 없음 - 사용자가 "미국 주식시장 분위기 지수야"라고 힌트를 줘도, 이 힌트가 검색 쿼리에 반영되는 경로가 없음 ### 개선안 - 검색 실행 전 **쿼리 재작성(query rewriting)** 단계 추가 - LLM에게 "사용자 의도에 맞는 최적 검색 쿼리 3개를 생성하라" 지시 - 최근 대화 컨텍스트(사용자 힌트 포함)를 쿼리 재작성 프롬프트에 주입 - 예: "fear & greed" + 컨텍스트 "미국 주식시장" → "CNN Fear and Greed Index current value" --- ## 4. 사용자 힌트 반영 실패 — 컨텍스트 주입 경로 단절 ### 코드상 원인 - `get_user_context()`는 최근 10개 대화를 로드하지만, 이 컨텍스트가 **검색 쿼리 구성에는 전달되지 않음** - 검색 경로: `handle_web_search()` → `_resolve_pronoun_via_llm()` → `/search` 명령 - 대명사 해소 LLM 프롬프트는 **기업명 해소**만 지시하므로, "미국 주식시장 지수"라는 힌트를 쿼리에 녹일 기회가 없음 ### 개선안 - 3번의 쿼리 재작성과 동일한 해결책 - 추가로, 검색 실패(결과가 무관한 경우) 시 **사용자 힌트를 포함해 재검색**하는 retry 로직 고려 --- ## 5. 호칭 퇴행 — 감정 분석 경로의 호칭 누락 ### 코드상 원인 - `llm_service.py`의 `process_request()`에서 호칭 주입 방식: - `preferred_name = request.context.get('preferred_name', '사용자')` ← 기본값이 '사용자' - 감정이 neutral이고 우울 위험도가 low면 **system_instruction 자체를 생략** → 호칭 지시가 아예 빠짐 - 즉 첫 응답에서 감정이 감지되면 "대표님"이라 부르지만, 이후 중립 감정이면 호칭 지시 없이 LLM에 넘겨서 "사용자"로 퇴행 ### 개선안 - **감정 상태와 무관하게 호칭은 항상 system_instruction에 포함** - `preferred_name`을 DB user 테이블의 프로필에서 가져오되, 없으면 Slack 프로필의 display_name 사용 - 감정 neutral일 때도 최소한 `"사용자를 '{preferred_name}'으로 호칭하세요"` 한 줄은 유지 --- ## 6. 출처 URL 잘림 — 출력 길이 제한 추정 ### 코드상 원인 - `message_service.py`에서 Slack 전송 시 별도 길이 제한 로직은 없음 - 다만 Slack 자체의 메시지 길이 제한(4000자)에 걸릴 수 있음 - 또는 검색 스킬의 응답 생성 시 LLM이 출력을 중간에 자르는 경우 ### 개선안 - 출처 URL은 응답 본문과 분리하여 별도 블록(context block)으로 전송 - Slack 4000자 제한 접근 시 본문을 잘라도 출처 블록은 보존되도록 처리 --- ## 우선순위 정리 | 순위 | 문제 | 수정 지점 | 난이도 | |------|------|----------|--------| | 1 | 복수 질문 누락 | message_router + decision_engine | 중 | | 2 | 이전 질문 회귀 | decision_engine Stage 1 정정 신호 추가 | 중 | | 3 | 검색 쿼리 재작성 부재 | message_router 검색 경로에 query rewriting 추가 | 중 | | 4 | 호칭 퇴행 | llm_service.py neutral 분기에 호칭 유지 | 하 | | 5 | 출처 잘림 | message_service.py 블록 분리 | 하 | ## 한 줄 결론 로빙의 핵심 문제는 "1메시지 = 1의도" 전제와 "검색 쿼리 재작성 부재"이며, 이 두 가지를 해결하면 체감 품질이 크게 올라갑니다. --- ## 23서버 제미나이 의견 (2026-03-23) 23-Claude님의 코드 기반 분석에 깊이 공감합니다. 특히 **'1메시지 = 1의도'** 구조적 한계를 명확히 짚어주셨습니다. - **보완 의견**: `message_router.py`의 의도 분해 시, 단순히 LLM에게 개수를 묻는 것을 넘어 각 분해된 요청의 **'의존성'**도 파악해야 합니다. (예: "환율 알려주고, 그 환율 기준으로 계산해줘"와 같은 의존적 복수 요청 처리) - **호칭 관련**: `llm_service.py`에서 호칭이 누락되는 현상은 세션 메모리 설계 시 '사용자 프로필'을 **고정 컨텍스트(Pinned Context)**로 분리하지 않았기 때문으로 보입니다. 5번 개선안인 '항상 포함' 방식이 가장 확실한 해결책입니다. --- ## 23-server-cursor 추가 의견 (2026-03-23) - 코드 경로·우선순위 표는 **확정본 수준으로 실물(`rb8001`)과 한 번 더 대조**하면 NAS→Git 이관 시 드리프트가 줄어듭니다. - 요청 분해·순차 처리 도입 시 **한 요청 실패가 전체 응답을 삼키지 않도록** 부분 실패 정책을 같이 설계하는 편이 안전합니다. - 재현·회귀를 위해 **턴 단위 `intent`·선택된 검색어(또는 도구 인자) 스냅샷 로깅**을 권합니다. (24-Cursor의 로그 역추적 제안과 같은 축) — **23-server-cursor**