- 파일명 변경: 250805-06_happybell80_AI응답개선Phase1-5.md - Phase 2 캐시 인프라 구축 내용 추가 - Lock TTL 메모리 관리 구현 상세 - 서버팀 검증 결과 포함 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
7.8 KiB
7.8 KiB
AI 응답 단조로움 해결 - Phase 1-5 구현
날짜: 2025-08-05 ~ 2025-08-06
작업자: happybell80 & Claude
관련 서비스: rb10508_micro
오후 1시 30분
문제 상황
서버팀 보고:
- rb10508_micro의 AI 응답이 지나치게 단조로움
- 3개의 고정 템플릿만 반복 사용
- "흥미로운 이야기네요. 더 자세히 들려주시겠어요?"
- "네, 이해했습니다. 어떻게 도와드릴까요?"
- "그렇군요. 제가 어떤 도움을 드릴 수 있을까요?"
원인 분석:
# brain.py:298-302
responses = [
"흥미로운 이야기네요. 더 자세히 들려주시겠어요?",
"네, 이해했습니다. 어떻게 도와드릴까요?",
"그렇군요. 제가 어떤 도움을 드릴 수 있을까요?"
]
return random.choice(responses)
5단계 개선 계획 수립
1단계: Gemini 전면 도입 (즉시 적용) 2-5단계: 캐시 시스템 구축 (점진적 구현)
오후 2시 00분
Phase 1 구현
목표: 모든 대화에 Gemini API 사용
구현 내용:
-
config.py에 환경변수 추가
USE_GEMINI_CONVERSATION: bool = False GEMINI_FALLBACK_MODEL: str = "gemini-2.5-flash-lite" -
brain.py에 Gemini 전면 도입 및 병렬화
- 템플릿 응답 → Gemini 응답으로 전환
- novelty 체크와 병렬 처리
- 쿼터 초과 시 fallback 모델 사용
오후 2시 30분
서버팀 테스트 결과 - async 오류 발생
에러 메시지:
2025-08-05 13:41:08,707 - app.core.brain - ERROR - Gemini 호출 완전 실패:
An asyncio.Future, a coroutine or an awaitable is required
문제: Gemini API 호출에서 async/await 처리 오류
오후 10시 30분
첫 번째 실수 - 성급한 판단
잘못된 분석:
generate_content_async()메서드가 존재하지 않는다고 판단_generate_gemini_response를 async → 동기 함수로 변경await제거
실제 문제:
asyncio.gather()에 동기 함수 결과값 전달- awaitable이 아닌 일반 값이라 에러 발생
오후 11시 00분
Sequential Thinking으로 재분석
GPT 검색 결과 확인:
google-generativeai0.3.x~0.5.x에는generate_content_async()없음- 동기 메서드
generate_content()만 존재 - async로 사용하려면
run_in_executor()필요
올바른 해결:
# generate_content를 비동기로 실행
loop = asyncio.get_running_loop()
response = await loop.run_in_executor(
None,
self.gemini_model.generate_content,
prompt
)
오후 11시 20분
서버팀 추가 문제 발견
-
check_novelty 함수 async 불일치
# 문제: 동기 함수를 asyncio.gather()에 전달 novelty_task = self.memory.check_novelty(message, self.current_user_id) -
EmotionState() 빈 객체 전달
# 문제: 실제 감정 대신 빈 객체 gemini_task = self._generate_gemini_response(message, memories, EmotionState())
최종 수정
- check_novelty를 run_in_executor로 감싸기
- 실제 emotion_state를 Gemini에 전달
- _generate_conversational_response 시그니처 수정
교훈
-
async/await 처리 주의
- 라이브러리 문서 확인 필수
- 동기 함수는
run_in_executor()로 비동기 변환 asyncio.gather()에는 awaitable만 전달
-
성급한 판단 금지
- 에러 메시지만 보고 판단하지 말기
- Sequential Thinking으로 차근차근 분석
- 실제 라이브러리 동작 확인
-
파라미터 전달 확인
- 빈 객체 대신 실제 데이터 전달
- 함수 시그니처 일관성 유지
- 데이터 흐름 추적
-
테스트의 중요성
- 로컬 테스트 환경 구축 필요
- 서버팀 피드백 적극 활용
- 단계별 검증
최종 성과
✅ Phase 1 완전 성공
- 단조로운 템플릿 → 자연스러운 AI 대화
- 메모리: 113.1MiB → 122.2MiB (+8.1%)
- 안정적인 async 처리
- 실제 감정 정보 활용
개선된 응답 예시: "정말 그렇게 느껴지시나요? AI 기술의 발전 속도가 어마어마한 건 저도 매일 피부로 느끼고 있어요..."
Phase 2: 캐시 인프라 구축
날짜: 2025-08-06
시작 시간: 오전 9시 00분
오전 9시 00분
서버팀 사전 점검
Phase 2 계획 검토:
- 철학적 기반 확인 (기억-감정-윤리 삼각형)
- 트러블슈팅 경험 반영 (async/await, 권한 문제)
- Lock TTL 메모리 관리 개선 요청
서버 상태 점검:
✅ ChromaDB 권한: 999:999 정상
✅ 디스크 공간: 369G 사용 가능 (충분)
✅ skill-embedding: 8015 포트 정상 작동
✅ rb10508_micro: 126.2MiB, healthy
오전 9시 30분
Phase 2 구현
목표: 안전한 캐시 인프라 구축 + 중복 방지
구현 내용:
-
config.py 수정
# 대화 캐시 설정 (Phase 2-5) USE_CONVERSATION_CACHE: bool = False CACHE_DISTANCE_THRESHOLD: float = 0.3 CACHE_MAX_ITEMS_PER_USER: int = 1000 CACHE_TTL_DAYS: int = 30 CACHE_LOCK_TTL_SECONDS: int = 300 # 5분 -
memory.py 개선
- TTL 기반 Lock 관리 시스템
- 예외 처리 강화된 컬렉션 초기화
- 디버그 로깅 추가
핵심 구현 - Lock TTL 관리:
async def get_user_lock(self, user_id: str) -> asyncio.Lock:
"""TTL 기반 Lock 관리 - 메모리 누수 방지"""
current_time = time.time()
# 5분 미사용 Lock 정리
for uid, last_used in list(self._lock_cleanup_time.items()):
if current_time - last_used > settings.CACHE_LOCK_TTL_SECONDS:
self._cache_locks.pop(uid, None)
self._lock_cleanup_time.pop(uid, None)
if settings.DEBUG:
logger.debug(f"[CACHE] Lock 정리: {uid}")
# Lock이 없으면 생성
if user_id not in self._cache_locks:
self._cache_locks[user_id] = asyncio.Lock()
# 사용 시간 업데이트
self._lock_cleanup_time[user_id] = current_time
return self._cache_locks[user_id]
예외 처리 강화:
if settings.USE_CONVERSATION_CACHE:
try:
self.conversation_cache = self.client.get_or_create_collection(
name=f"{settings.ROBING_ID}_conversation_cache",
metadata={
"type": "conversation_cache",
"version": "1.0",
"max_items": settings.CACHE_MAX_ITEMS_PER_USER
},
embedding_function=self.embedding_function
)
logger.info("Conversation cache 컬렉션 생성 성공")
except Exception as e:
logger.error(f"Conversation cache 컬렉션 생성 실패: {e}")
self.conversation_cache = None
오전 9시 52분
Phase 2 배포 및 검증
배포 결과:
- Git push 완료
- Gitea Actions 자동 배포
서버팀 검증 결과:
✅ 메모리 사용량: 126.2MiB → 108.4MiB (-17.8MiB)
✅ CPU 사용률: 0.13% → 0.05%
✅ 헬스체크: 정상
✅ ChromaDB: 1097728 bytes 업데이트
✅ USE_CONVERSATION_CACHE: False (안전)
Phase 2 교훈
-
서버팀 피드백의 가치
- Lock TTL 메모리 누수 방지 제안
- 예외 처리 강화 요구
- 사전 점검으로 안전한 배포
-
점진적 롤아웃의 중요성
- 기본값 False로 기존 시스템 보호
- 기능 토글로 위험 최소화
- 단계별 활성화 가능
-
예상치 못한 개선
- 코드 추가했는데 메모리 사용량 감소
- import 정리 효과로 추정
- 최적화의 부수 효과
Phase 2 성과
✅ 캐시 인프라 구축 완료
- conversation_cache 컬렉션 준비
- TTL 기반 Lock 시스템 구현
- 메모리 누수 방지 메커니즘
✅ 안전한 배포
- 기존 기능 무영향
- 메모리 사용량 오히려 감소
- Phase 3 진행 준비 완료