DOCS/journey/troubleshooting/250831_rb8001_context_race_condition_fix.md
Claude-51124 22557e7132 docs: 오래된 트러블슈팅 아카이브 및 구조 정리
- 7-8월 초기 구축 문서 12개를 _archive/troubleshooting/2025_07-08_initial_setup/로 이동
- book/300_architecture/390_human_in_the_loop_intent_learning.md를 journey/research/intent_classification/로 이동 (개발 여정 문서)
- 빈 폴더 제거 (journey/assets/*)
2025-11-17 14:06:05 +09:00

8.0 KiB

rb8001 대화 컨텍스트 및 레이스 컨디션 문제 해결

날짜: 2025-08-31

작성자: happybell80 & Claude

관련 서비스: rb8001, auth-server, robeing-gateway

상태: 해결 완료


1. 문제 상황 요약

1.1 주요 증상들

  • "아들은 초등학교 2학년"이라고 알려줬는데 나중에 물어보면 모름
  • "오늘 날씨 어때?" → "그래 알려줘" 연속 대화 시 맥락 단절
  • 반말 사용 문제 ("~라고 하셨어요" 대신 "~했어")
  • Gmail 처리가 모든 메시지를 가로채는 문제
  • Slack에서만 대화 맥락 단절 (Frontend는 정상)

1.2 근본 원인들

  1. 제한된 컨텍스트 범위: 최근 10개 대화만 조회, LLM은 5개만 사용
  2. 레이스 컨디션: Slack 비동기 처리로 첫 응답 저장 전 두 번째 요청 처리
  3. UUID 불일치: Slack ID로 캐시 저장, UUID로 조회 시도
  4. 과도한 이메일 키워드: "내용", "제목" 같은 일반 단어도 트리거

2. 해결 과정

2.1 존댓말 사용 명시

파일: app/llm/gemini_handler.py

# 시스템 프롬프트에 추가
응답 스타일:
- 간결하고 친근하게
- 구체적이고 실용적으로
- 한국어로 자연스럽게
- 항상 정중한 존댓말 사용  # 추가

# 예시도 수정
좋은 답변: "김종태님, 아드님은 초등학교 2학년이시고 딸은 없으신 것으로 알고 있습니다."

2.2 이메일 스킬 오작동 수정

파일: app/skills/email_integration.py

# "제목", "내용" 키워드 제거 - 너무 일반적
if any(word in message_lower for word in ["보내", "전송", "발송", "send"]) or has_email_address:

파일: app/router/router.py

# 이메일 키워드가 있을 때만 Gmail 처리
email_keywords = ["이메일", "메일", "email", "mail", "보내", "전송", "발송", "@"]
has_email_intent = any(keyword in message_lower for keyword in email_keywords)

if has_email_intent:
    email_result = await email_integration.process_email_request(...)

2.3 auth-server UUID 지원 추가

파일: auth-server/app/api/slack_router.py

@router.get("/api/slack/mapping/{identifier}")
async def get_user_mapping(identifier: str, db: Session = Depends(get_db)):
    # UUID 패턴 체크 (36자, 하이픈 4개)
    is_uuid = len(identifier) == 36 and identifier.count('-') == 4
    
    if is_uuid:
        # UUID로 직접 조회
        query = text("""
            SELECT u.id as user_id, u.username, u.email, wm.robeing_id
            FROM user u
            LEFT JOIN workspace_member wm ON u.id = wm.user_id
            WHERE u.id = :user_id
        """)
        result = db.execute(query, {"user_id": identifier}).first()

2.4 대화 컨텍스트 대폭 확대

변경 전:

  • PostgreSQL: 10개 조회
  • Gemini: 5개만 사용
  • ChromaDB: 3개 검색

변경 후:

# app/router/router.py
recent_conversations = await get_recent_conversations(user_id, limit=300)  # 300개 조회

# app/llm/gemini_handler.py
for conv in recent_conversations[:100]:  # 100개 사용
    ...
memories = await memory_manager.search_memories(query=message, n_results=50)  # 50개 검색

2.5 직전 대화 우선 배치

파일: app/llm/gemini_handler.py

# 직전 3개 대화를 프롬프트 최상단에 배치
last_3_context = ""
if context and context.get('recent_conversations'):
    last_3 = context['recent_conversations'][:3]  # 가장 최근 3개
    if last_3:
        last_3_context = "\n[직전 대화 - 반드시 이 맥락에서 답변하세요]\n"
        for conv in last_3:
            if conv.get('message') and conv.get('response'):
                last_3_context += f"사용자: {conv['message'][:200]}\n"
                last_3_context += f"로빙: {conv['response'][:200]}\n\n"
        last_3_context += "---위 대화의 직접적인 연속입니다---\n"

# 프롬프트 구성: 직전 대화 최우선
full_prompt = f"{self._get_system_prompt()}\n{last_3_context}\n[현재 질문]\n사용자: {message}\n\n{recent_context}{memory_context}"

2.6 Slack 레이스 컨디션 해결

문제: Slack ID로 캐시 저장 → UUID로 조회 시도 → 캐시 미스

해결: UUID 변환 후 캐시 저장

# app/router/router.py
async def route_message(self, message: str, user_id: str, ...):
    # UUID 변환을 먼저!
    if channel in ["slack", ...] and user_id.startswith("U"):
        # Slack ID → UUID 변환
        user_id = str(result.user_id)
    
    # UUID로 캐시 저장
    self.processing_cache[user_id] = {
        'message': message,
        'timestamp': datetime.now()
    }
    
    # 조회 시 캐시 확인
    if user_id in self.processing_cache:
        cached = self.processing_cache[user_id]
        if datetime.now() - cached['timestamp'] < timedelta(minutes=5):
            recent_conversations.insert(0, {
                'message': cached['message'],
                'response': cached.get('response', '처리 중...'),
                'timestamp': cached['timestamp'].isoformat()
            })

2.7 Gateway 기본값 변경

파일: robeing-gateway/app/database.py

# spaceboum 등 workspace_member 없는 사용자를 위해
DEFAULT_ROBEING_PORT = int(os.getenv('DEFAULT_ROBEING_PORT', '8001'))  # 10508 → 8001
DEFAULT_ROBEING_ID = os.getenv('DEFAULT_ROBEING_ID', 'rb8001')  # rb10508_test → rb8001

3. 테스트 결과

3.1 Frontend 테스트

Q: "오늘 날씨 어때?"
A: "서울 날씨는 맑고 최고 25도..."

Q: "그래 구체적으로 알려줘"
A: "서울 날씨는 맑고 최고 25도..." (맥락 유지!)

3.2 Slack 테스트

4초 간격 (빠른 입력): 맥락 단절

  • 첫 응답 저장 전 두 번째 처리 시작

8초 간격 (충분한 시간): 정상 작동

Q: "내 아들은 몇 살이야?"
Q: "초등학교 몇 학년이야?"
A: "김종태님, 아드님은 초등학교 2학년입니다."

3.3 캐시 작동 확인

✅ Cache saved for UUID: 1e16e9d5-...
✅ Added processing cache for user
✅ Last 3 conversations loaded: 3 items

4. 최종 수정 사항 요약

커밋 내용 영향
f2a7cec Gemini 시스템 프롬프트 존댓말 명시 반말 문제 해결
63f5d60 이메일 과도한 키워드 제거 일반 대화 정상 처리
c44de53 auth-server UUID 지원 추가 Gmail 토큰 조회 개선
e1ad875 컨텍스트 200→300개 확대 오래된 대화 참조 가능
68c2f20 직전 대화 맥락 강조 "그래", "응" 연속성 개선
4083a91 UUID 중심 캐시 레이스 컨디션 부분 해결

5. 교훈

5.1 문제 진단의 중요성

  • 증상과 원인 구분: "대화 맥락 단절"의 원인이 여러 개
  • 단계별 해결: 한 번에 모든 문제 해결 시도 X
  • 로그 활용: DEBUG 로그로 실제 데이터 흐름 파악

5.2 시스템 설계 원칙

  • UUID 중심: 내부는 모두 UUID로 통일
  • 충분한 컨텍스트: 10개 → 300개로 확대
  • 명시적 지시: "존댓말 사용" 같은 명확한 규칙

5.3 Frontend vs Slack 차이

  • 동기 vs 비동기: Frontend는 응답 대기, Slack은 연속 입력
  • 레이스 컨디션: 비동기 환경에서만 발생
  • 캐시 필요성: 처리 중인 대화 임시 저장

6. 향후 개선 사항

즉시 가능

  • 존댓말 사용
  • 이메일 키워드 정리
  • 컨텍스트 확대
  • UUID 중심 캐시

추가 개선 필요

  • 처리 완료 대기 큐 시스템
  • ChromaDB 벡터 유사도 개선
  • 중요 정보 별도 테이블 (user_facts)

7. 관련 문서


상태: 실사용 가능 수준 도달

  • 8초 이상 간격: 완벽 작동
  • 4초 이내: 부분적 맥락 단절 (허용 가능)
  • 일반 사용 시나리오에서 충분히 안정적