- 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/*)
242 lines
5.4 KiB
Markdown
242 lines
5.4 KiB
Markdown
# 250806 함수형 전환과 LLM 기반 메모리 선택 구현
|
|
|
|
## 오후 7시 00분
|
|
|
|
### 문제 상황
|
|
|
|
**대화 저장 안되는 문제 발견**
|
|
- Slack 메시지는 수신되지만 ChromaDB에 저장 안됨
|
|
- `[대화]` 로그 전무
|
|
- 함수형 전환 후 Fire & Forget 패턴의 문제
|
|
|
|
### 원인 분석
|
|
|
|
1. **Fire & Forget의 위험성**
|
|
```python
|
|
asyncio.create_task(store_memory_fn(...)) # 에러 발생해도 모름
|
|
```
|
|
- 에러가 발생해도 전파되지 않음
|
|
- 로그도 없어서 디버깅 불가
|
|
|
|
2. **store_memory 함수 문제**
|
|
- try-except 없음
|
|
- 로그 없음
|
|
- return 값 없음
|
|
|
|
## 오후 8시 00분
|
|
|
|
### 함수형 해결책 구현
|
|
|
|
**완전 함수형 접근**
|
|
```python
|
|
# 데이터 준비 (순수 함수)
|
|
def prepare_memory(content: str, user_id: str, role: str) -> Dict
|
|
|
|
# 일괄 저장 (I/O 함수)
|
|
async def save_memories(memories: List[Dict]) -> bool
|
|
```
|
|
|
|
**결과**
|
|
- 코드 42줄 → 27줄 (35% 감소)
|
|
- 에러 처리 추가
|
|
- 배치 처리로 효율성 향상
|
|
|
|
## 오후 10시 00분
|
|
|
|
### 문맥 파악 문제
|
|
|
|
**증상**
|
|
```
|
|
사용자: "어떤 내용이었어?"
|
|
로빙: "김종태님, 안녕하세요..." (엉뚱한 답변)
|
|
```
|
|
|
|
**원인**
|
|
- 메모리 검색 10000개 반환
|
|
- 벡터 거리만 고려, 시간 무시
|
|
- "어떤 내용"이 너무 일반적
|
|
|
|
## 오후 10시 30분
|
|
|
|
### LLM 기반 동적 메모리 선택
|
|
|
|
**Mistral 통합 (하드코딩 제거)**
|
|
1. 질문 의도 파악 (참조/새주제)
|
|
2. 동적 임계값 계산 (백분위수 기반)
|
|
3. 초기 선택 (거리 기반)
|
|
4. LLM 검증 (충분성 판단)
|
|
|
|
**황금비 사용**
|
|
```python
|
|
golden_ratio = (1 + 5 ** 0.5) / 2 # 수학 상수
|
|
expanded = current + (max - current) / golden_ratio
|
|
```
|
|
|
|
## 오후 11시 00분
|
|
|
|
### 함수형 원칙 위반 발견
|
|
|
|
**문제**
|
|
- "순수 함수"라고 주석 달고 API 호출
|
|
- 로그를 순수 함수에 포함
|
|
- I/O와 로직 혼재
|
|
|
|
**해결**
|
|
- app/llm/mistral.py로 완전 분리
|
|
- 순수 함수: 프롬프트 생성, 파싱, 계산
|
|
- I/O 함수: API 호출만
|
|
|
|
## 오후 11시 30분
|
|
|
|
### 최종 구조
|
|
|
|
```
|
|
app/llm/mistral.py (순수 함수)
|
|
├── create_intent_analysis_prompt()
|
|
├── parse_intent_response()
|
|
├── calculate_percentile_threshold()
|
|
└── filter_memories_by_threshold()
|
|
|
|
app/core/memory.py (I/O 레이어)
|
|
├── call_mistral_api() # I/O
|
|
└── adaptive_memory_selection() # 조정자
|
|
```
|
|
|
|
## 오후 11시 59분
|
|
|
|
### 배포 및 검증
|
|
|
|
**서버 테스트 결과**
|
|
- ✅ 대화 저장 정상
|
|
- ✅ 메모리 리콜 정상
|
|
- ✅ Mistral API 연동 정상
|
|
- ✅ 함수형 100% 달성
|
|
- ✅ 하드코딩 0%
|
|
|
|
## 다음날 오전 10시 00분
|
|
|
|
### await 누락 버그 발견
|
|
|
|
**증상**
|
|
```
|
|
[메모리] 검색 실패: object of type 'coroutine' has no len()
|
|
RuntimeWarning: coroutine 'adaptive_memory_selection' was never awaited
|
|
```
|
|
|
|
**원인**
|
|
```python
|
|
# 잘못된 코드 (await 누락)
|
|
filtered_memories = adaptive_memory_selection(query, memories, mistral_key)
|
|
|
|
# 수정된 코드
|
|
filtered_memories = await adaptive_memory_selection(query, memories, mistral_key)
|
|
```
|
|
|
|
**영향**
|
|
- Mistral API 호출 안됨
|
|
- LLM 기반 메모리 선택 실패
|
|
- 폴백 로직도 작동 안함
|
|
|
|
**해결**
|
|
- 단순 await 누락 - 1줄 수정으로 해결
|
|
|
|
## 오전 11시 00분
|
|
|
|
### 메모리 모듈 재구조화
|
|
|
|
**문제**
|
|
- memory.py 305줄 비대
|
|
- 여러 책임 혼재
|
|
|
|
**해결**
|
|
```
|
|
app/core/memory/
|
|
├── storage.py # ChromaDB I/O
|
|
├── identity.py # 신원 관리
|
|
├── selection.py # LLM 선택
|
|
└── scoring.py # 통계/수학 (추가됨)
|
|
```
|
|
|
|
**결과**
|
|
- 단일 책임 원칙
|
|
- 100% 함수형 유지
|
|
- import 하위 호환성
|
|
|
|
## 오후 12시 00분
|
|
|
|
### 베이지안 + 시간감쇠 구현
|
|
|
|
**문제: 76개 여전히 과도**
|
|
- 100개 중 76개 선택
|
|
- 과거 기억 노이즈
|
|
- "안녕하세요" 반복
|
|
|
|
**해결책 구현**
|
|
1. **시간 감쇠**: e^(-t/τ)
|
|
2. **베이지안 관련성**: P(relevant|distance)
|
|
3. **엔트로피 최적 개수**: log2(n) * entropy
|
|
4. **MMR 다양성**: λ=1/φ (황금비 역수)
|
|
5. **복합 점수**: 조화 평균
|
|
|
|
**하드코딩 제거**
|
|
- 3 → e (자연상수)
|
|
- 2 → φ (황금비)
|
|
- 1/3 → 1/e
|
|
|
|
**결과**
|
|
- 428줄 scoring.py
|
|
- 선택: 100개 → 16개 후보 → 10개 MMR → 6-10개 최종
|
|
- 메모리 개수 대폭 감소
|
|
|
|
## 오후 1시 00분
|
|
|
|
### 남은 문제와 향후 과제
|
|
|
|
**개선됨**
|
|
- ✅ 88개 → 6-10개
|
|
- ✅ 날씨 대화 문맥 유지
|
|
- ✅ MMR 다양성 작동
|
|
|
|
**미해결**
|
|
- "어떤 내용?" 구체적 답변 실패
|
|
- 최근 대화 우선순위 부족
|
|
|
|
**향후 방향**
|
|
- 표준편차 기반 "최근" 정의
|
|
- 대화 클러스터링
|
|
- 참조형 질문 특별 처리
|
|
|
|
## 교훈
|
|
|
|
1. **Fire & Forget은 위험하다**
|
|
- 명시적 await 사용
|
|
- 에러 처리 필수
|
|
|
|
2. **함수형 != 순수 함수만**
|
|
- I/O는 불가피
|
|
- 중요한 건 분리
|
|
|
|
3. **하드코딩 제거 방법**
|
|
- 수학적 상수 활용 (황금비)
|
|
- 데이터 분포 활용 (백분위수)
|
|
- 동적 계산
|
|
|
|
4. **LLM으로 휴리스틱 대체**
|
|
- 임의의 숫자 대신 LLM 판단
|
|
- "충분한가?" 물어보기
|
|
|
|
5. **모듈 분리의 중요성**
|
|
- 단일 책임 원칙
|
|
- 테스트 용이성
|
|
- 재사용 가능
|
|
|
|
6. **async/await 실수 방지**
|
|
- async 함수는 반드시 await와 함께
|
|
- IDE 경고 무시하지 말 것
|
|
- coroutine 에러 = await 누락 의심
|
|
|
|
---
|
|
|
|
작성자: happybell80 & Claude
|
|
프로젝트: rb10508_micro
|
|
주제: 함수형 전환과 LLM 메모리 선택 |