- DB 스키마: scope_id NULL 처리 및 병합 로직 명확화 - 캐싱 전략: Redis 선택 사항, 메모리 캐시로 시작 가능 - 변수 치환: 런타임 치환 방식 명시 - 폴백 전략: DB 조회 실패 시 하드코딩 프롬프트 사용 (필수) - 웹 검색 기반 재검토 결과 반영
5.5 KiB
5.5 KiB
프롬프트 동적 관리 시스템 계획
날짜: 2025-12-25
작성자: happybell80
관련 파일: rb8001/app/services/llm/gemini_handler.py, rb8001/app/services/llm/llm_service.py
상태: 📋 계획 단계
개선 목표
현재 프롬프트가 코드에 하드코딩되어 있어 변경 시 배포가 필요하고, 사용자별 개인화가 어려움. DB화를 통해 동적 변경 및 개인화 구현.
기대 UX 개선
- 개인화: 사용자별 선호사항(톤, 길이, 스타일) 즉시 반영 ("짧게 대답해" → 다음 대화부터 적용)
- 빠른 반응성: 프롬프트 수정 시 배포 없이 즉시 적용, A/B 테스트 가능
- 일관성 향상: 버전 관리 및 롤백으로 품질 지속 개선
DB 스키마 설계
CREATE TABLE prompt_templates (
id UUID PRIMARY KEY,
scope_level VARCHAR(20) NOT NULL, -- 'global', 'robeing', 'user', 'task'
scope_id VARCHAR(255), -- robeing_id, user_id, task_type 등
prompt_type VARCHAR(50) NOT NULL, -- 'system', 'chat', 'extract', 'calendar_confirm', 'ir_analysis' 등
version VARCHAR(20) DEFAULT '1.0',
content JSONB NOT NULL, -- 템플릿 내용 + 변수 정의
is_active BOOLEAN DEFAULT true,
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now(),
UNIQUE(scope_level, scope_id, prompt_type, version)
);
프롬프트 분류 기준
1. 변경 빈도별
- 거의 안 바뀜: Global/system_basic (기본 정체성)
- 자주 변경: User/preferred_name (사용자 피드백 반영)
2. 계층적 범위(Scope)
- Global: 모든 사용자 공통 기본 프롬프트
- Robeing: 특정 로빙 공통
- User: 사용자별 개인화
- Task: 작업 타입별 (chat, extract, calendar_confirm 등)
3. 병합 로직
Global → Robeing → User 순서로 조회, 상위에서 정의되면 하위가 상속, 하위에서 오버라이드 시 상위 우선순위 적용.
- 각 레벨별 개별 쿼리 후 애플리케이션에서 병합 (복잡한 JOIN 대신)
- Global scope는 scope_id=NULL로 조회 (PostgreSQL NULL은 UNIQUE 제약에서 제외)
관리 포인트 (10가지)
- 시스템 프롬프트 (기본 정체성/규칙)
- 작업 타입별 (chat, summarize, extract_actions, calendar_confirm)
- 의도 분석 (IntentAnalyzer, IntentParserLLM)
- 스킬별 (naverworks_briefing, dm_skill, startup_news)
- IR Deck 분석
- 일기 생성
- 콜드메일 처리
- 감정 기반 동적 프롬프트
- 맥락 기반 (직전 대화, 메모리 검색)
- 변수 치환 ({current_time}, {user_name}, {emotion_labels})
핵심 설계 원칙 (기존 문서 참고)
- rule_type 분류: never/always/prefer/avoid + 감정 가중치 (ideas/250828_dynamic_system_prompt_memory)
- 계층 구조: Base Layer(기본 규칙) + Dynamic Layer(동적 변수) (troubleshooting/250806_happybell80_동적프롬프트구현)
- 우선순위 병합: Global → Robeing → User 순서, 하위가 상위 오버라이드
구현 Phase
Phase 1: DB 스키마 및 기본 인프라
prompt_templates테이블 생성- PromptService 클래스 (조회, 병합, 캐싱)
- 기본 프롬프트 마이그레이션 (하드코딩 → DB)
Phase 2: 핵심 프롬프트 DB화
- 시스템 프롬프트 (gemini_handler, openai_handler)
- 작업 타입별 프롬프트 (chat, extract, calendar_confirm)
- 폴백 로직 (DB 조회 실패 시 하드코딩 사용) - 필수
- DB 조회 실패/타임아웃 시 기존 _get_system_prompt() 메서드 사용
- 점진적 마이그레이션 가능, 안전한 전환 보장
Phase 3: 개인화 및 동적 프롬프트
- 사용자별 프롬프트 오버라이드
- 감정 기반 동적 프롬프트 조합
- 변수 치환 시스템 ({current_time}, {emotion_labels} 등)
- DB 저장 시 변수 플레이스홀더 유지
- 조회 후 str.format() 또는 템플릿 엔진으로 런타임 치환
Phase 4: A/B 테스트 및 모니터링
- 프롬프트 버전 관리
- A/B 테스트 지원 (암묵적 피드백 + 명시적 피드백)
- 롤백 기능
캐싱 전략
- Redis/Memory 캐시 (키:
prompt:{scope}:{type}:{version}) - TTL 1시간, DB 변경 시 무효화 (웹훅/이벤트)
- 캐시 히트율 99%+ 목표 (테스트 결과 99.8% 달성)
- Redis는 선택 사항: 메모리 캐시(ThreadDocCache 패턴)로 시작, 필요 시 Redis 전환
- Redis 없이도 동작하도록 폴백 로직 구현
구현 시 주의사항
DB 스키마
- scope_id NULL 처리: Global scope는 scope_id=NULL로 조회 (PostgreSQL NULL은 UNIQUE 제약에서 제외되어 문제 없음)
- 각 레벨별 개별 쿼리 후 애플리케이션에서 병합 권장 (복잡한 JOIN 대신)
캐싱
- Redis는 선택 사항, 메모리 캐시로 시작 가능
- Redis 없이도 동작하도록 폴백 로직 구현 필수
변수 치환
- 템플릿 변수({current_time}, {emotion_labels})는 DB 저장 시 플레이스홀더로 유지
- 조회 후 런타임에 str.format() 또는 템플릿 엔진으로 치환
폴백 전략
- DB 조회 실패/타임아웃 시 하드코딩 프롬프트 사용 (기존 코드 유지)
- 점진적 마이그레이션으로 안전한 전환 보장
참고 문서
- ideas/250828_dynamic_system_prompt_memory - 동적 시스템 프롬프트 메모리 시스템
- troubleshooting/250806_happybell80_동적프롬프트구현 - 동적 프롬프트 구현 기록
- book/300_architecture/311_FastAPI_구조_원칙 - 계층 분리 원칙